phanpy/assets/useTitle-zvzl1Hla.js.map

1 line
354 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{"version":3,"mappings":"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","names":["getAccount","id","accounts","store","local","getJSON","find","a","info","getAccountByAccessToken","accessToken","getAccountByInstance","instance","instanceURL","getCurrentAccount","window","__IGNORE_GET_ACCOUNT_ERROR__","currentAccount","account","getCurrentAccountNS","saveAccount","acc","updateAccount","accountInfo","displayName","currentInstance","getCurrentInstance","instances","e","location","getInstanceConfiguration","configuration","maxMediaAttachments","maxTootChars","pleroma","pollLimits","statuses","polls","maxCharactersPerOption","maxExpiration","getCurrentInstanceConfiguration","get","key","localStorage","getItem","console","JSON","value","set","setJSON","warn","session","sessionStorage","setItem","data","del","DEFAULT_INSTANCE","apis","accountApis","__ACCOUNT_APIS__","currentAccountApi","initClient","test","replace","toLowerCase","url","client","masto","createRestAPIClient","timeout","initInstance","uri","streamingApi","urls","domain","streaming","streamClient","createStreamingAPIClient","implementation","initAccount","vapidKey","mastoAccount","initPreferences","preferences","accountID","authenticated","_b","_c","instanceAccount","_e","_showToast","showToast","props","text","onClick","delay","rest","toast","Toastify","className","destination","gravity","position","setTimeout","isMastodonLinkMaybe","pathname","hash","URL","_moize","moize","mem","fn","opts","maxSize","isDeepEqual","pmem","isPromise","rateLimit","interval","queue","isRunning","executeNext","length","shift","args","callFn","apply","push","throttle","pThrottle","limit","denylistDomains","failedUnfurls","_unfurlMastodonLink","snapStates","snapshot","states","RegExp","unfurledLinks","Promise","resolve","debug","remoteInstanceFetch","theURL","urlAfterHash","urlObj","hostname","statusRegex","path","statusMatch","masto2","status","mastoSearchFetch","q","type","results","then","handleFulfill","result","selfURL","instance2","saveStatus","reject","handleCatch","finalPromise","unfurlMastodonLink","proxy","appVersion","prevLocation","currentLocation","statusThreadNumber","home","homeNew","homeLast","homeLastFetchTime","notifications","notificationsLast","notificationsNew","notificationsShowNew","notificationsLastFetchTime","reloadStatusPage","reloadGenericAccounts","counter","spoilers","spoilersMedia","scrollPositions","statusQuotes","statusFollowedTags","routeNotification","showCompose","showSettings","showAccount","showAccounts","showDrafts","showMediaModal","showShortcutsSettings","showKeyboardShortcutsHelp","showGenericAccounts","showMediaAlt","shortcuts","settings","autoRefresh","shortcutsViewMode","shortcutsColumnsMode","boostsCarousel","contentTranslation","contentTranslationTargetLanguage","contentTranslationHideLanguages","contentTranslationAutoInline","mediaAltGenerator","cloakMode","states$1","initStates","subscribeKey","v","subscribe","changes","prevValue","statusKey","getStatus","statusID","skipThreading","skipUnfurling","override","oldStatus","_pinned","threadifyStatus","queueMicrotask","unfurlStatus","_threadifyStatus","propInstance","api","traverse","index","fetchIndex","inReplyToAccountId","inReplyToId","prevStatus","r","fetchStatus","fauxDiv","content","document","sKey","reblog","i","splice","useInterval","deps","immediate","savedCallback","useRef","useEffect","current","setInterval","tick","clearInterval","openCompose","width","screenWidth","height","screenHeight","screen","left","Math","max","top","min","winUID","uid","random","newWin","open","__COMPOSE__","alert","SIZES","s","m","l","xl","xxl","ICONS","x","__vitePreload","heart","bookmark","check-circle","x-circle","transfer","rocket","arrow-left","arrow-right","arrow-up","arrow-down","earth","lock","unlock","eye-close","eye-open","message","comment","comment2","notification","follow","follow-add","poll","pencil","quill","at","attachment","upload","gear","more","external","popout","popin","plus","chevron-left","chevron-right","chevron-down","reply","thread","group","bot","menu","list","search","hashtag","shortcut","user","following","pin","bus","link","history","share","sparkles","sparkles2","exit","translate","play","trash","mute","unmute","block","unblock","flag","time","refresh","emoji2","filter","chart","react","layout4","layout5","announce","round","arrow-up-circle","arrow-down-circle","clipboard","account-edit","account-warning","keyboard","cloud","month","media","speak","building","ICONDATA","Icon","icon","size","alt","title","class","style","iconSize","iconBlock","rotate","flip","Array","isArray","iconData","setIconData","useState","currentIcon","iconB","default","_jsx","children","viewBox","dangerouslySetInnerHTML","__html","body","transform","useCloseWatcher","watcher","CloseWatcher","addEventListener","destroy","useCloseWatcher$1","$modalContainer","getElementById","Modal","onClose","modalRef","timer","focusElement","querySelector","focus","clearTimeout","supportsCloseWatcher","escRef","useHotkeys","enabled","keydown","keyup","ref","node","target","currentTarget","tabIndex","onFocus","createPortal","Loader","abrupt","hidden","xxxl","alphaCache","canvas","OffscreenCanvas","createElement","ctx","getContext","willReadFrequently","Avatar","squircle","avatarRef","isMissing","src","loading","decoding","crossOrigin","undefined","onError","onLoad","dataset","loaded","drawImage","allPixels","getImageData","hasAlpha","pixel","classList","add","clearRect","Link","forwardRef","routerLocation","useLocation","trim","to","restProps","includes","parsedHash","origin","searchParams","searchParamsStr","from","entries","map","encodeURIComponent","join","isActive","decodeURIComponent","href","parentNode","closest","stopPropagation","root","documentElement","getComputedStyle","defaultBoundingBoxPadding","safeAreaInsets","getSafeAreaInsets","safeAreaInsetTop","getPropertyValue","safeAreaInsetRight","safeAreaInsetBottom","safeAreaInsetLeft","parseInt","requestAnimationFrame","safeBoundingBoxPadding","paddings","CSS","supports","passive","Menu2","containerProps","instanceRef","_instanceRef","useWindowSize","Menu","boundingBoxPadding","repositionFlag","unmountOnClose","closeMenu","MenuConfirm","subMenu","confirm","confirmLabel","menuItemClassName","menuFooter","MenuItem","cloneElement","Parent","SubMenu","menuRef","_jsxs","openTrigger","direction","overflow","gap","menuClassName","menuButton","label","itemProps","onPointerMove","pointerType","openMenu","onPointerLeave","EmojiText","emojis","indexOf","regex","shortcode","elements","split","word","emoji","staticUrl","srcset","nameCollator","Intl","Collator","sensitivity","NameText","showAvatar","showAcct","short","acct","avatar","avatarStatic","username","_","acct1","acct2","match","trimmedUsername","trimmedDisplayName","shortenedDisplayName","shortenedAlphaNumericDisplayName","compare","preventDefault","_Fragment","memo","dayjs","extend","dayjsTwitter","localizedFormat","relativeTime","dtf","DateTimeFormat","RelativeTime","datetime","format","date","dateStr","now","diff","year","twitter","toDate","fromNow","toISOString","_localeMatch","localeMatch","locales","resolvedOptions","locale","navigator","languages","localeTargetLanguages","translationTargetLanguages","code","getTranslateTargetLanguage","fromSettings","localeCode2Text","DisplayNames","of","emojifyText","forEach","whitelistLinkClasses","_enhanceContent","postEnhanceDOM","enhancedContent","dom","innerHTML","hasLink","hasCodeBlock","querySelectorAll","setAttribute","c","remove","textContent","links","usernames","innerText","hasChildren","some","u","d","slice","textNodes","extractTextNodes","html","nodeValue","replaceWith","childNodes","p","pre","br","nextParagraphs","currentBlock","nextElementSibling","next","tagName","codeText","replaceAll","rejectFilter","prevIndex","hashtagStuffedParagraphs","hashtagCount","nodeType","Node","TEXT_NODE","linkText","startsWith","enhanceContent","defaultRejectFilter","defaultRejectFilterMap","Object","fromEntries","nodeName","rejectFilterMap","assign","reduce","cur","walk","createTreeWalker","NodeFilter","SHOW_TEXT","acceptNode","FILTER_REJECT","FILTER_ACCEPT","nextNode","div","getHTMLText","handleContentLinks","mentions","previewMode","statusURL","metaKey","ctrlKey","shiftKey","altKey","which","prevText","previousSibling","textBeforeLinkIsAt","endsWith","textStartsWithAt","contains","targetText","getAttribute","mention","textBeforeLinkIsHash","tag","_DateTimeFormat","dateYear","hideTime","formatOpts","currentYear","Date","getFullYear","day","hour","minute","niceDateTime","dtfOpts","NumberFormat","shortenNumber","notation","AccountBlock","skeleton","avatarSize","useAvatarStatic","internal","showActivity","showStats","accountInstance","hideDisplayName","relationship","excludeRelationshipAttrs","statusesCount","lastStatusAt","fields","note","followersCount","verifiedField","f","verifiedAt","excludedRelationship","hasRelationship","followedBy","requested","supportsCache","feature","version","range","features","satisfies","includePrerelease","loose","define_import_meta_env_default","draftsStore","createStore","drafts","val","getMany","keys","delMany","clear","db","visibilityIconsMap","public","unlisted","private","direct","Poll","lang","readOnly","votePoll","uiState","setUIState","expired","expiresAt","multiple","options","ownVotes","voted","votersCount","votesCount","expiresAtDate","pollVotesCount","roundPrecision","showResults","setShowResults","optionsHaveVoteCounts","every","o","dir","option","optionVotesCount","percentage","toFixed","isLeading","disabled","onSubmit","form","formData","FormData","choices","name","marginLeft","FilterContext","createContext","_isFiltered","filtered","filterContext","appliedFilters","hasContext","context","isHidden","filterAction","action","filterTitles","titles","titlesStr","isFiltered","filteredItem","item","currentAccountID","isSelf","filterState","filteredItems","items","htmlContentLength","el","supportsTTS","speechSynthesis","speaking","cancel","utterance","SpeechSynthesisUtterance","statusPeek","spoilerText","mediaAttachments","image","gifv","video","audio","unknown","useTruncated","onResize","useThrottledCallback","scrollHeight","truncated","_height","maxHeight","computedHeight","toggle","useResizeObserver","box","isSafari","userAgent","dataAltLabel","AltBadge","MEDIA_CAPTION_LIMIT","isMediaCaptionLong","caption","Media","showOriginal","autoAnimate","showCaption","altIndex","blurhash","description","meta","previewRemoteUrl","previewUrl","remoteUrl","original","small","mediaURL","remoteMediaURL","orientation","rgbAverageColor","getBlurHashAverageColor","videoRef","focalPosition","y","mediaRef","onUpdate","useCallback","scale","make3dTransformValue","removeProperty","setProperty","touchAction","pinchZoomEnabled","setPinchZoomEnabled","quickPinchZoomProps","draggableUnZoomed","inertiaFriction","doubleTapZoomOutOnMaxScale","useMemo","isVideoMaybe","isImage","parentRef","imageSmallerThanParent","setImageSmallerThanParent","useLayoutEffect","offsetWidth","offsetHeight","smaller","maxAspectHeight","innerHeight","averageColorStyle","mediaStyles","aspectRatio","longDesc","showInlineDesc","Figure","Fragment","display","fetch","mode","backgroundImage","backgroundSize","QuickPinchZoom","objectPosition","shortDuration","duration","isGIF","loopable","formattedDuration","formatDuration","hoverAnimate","autoGIFAnimate","showProgress","videoHTML","pause","onMouseEnter","onMouseLeave","onBlur","poster","preload","playsinline","loop","muted","onTimeUpdate","container","currentTime","controls","autoplay","MenuLink","FocusableItem","detail","SHOW_COMMENT_COUNT_LIMIT","INLINE_TRANSLATE_LIMIT","fetchAccount","v1","$select","memFetchAccount","visibilityText","isIOS","ontouchstart","REACTIONS_LIMIT","getPollText","getPostText","Status","contentTextWeight","enableCommentHint","withinContext","enableTranslate","forceTranslate","_forceTranslate","onMediaClick","quoted","onStatusLinkClick","showFollowedTags","sameInstance","useSnapshot","accountId","accountURL","accountEmojis","repliesCount","reblogged","reblogsCount","favourited","favouritesCount","bookmarked","sensitive","visibility","language","editedAt","card","createdAt","tags","_deleted","useContext","filterInfo","debugHover","FilteredStatus","editedAtDate","inReplyToAccountRef","createdAtDate","setInReplyToAccount","inReplyToAccount","account2","catch","mentionSelf","readingExpandSpoilers","showSpoiler","readingExpandMedia","prefs","showSpoilerMedia","setForceTranslate","targetLanguage","contentLength","inlineTranslate","isSizeLarge","showEdited","setShowEdited","spoilerContentRef","statusRef","contentRef","mediaContainerRef","readMoreText","textWeight","editedDateText","_a","mediaNoDesc","replyToStatus","boostStatus","unauthInteractionErrorMessage","confirmText","yes","newStatus","favouriteStatus","differentLanguage","fetchBoostedLikedByAccounts","firstLoad","favouriteIterator","reblogResults","reblogIterator","favouriteResults","_types","done","MenuHeader","StatusMenuItems","createdDateText","MenuDivider","fetchAccounts","showReactions","replyStatus","postText","nicePostURL","editStatus","cachedStatus","setIsContextMenuOpen","contextMenuProps","setContextMenuProps","showContextMenu","bindLongPressContext","useLongPress","clientX","clientY","isContextMenuOpen","hotkeysEnabled","fRef","rRef","dRef","confirmBoostStatus","bRef","activeStatus","spoilerButton","spoilerMediaButton","canBoost","xRef","showMultipleMediaCaptions","displayedMediaAttachments","attachments","indices","showCommentHint","isThread","showCommentCount","nodeRef","containsQuestion","contextMenuRef","portal","QuoteStatuses","newPoll","pollResponse","TranslationBlock","mini","captionChildren","StatusButton","checked","bookmarkStatus","count","EditedAtModal","selfReferential","providerName","providerUrl","authorName","authorUrl","imageDescription","embedUrl","publishedAt","hasText","isLandscape","cardStatusURL","blurhashImage","setCardStatusURL","imageData","w","putImageData","videoID","fetchStatusHistory","editHistory","setEditHistory","editHistory2","setIconAlt","buttonTitle","iconAlt","host","minutes","restPath","seconds","toString","filterTitleStr","statusPeekText","bindLongPressPeek","isReblog","actualStatusID","isFollowedTags","setShowPeek","threshold","captureEvent","ssKey","showPeek","statusPeekRef","quotes","uniqueQuotes","arr","q2","level"],"sources":["../../src/utils/store-utils.js","../../src/utils/store.js","../../src/utils/api.js","../../src/utils/show-toast.js","../../src/utils/isMastodonLinkMaybe.jsx","../../src/utils/mem.js","../../src/utils/pmem.js","../../src/utils/ratelimit.js","../../src/utils/unfurl-link.jsx","../../src/utils/states.js","../../src/utils/useInterval.js","../../src/utils/open-compose.js","../../src/components/icon.jsx","../../src/utils/useCloseWatcher.js","../../src/components/modal.jsx","../../src/components/loader.jsx","../../src/components/avatar.jsx","../../src/components/link.jsx","../../src/utils/safe-bounding-box-padding.js","../../src/components/menu2.jsx","../../src/components/menu-confirm.jsx","../../src/components/emoji-text.jsx","../../src/components/name-text.jsx","../../src/components/relative-time.jsx","../../src/utils/locale-match.jsx","../../src/utils/get-translate-target-language.jsx","../../src/utils/localeCode2Text.jsx","../../src/utils/emojify-text.js","../../src/utils/enhance-content.js","../../src/utils/getHTMLText.jsx","../../src/utils/handle-content-links.js","../../src/utils/nice-date-time.js","../../src/utils/shorten-number.jsx","../../src/components/account-block.jsx","../../src/utils/supports.js","../../src/components/translation-block.jsx","../../src/utils/db.js","../../src/utils/visibility-icons-map.js","../../src/components/poll.jsx","../../src/utils/filter-context.js","../../src/utils/filters.jsx","../../src/utils/html-content-length.js","../../src/utils/speech.js","../../src/utils/status-peek.jsx","../../src/utils/useTruncated.js","../../src/components/media.jsx","../../src/components/menu-link.jsx","../../src/components/status.jsx","../../src/components/compose.jsx","../../src/utils/useTitle.js"],"sourcesContent":["import store from './store';\n\nexport function getAccount(id) {\n const accounts = store.local.getJSON('accounts') || [];\n return accounts.find((a) => a.info.id === id) || accounts[0];\n}\n\nexport function getAccountByAccessToken(accessToken) {\n const accounts = store.local.getJSON('accounts') || [];\n return accounts.find((a) => a.accessToken === accessToken);\n}\n\nexport function getAccountByInstance(instance) {\n const accounts = store.local.getJSON('accounts') || [];\n return accounts.find((a) => a.instanceURL === instance);\n}\n\nexport function getCurrentAccount() {\n if (!window.__IGNORE_GET_ACCOUNT_ERROR__) {\n // Track down getCurrentAccount() calls before account-based states are initialized\n console.error('getCurrentAccount() called before states are initialized');\n if (import.meta.env.DEV) console.trace();\n }\n const currentAccount = store.session.get('currentAccount');\n const account = getAccount(currentAccount);\n return account;\n}\n\nexport function getCurrentAccountNS() {\n const account = getCurrentAccount();\n const {\n instanceURL,\n info: { id },\n } = account;\n return `${id}@${instanceURL}`;\n}\n\nexport function saveAccount(account) {\n const accounts = store.local.getJSON('accounts') || [];\n const acc = accounts.find((a) => a.info.id === account.info.id);\n if (acc) {\n acc.info = account.info;\n acc.instanceURL = account.instanceURL;\n acc.accessToken = account.accessToken;\n acc.vapidKey = account.vapidKey;\n } else {\n accounts.push(account);\n }\n store.local.setJSON('accounts', accounts);\n store.session.set('currentAccount', account.info.id);\n}\n\nexport function updateAccount(accountInfo) {\n // Only update if displayName or avatar or avatar_static is different\n const accounts = store.local.getJSON('accounts') || [];\n const acc = accounts.find((a) => a.info.id === accountInfo.id);\n if (acc) {\n if (\n acc.info.displayName !== accountInfo.displayName ||\n acc.info.avatar !== accountInfo.avatar ||\n acc.info.avatar_static !== accountInfo.avatar_static\n ) {\n acc.info = {\n ...acc.info,\n ...accountInfo,\n };\n store.local.setJSON('accounts', accounts);\n }\n }\n}\n\nlet currentInstance = null;\nexport function getCurrentInstance() {\n if (currentInstance) return currentInstance;\n try {\n const account = getCurrentAccount();\n const instances = store.local.getJSON('instances');\n const instance = account.instanceURL.toLowerCase();\n return (currentInstance = instances[instance]);\n } catch (e) {\n console.error(e);\n alert(`Failed to load instance configuration. Please try again.\\n\\n${e}`);\n // Temporary fix for corrupted data\n store.local.del('instances');\n location.reload();\n return {};\n }\n}\n\n// Massage these instance configurations to match the Mastodon API\n// - Pleroma\nfunction getInstanceConfiguration(instance) {\n const {\n configuration,\n maxMediaAttachments,\n maxTootChars,\n pleroma,\n pollLimits,\n } = instance;\n\n const statuses = configuration?.statuses || {};\n if (maxMediaAttachments) {\n statuses.maxMediaAttachments ??= maxMediaAttachments;\n }\n if (maxTootChars) {\n statuses.maxCharacters ??= maxTootChars;\n }\n\n const polls = configuration?.polls || {};\n if (pollLimits) {\n polls.maxCharactersPerOption ??= pollLimits.maxOptionChars;\n polls.maxExpiration ??= pollLimits.maxExpiration;\n polls.maxOptions ??= pollLimits.maxOptions;\n polls.minExpiration ??= pollLimits.minExpiration;\n }\n\n return {\n ...configuration,\n statuses,\n polls,\n };\n}\n\nexport function getCurrentInstanceConfiguration() {\n const instance = getCurrentInstance();\n return getInstanceConfiguration(instance);\n}\n","import { getCurrentAccountNS } from './store-utils';\n\nconst local = {\n get: (key) => {\n try {\n return localStorage.getItem(key);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n getJSON: (key) => {\n try {\n return JSON.parse(local.get(key));\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n set: (key, value) => {\n try {\n return localStorage.setItem(key, value);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n setJSON: (key, value) => {\n try {\n return local.set(key, JSON.stringify(value));\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n del: (key) => {\n try {\n return localStorage.removeItem(key);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n};\n\nconst session = {\n get: (key) => {\n try {\n return sessionStorage.getItem(key);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n getJSON: (key) => {\n try {\n return JSON.parse(session.get(key));\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n set: (key, value) => {\n try {\n return sessionStorage.setItem(key, value);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n setJSON: (key, value) => {\n try {\n return session.set(key, JSON.stringify(value));\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n del: (key) => {\n try {\n return sessionStorage.removeItem(key);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n};\n\n// Store with account namespace (id@domain.tld) <- uses id, not username\nconst account = {\n get: (key) => {\n try {\n return local.getJSON(key)[getCurrentAccountNS()];\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n set: (key, value) => {\n try {\n const data = local.getJSON(key) || {};\n data[getCurrentAccountNS()] = value;\n return local.setJSON(key, data);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n del: (key) => {\n try {\n const data = local.getJSON(key) || {};\n delete data[getCurrentAccountNS()];\n return local.setJSON(key, data);\n } catch (e) {\n console.warn(e);\n return null;\n }\n },\n};\n\nexport default { local, session, account };\n","import { createRestAPIClient, createStreamingAPIClient } from 'masto';\n\nimport store from './store';\nimport {\n getAccount,\n getAccountByAccessToken,\n getAccountByInstance,\n getCurrentAccount,\n saveAccount,\n} from './store-utils';\n\n// Default *fallback* instance\nconst DEFAULT_INSTANCE = 'mastodon.social';\n\n// Per-instance masto instance\n// Useful when only one account is logged in\n// I'm not sure if I'll ever allow multiple logged-in accounts but oh well...\n// E.g. apis['mastodon.social']\nconst apis = {};\n\n// Per-account masto instance\n// Note: There can be many accounts per instance\n// Useful when multiple accounts are logged in or when certain actions require a specific account\n// Just in case if I need this one day.\n// E.g. accountApis['mastodon.social']['ACCESS_TOKEN']\nconst accountApis = {};\nwindow.__ACCOUNT_APIS__ = accountApis;\n\n// Current account masto instance\nlet currentAccountApi;\n\nexport function initClient({ instance, accessToken }) {\n if (/^https?:\\/\\//.test(instance)) {\n instance = instance\n .replace(/^https?:\\/\\//, '')\n .replace(/\\/+$/, '')\n .toLowerCase();\n }\n const url = instance ? `https://${instance}` : `https://${DEFAULT_INSTANCE}`;\n\n const masto = createRestAPIClient({\n url,\n accessToken, // Can be null\n timeout: 30_000, // Unfortunatly this is global instead of per-request\n });\n\n const client = {\n masto,\n instance,\n accessToken,\n };\n apis[instance] = client;\n if (!accountApis[instance]) accountApis[instance] = {};\n if (accessToken) accountApis[instance][accessToken] = client;\n\n return client;\n}\n\n// Get the instance information\n// The config is needed for composing\nexport async function initInstance(client, instance) {\n console.log('INIT INSTANCE', client, instance);\n const { masto, accessToken } = client;\n // Request v2, fallback to v1 if fail\n let info;\n try {\n info = await masto.v2.instance.fetch();\n } catch (e) {}\n if (!info) {\n try {\n info = await masto.v1.instance.fetch();\n } catch (e) {}\n }\n if (!info) return;\n console.log(info);\n const {\n // v1\n uri,\n urls: { streamingApi } = {},\n // v2\n domain,\n configuration: { urls: { streaming } = {} } = {},\n } = info;\n const instances = store.local.getJSON('instances') || {};\n if (uri || domain) {\n instances[\n (domain || uri)\n .replace(/^https?:\\/\\//, '')\n .replace(/\\/+$/, '')\n .toLowerCase()\n ] = info;\n }\n if (instance) {\n instances[instance.toLowerCase()] = info;\n }\n store.local.setJSON('instances', instances);\n // This is a weird place to put this but here's updating the masto instance with the streaming API URL set in the configuration\n // Reason: Streaming WebSocket URL may change, unlike the standard API REST URLs\n const supportsWebSocket = 'WebSocket' in window;\n if (supportsWebSocket && (streamingApi || streaming)) {\n console.log('🎏 Streaming API URL:', streaming || streamingApi);\n // masto.config.props.streamingApiUrl = streaming || streamingApi;\n // Legacy masto.ws\n const streamClient = createStreamingAPIClient({\n streamingApiUrl: streaming || streamingApi,\n accessToken,\n implementation: WebSocket,\n });\n client.streaming = streamClient;\n // masto.ws = streamClient;\n console.log('🎏 Streaming API client:', client);\n }\n}\n\n// Get the account information and store it\nexport async function initAccount(client, instance, accessToken, vapidKey) {\n const { masto } = client;\n const mastoAccount = await masto.v1.accounts.verifyCredentials();\n\n console.log('CURRENTACCOUNT SET', mastoAccount.id);\n store.session.set('currentAccount', mastoAccount.id);\n\n saveAccount({\n info: mastoAccount,\n instanceURL: instance.toLowerCase(),\n accessToken,\n vapidKey,\n });\n}\n\n// Get preferences\nexport async function initPreferences(client) {\n try {\n const { masto } = client;\n const preferences = await masto.v1.preferences.fetch();\n store.account.set('preferences', preferences);\n } catch (e) {\n // silently fail\n console.error(e);\n }\n}\n\n// Get the masto instance\n// If accountID is provided, get the masto instance for that account\nexport function api({ instance, accessToken, accountID, account } = {}) {\n // Always lowercase and trim the instance\n if (instance) {\n instance = instance.toLowerCase().trim();\n }\n\n // If instance and accessToken are provided, get the masto instance for that account\n if (instance && accessToken) {\n const client =\n accountApis[instance]?.[accessToken] ||\n initClient({ instance, accessToken });\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: true,\n instance,\n };\n }\n\n if (accessToken) {\n // If only accessToken is provided, get the masto instance for that accessToken\n console.log('X 1', accountApis);\n for (const instance in accountApis) {\n if (accountApis[instance][accessToken]) {\n console.log('X 2', accountApis, instance, accessToken);\n const client = accountApis[instance][accessToken];\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: true,\n instance,\n };\n } else {\n console.log('X 3', accountApis, instance, accessToken);\n const account = getAccountByAccessToken(accessToken);\n if (account) {\n const accessToken = account.accessToken;\n const instance = account.instanceURL.toLowerCase().trim();\n const client = initClient({ instance, accessToken });\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: true,\n instance,\n };\n } else {\n throw new Error(`Access token not found`);\n }\n }\n }\n }\n\n // If account is provided, get the masto instance for that account\n if (account || accountID) {\n account = account || getAccount(accountID);\n if (account) {\n const accessToken = account.accessToken;\n const instance = account.instanceURL.toLowerCase().trim();\n const client =\n accountApis[instance]?.[accessToken] ||\n initClient({ instance, accessToken });\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: true,\n instance,\n };\n } else {\n throw new Error(`Account ${accountID} not found`);\n }\n }\n\n const currentAccount = getCurrentAccount();\n\n // If only instance is provided, get the masto instance for that instance\n if (instance) {\n if (currentAccountApi?.instance === instance) {\n return {\n masto: currentAccountApi.masto,\n streaming: currentAccountApi.streaming,\n client: currentAccountApi,\n authenticated: true,\n instance,\n };\n }\n\n if (currentAccount?.instanceURL === instance) {\n const { accessToken } = currentAccount;\n currentAccountApi =\n accountApis[instance]?.[accessToken] ||\n initClient({ instance, accessToken });\n return {\n masto: currentAccountApi.masto,\n streaming: currentAccountApi.streaming,\n client: currentAccountApi,\n authenticated: true,\n instance,\n };\n }\n\n const instanceAccount = getAccountByInstance(instance);\n if (instanceAccount) {\n const accessToken = instanceAccount.accessToken;\n const client =\n accountApis[instance]?.[accessToken] ||\n initClient({ instance, accessToken });\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: true,\n instance,\n };\n }\n\n const client = apis[instance] || initClient({ instance });\n const { masto, streaming, accessToken } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: !!accessToken,\n instance,\n };\n }\n\n // If no instance is provided, get the masto instance for the current account\n if (currentAccountApi) {\n return {\n masto: currentAccountApi.masto,\n streaming: currentAccountApi.streaming,\n client: currentAccountApi,\n authenticated: true,\n instance: currentAccountApi.instance,\n };\n }\n if (currentAccount) {\n const { accessToken, instanceURL: instance } = currentAccount;\n currentAccountApi =\n accountApis[instance]?.[accessToken] ||\n initClient({ instance, accessToken });\n return {\n masto: currentAccountApi.masto,\n streaming: currentAccountApi.streaming,\n client: currentAccountApi,\n authenticated: true,\n instance,\n };\n }\n\n // If no instance is provided and no account is logged in, get the masto instance for DEFAULT_INSTANCE\n const client =\n apis[DEFAULT_INSTANCE] || initClient({ instance: DEFAULT_INSTANCE });\n const { masto, streaming } = client;\n return {\n masto,\n streaming,\n client,\n authenticated: false,\n instance: DEFAULT_INSTANCE,\n };\n}\n\nwindow.__API__ = {\n currentAccountApi,\n apis,\n accountApis,\n};\n","import Toastify from 'toastify-js';\n\nwindow._showToast = showToast;\n\nfunction showToast(props) {\n if (typeof props === 'string') {\n props = { text: props };\n }\n const { onClick, delay, ...rest } = props;\n const toast = Toastify({\n className: `${onClick || props.destination ? 'shiny-pill' : ''}`,\n gravity: 'bottom',\n position: 'center',\n ...rest,\n onClick: () => {\n onClick?.(toast); // Pass in the object itself!\n },\n });\n if (delay) {\n setTimeout(() => {\n toast.showToast();\n }, delay);\n } else {\n toast.showToast();\n }\n return toast;\n}\n\nexport default showToast;\n","export default function isMastodonLinkMaybe(url) {\n try {\n const { pathname, hash } = new URL(url);\n return (\n /^\\/.*\\/\\d+$/i.test(pathname) ||\n /^\\/(@[^/]+|users\\/[^/]+)\\/(statuses|posts)\\/\\w+\\/?$/i.test(pathname) || // GoToSocial, Takahe\n /^\\/notes\\/[a-z0-9]+$/i.test(pathname) || // Misskey, Firefish\n /^\\/(notice|objects)\\/[a-z0-9-]+$/i.test(pathname) || // Pleroma\n /#\\/[^\\/]+\\.[^\\/]+\\/s\\/.+/i.test(hash) // Phanpy 🫣\n );\n } catch (e) {\n return false;\n }\n}\n","import moize from 'moize';\n\nwindow._moize = moize;\n\nexport default function mem(fn, opts = {}) {\n return moize(fn, { ...opts, maxSize: 50, isDeepEqual: true });\n}\n","import mem from './mem';\n\nexport default function pmem(fn, opts = {}) {\n return mem(fn, { isPromise: true, ...opts });\n}\n","// Rate limit repeated function calls and queue them to set interval\nexport default function rateLimit(fn, interval) {\n let queue = [];\n let isRunning = false;\n\n function executeNext() {\n if (queue.length === 0) {\n isRunning = false;\n return;\n }\n\n const nextFn = queue.shift();\n nextFn();\n setTimeout(executeNext, interval);\n }\n\n return function (...args) {\n const callFn = () => fn.apply(this, args);\n queue.push(callFn);\n\n if (!isRunning) {\n isRunning = true;\n setTimeout(executeNext, interval);\n }\n };\n}\n","import pThrottle from 'p-throttle';\nimport { snapshot } from 'valtio/vanilla';\n\nimport { api } from './api';\nimport states, { saveStatus } from './states';\n\nexport const throttle = pThrottle({\n limit: 1,\n interval: 1000,\n});\n\nconst denylistDomains = /(twitter|github)\\.com/i;\nconst failedUnfurls = {};\nfunction _unfurlMastodonLink(instance, url) {\n const snapStates = snapshot(states);\n if (denylistDomains.test(url)) {\n return;\n }\n if (failedUnfurls[url]) {\n return;\n }\n const instanceRegex = new RegExp(instance + '/');\n if (instanceRegex.test(snapStates.unfurledLinks[url]?.url)) {\n return Promise.resolve(snapStates.unfurledLinks[url]);\n }\n console.debug('🦦 Unfurling URL', url);\n\n let remoteInstanceFetch;\n let theURL = url;\n\n // https://elk.zone/domain.com/@stest/123 -> https://domain.com/@stest/123\n if (/\\/\\/elk\\.[^\\/]+\\/[^\\/]+\\.[^\\/]+/i.test(theURL)) {\n theURL = theURL.replace(/elk\\.[^\\/]+\\//i, '');\n }\n\n // https://trunks.social/status/domain.com/@stest/123 -> https://domain.com/@stest/123\n if (/\\/\\/trunks\\.[^\\/]+\\/status\\/[^\\/]+\\.[^\\/]+/i.test(theURL)) {\n theURL = theURL.replace(/trunks\\.[^\\/]+\\/status\\//i, '');\n }\n\n // https://phanpy.social/#/domain.com/s/123 -> https://domain.com/statuses/123\n if (/\\/#\\/[^\\/]+\\.[^\\/]+\\/s\\/.+/i.test(theURL)) {\n const urlAfterHash = theURL.split('/#/')[1];\n const finalURL = urlAfterHash.replace(/\\/s\\//i, '/@fakeUsername/');\n theURL = `https://${finalURL}`;\n }\n\n let urlObj;\n try {\n urlObj = new URL(theURL);\n } catch (e) {\n return;\n }\n const domain = urlObj.hostname;\n const path = urlObj.pathname;\n // Regex /:username/:id, where username = @username or @username@domain, id = number\n const statusRegex = /\\/@([^@\\/]+)@?([^\\/]+)?\\/(\\d+)$/i;\n const statusMatch = statusRegex.exec(path);\n if (statusMatch) {\n const id = statusMatch[3];\n const { masto } = api({ instance: domain });\n remoteInstanceFetch = masto.v1.statuses\n .$select(id)\n .fetch()\n .then((status) => {\n if (status?.id) {\n return {\n status,\n instance: domain,\n };\n } else {\n throw new Error('No results');\n }\n });\n }\n\n const { masto } = api({ instance });\n const mastoSearchFetch = masto.v2.search\n .fetch({\n q: theURL,\n type: 'statuses',\n resolve: true,\n limit: 1,\n })\n .then((results) => {\n if (results.statuses.length > 0) {\n const status = results.statuses[0];\n return {\n status,\n instance,\n };\n } else {\n throw new Error('No results');\n }\n });\n\n function handleFulfill(result) {\n const { status, instance } = result;\n const { id } = status;\n const selfURL = `/${instance}/s/${id}`;\n console.debug('🦦 Unfurled URL', url, id, selfURL);\n const data = {\n id,\n instance,\n url: selfURL,\n };\n states.unfurledLinks[url] = data;\n saveStatus(status, instance, {\n skipThreading: true,\n });\n return data;\n }\n function handleCatch(e) {\n failedUnfurls[url] = true;\n }\n\n if (remoteInstanceFetch) {\n // return Promise.any([remoteInstanceFetch, mastoSearchFetch])\n // .then(handleFulfill)\n // .catch(handleCatch);\n // If mastoSearchFetch is fulfilled within 3s, return it, else return remoteInstanceFetch\n const finalPromise = Promise.race([\n mastoSearchFetch,\n new Promise((resolve, reject) => setTimeout(reject, 3000)),\n ]).catch(() => {\n // If remoteInstanceFetch is fullfilled, return it, else return mastoSearchFetch\n return remoteInstanceFetch.catch(() => mastoSearchFetch);\n });\n return finalPromise.then(handleFulfill).catch(handleCatch);\n } else {\n return mastoSearchFetch.then(handleFulfill).catch(handleCatch);\n }\n}\n\nconst unfurlMastodonLink = throttle(_unfurlMastodonLink);\nexport default unfurlMastodonLink;\n","import { proxy, subscribe } from 'valtio';\nimport { subscribeKey } from 'valtio/utils';\n\nimport { api } from './api';\nimport isMastodonLinkMaybe from './isMastodonLinkMaybe';\nimport pmem from './pmem';\nimport rateLimit from './ratelimit';\nimport store from './store';\nimport unfurlMastodonLink from './unfurl-link';\n\nconst states = proxy({\n appVersion: {},\n // history: [],\n prevLocation: null,\n currentLocation: null,\n statuses: {},\n statusThreadNumber: {},\n home: [],\n // specialHome: [],\n homeNew: [],\n homeLast: null, // Last item in 'home' list\n homeLastFetchTime: null,\n notifications: [],\n notificationsLast: null, // Last read notification\n notificationsNew: [],\n notificationsShowNew: false,\n notificationsLastFetchTime: null,\n reloadStatusPage: 0,\n reloadGenericAccounts: {\n id: null,\n counter: 0,\n },\n spoilers: {},\n spoilersMedia: {},\n scrollPositions: {},\n unfurledLinks: {},\n statusQuotes: {},\n statusFollowedTags: {},\n accounts: {},\n routeNotification: null,\n // Modals\n showCompose: false,\n showSettings: false,\n showAccount: false,\n showAccounts: false,\n showDrafts: false,\n showMediaModal: false,\n showShortcutsSettings: false,\n showKeyboardShortcutsHelp: false,\n showGenericAccounts: false,\n showMediaAlt: false,\n // Shortcuts\n shortcuts: [],\n // Settings\n settings: {\n autoRefresh: false,\n shortcutsViewMode: null,\n shortcutsColumnsMode: false,\n boostsCarousel: true,\n contentTranslation: true,\n contentTranslationTargetLanguage: null,\n contentTranslationHideLanguages: [],\n contentTranslationAutoInline: false,\n mediaAltGenerator: false,\n cloakMode: false,\n },\n});\n\nexport default states;\n\nexport function initStates() {\n // init all account based states\n // all keys that uses store.account.get() should be initialized here\n states.notificationsLast = store.account.get('notificationsLast') || null;\n states.shortcuts = store.account.get('shortcuts') ?? [];\n states.settings.autoRefresh =\n store.account.get('settings-autoRefresh') ?? false;\n states.settings.shortcutsViewMode =\n store.account.get('settings-shortcutsViewMode') ?? null;\n if (store.account.get('settings-shortcutsColumnsMode')) {\n states.settings.shortcutsColumnsMode = true;\n }\n states.settings.boostsCarousel =\n store.account.get('settings-boostsCarousel') ?? true;\n states.settings.contentTranslation =\n store.account.get('settings-contentTranslation') ?? true;\n states.settings.contentTranslationTargetLanguage =\n store.account.get('settings-contentTranslationTargetLanguage') || null;\n states.settings.contentTranslationHideLanguages =\n store.account.get('settings-contentTranslationHideLanguages') || [];\n states.settings.contentTranslationAutoInline =\n store.account.get('settings-contentTranslationAutoInline') ?? false;\n states.settings.mediaAltGenerator =\n store.account.get('settings-mediaAltGenerator') ?? false;\n states.settings.cloakMode = store.account.get('settings-cloakMode') ?? false;\n}\n\nsubscribeKey(states, 'notificationsLast', (v) => {\n console.log('CHANGE', v);\n store.account.set('notificationsLast', states.notificationsLast);\n});\nsubscribe(states, (changes) => {\n console.debug('STATES change', changes);\n for (const [action, path, value, prevValue] of changes) {\n if (path.join('.') === 'settings.autoRefresh') {\n store.account.set('settings-autoRefresh', !!value);\n }\n if (path.join('.') === 'settings.boostsCarousel') {\n store.account.set('settings-boostsCarousel', !!value);\n }\n if (path.join('.') === 'settings.shortcutsViewMode') {\n store.account.set('settings-shortcutsViewMode', value);\n }\n if (path.join('.') === 'settings.contentTranslation') {\n store.account.set('settings-contentTranslation', !!value);\n }\n if (path.join('.') === 'settings.contentTranslationAutoInline') {\n store.account.set('settings-contentTranslationAutoInline', !!value);\n }\n if (path.join('.') === 'settings.contentTranslationTargetLanguage') {\n console.log('SET', value);\n store.account.set('settings-contentTranslationTargetLanguage', value);\n }\n if (/^settings\\.contentTranslationHideLanguages/i.test(path.join('.'))) {\n store.account.set(\n 'settings-contentTranslationHideLanguages',\n states.settings.contentTranslationHideLanguages,\n );\n }\n if (path.join('.') === 'settings.mediaAltGenerator') {\n store.account.set('settings-mediaAltGenerator', !!value);\n }\n if (path?.[0] === 'shortcuts') {\n store.account.set('shortcuts', states.shortcuts);\n }\n if (path.join('.') === 'settings.cloakMode') {\n store.account.set('settings-cloakMode', !!value);\n }\n }\n});\n\nexport function hideAllModals() {\n states.showCompose = false;\n states.showSettings = false;\n states.showAccount = false;\n states.showAccounts = false;\n states.showDrafts = false;\n states.showMediaModal = false;\n states.showShortcutsSettings = false;\n states.showKeyboardShortcutsHelp = false;\n states.showGenericAccounts = false;\n states.showMediaAlt = false;\n}\n\nexport function statusKey(id, instance) {\n if (!id) return;\n return instance ? `${instance}/${id}` : id;\n}\n\nexport function getStatus(statusID, instance) {\n if (instance) {\n const key = statusKey(statusID, instance);\n return states.statuses[key];\n }\n return states.statuses[statusID];\n}\n\nexport function saveStatus(status, instance, opts) {\n if (typeof instance === 'object') {\n opts = instance;\n instance = null;\n }\n const {\n override = true,\n skipThreading = false,\n skipUnfurling = false,\n } = opts || {};\n if (!status) return;\n const oldStatus = getStatus(status.id, instance);\n if (!override && oldStatus) return;\n queueMicrotask(() => {\n const key = statusKey(status.id, instance);\n if (oldStatus?._pinned) status._pinned = oldStatus._pinned;\n // if (oldStatus?._filtered) status._filtered = oldStatus._filtered;\n states.statuses[key] = status;\n if (status.reblog) {\n const key = statusKey(status.reblog.id, instance);\n states.statuses[key] = status.reblog;\n }\n });\n\n // THREAD TRAVERSER\n if (!skipThreading) {\n queueMicrotask(() => {\n threadifyStatus(status, instance);\n if (status.reblog) {\n queueMicrotask(() => {\n threadifyStatus(status.reblog, instance);\n });\n }\n });\n }\n\n // UNFURLER\n if (!skipUnfurling) {\n queueMicrotask(() => {\n unfurlStatus(status, instance);\n });\n }\n}\n\nfunction _threadifyStatus(status, propInstance) {\n const { masto, instance } = api({ instance: propInstance });\n // Return all statuses in the thread, via inReplyToId, if inReplyToAccountId === account.id\n let fetchIndex = 0;\n async function traverse(status, index = 0) {\n const { inReplyToId, inReplyToAccountId } = status;\n if (!inReplyToId || inReplyToAccountId !== status.account.id) {\n return [status];\n }\n if (inReplyToId && inReplyToAccountId !== status.account.id) {\n throw 'Not a thread';\n // Possibly thread of replies by multiple people?\n }\n const key = statusKey(inReplyToId, instance);\n let prevStatus = states.statuses[key];\n if (!prevStatus) {\n if (fetchIndex++ > 3) throw 'Too many fetches for thread'; // Some people revive old threads\n await new Promise((r) => setTimeout(r, 500 * fetchIndex)); // Be nice to rate limits\n // prevStatus = await masto.v1.statuses.$.select(inReplyToId).fetch();\n prevStatus = await fetchStatus(inReplyToId, masto);\n saveStatus(prevStatus, instance, { skipThreading: true });\n }\n // Prepend so that first status in thread will be index 0\n return [...(await traverse(prevStatus, ++index)), status];\n }\n return traverse(status)\n .then((statuses) => {\n if (statuses.length > 1) {\n console.debug('THREAD', statuses);\n statuses.forEach((status, index) => {\n const key = statusKey(status.id, instance);\n states.statusThreadNumber[key] = index + 1;\n });\n }\n })\n .catch((e) => {\n console.error(e, status);\n });\n}\nexport const threadifyStatus = rateLimit(_threadifyStatus, 100);\n\nconst fauxDiv = document.createElement('div');\nexport function unfurlStatus(status, instance) {\n const { instance: currentInstance } = api();\n const content = status.reblog?.content || status.content;\n const hasLink = /<a/i.test(content);\n if (hasLink) {\n const sKey = statusKey(status?.reblog?.id || status?.id, instance);\n fauxDiv.innerHTML = content;\n const links = fauxDiv.querySelectorAll(\n 'a[href]:not(.u-url):not(.mention):not(.hashtag)',\n );\n [...links]\n .filter((a) => {\n const url = a.href;\n const isPostItself = url === status.url || url === status.uri;\n return !isPostItself && isMastodonLinkMaybe(url);\n })\n .forEach((a, i) => {\n unfurlMastodonLink(currentInstance, a.href).then((result) => {\n if (!result) return;\n if (!sKey) return;\n if (!Array.isArray(states.statusQuotes[sKey])) {\n states.statusQuotes[sKey] = [];\n }\n if (!states.statusQuotes[sKey][i]) {\n states.statusQuotes[sKey].splice(i, 0, result);\n }\n });\n });\n }\n}\n\nconst fetchStatus = pmem((statusID, masto) => {\n return masto.v1.statuses.$select(statusID).fetch();\n});\n","import { useEffect, useRef } from 'preact/hooks';\n\nfunction useInterval(fn, delay, deps, immediate) {\n const savedCallback = useRef(fn);\n useEffect(() => {\n savedCallback.current = fn;\n }, [fn, deps]);\n\n useEffect(() => {\n if (!immediate || delay === null || delay === false) return;\n savedCallback.current();\n }, [immediate]);\n\n useEffect(() => {\n if (delay === null || delay === false) return;\n const tick = () => savedCallback.current();\n const id = setInterval(tick, delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n\nexport default useInterval;\n","export default function openCompose(opts) {\n const url = new URL('/compose/', window.location);\n const { width: screenWidth, height: screenHeight } = window.screen;\n const left = Math.max(0, (screenWidth - 600) / 2);\n const top = Math.max(0, (screenHeight - 450) / 2);\n const width = Math.min(screenWidth, 600);\n const height = Math.min(screenHeight, 450);\n const winUID = opts?.uid || Math.random();\n const newWin = window.open(\n url,\n 'compose' + winUID,\n `width=${width},height=${height},left=${left},top=${top}`,\n );\n\n if (newWin) {\n // if (masto) {\n // newWin.masto = masto;\n // }\n\n newWin.__COMPOSE__ = opts;\n } else {\n alert('Looks like your browser is blocking popups.');\n }\n\n return newWin;\n}\n","import { useEffect, useRef, useState } from 'preact/hooks';\n\nconst SIZES = {\n s: 12,\n m: 16,\n l: 20,\n xl: 24,\n xxl: 32,\n};\n\nexport const ICONS = {\n x: () => import('@iconify-icons/mingcute/close-line'),\n heart: () => import('@iconify-icons/mingcute/heart-line'),\n bookmark: () => import('@iconify-icons/mingcute/bookmark-line'),\n 'check-circle': () => import('@iconify-icons/mingcute/check-circle-line'),\n 'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'),\n transfer: () => import('@iconify-icons/mingcute/transfer-4-line'),\n rocket: () => import('@iconify-icons/mingcute/rocket-line'),\n 'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'),\n 'arrow-right': () => import('@iconify-icons/mingcute/arrow-right-line'),\n 'arrow-up': () => import('@iconify-icons/mingcute/arrow-up-line'),\n 'arrow-down': () => import('@iconify-icons/mingcute/arrow-down-line'),\n earth: () => import('@iconify-icons/mingcute/earth-line'),\n lock: () => import('@iconify-icons/mingcute/lock-line'),\n unlock: () => import('@iconify-icons/mingcute/unlock-line'),\n 'eye-close': () => import('@iconify-icons/mingcute/eye-close-line'),\n 'eye-open': () => import('@iconify-icons/mingcute/eye-2-line'),\n message: () => import('@iconify-icons/mingcute/mail-line'),\n comment: () => import('@iconify-icons/mingcute/chat-3-line'),\n comment2: () => import('@iconify-icons/mingcute/comment-2-line'),\n home: () => import('@iconify-icons/mingcute/home-3-line'),\n notification: () => import('@iconify-icons/mingcute/notification-line'),\n follow: () => import('@iconify-icons/mingcute/user-follow-line'),\n 'follow-add': () => import('@iconify-icons/mingcute/user-add-line'),\n poll: [() => import('@iconify-icons/mingcute/chart-bar-line'), '90deg'],\n pencil: () => import('@iconify-icons/mingcute/pencil-line'),\n quill: () => import('@iconify-icons/mingcute/quill-pen-line'),\n at: () => import('@iconify-icons/mingcute/at-line'),\n attachment: () => import('@iconify-icons/mingcute/attachment-line'),\n upload: () => import('@iconify-icons/mingcute/upload-3-line'),\n gear: () => import('@iconify-icons/mingcute/settings-3-line'),\n more: () => import('@iconify-icons/mingcute/more-3-line'),\n external: () => import('@iconify-icons/mingcute/external-link-line'),\n popout: () => import('@iconify-icons/mingcute/external-link-line'),\n popin: [() => import('@iconify-icons/mingcute/external-link-line'), '180deg'],\n plus: () => import('@iconify-icons/mingcute/add-circle-line'),\n 'chevron-left': () => import('@iconify-icons/mingcute/left-line'),\n 'chevron-right': () => import('@iconify-icons/mingcute/right-line'),\n 'chevron-down': () => import('@iconify-icons/mingcute/down-line'),\n reply: [\n () => import('@iconify-icons/mingcute/share-forward-line'),\n '180deg',\n 'horizontal',\n ],\n thread: () => import('@iconify-icons/mingcute/route-line'),\n group: () => import('@iconify-icons/mingcute/group-line'),\n bot: () => import('@iconify-icons/mingcute/android-2-line'),\n menu: () => import('@iconify-icons/mingcute/rows-4-line'),\n list: () => import('@iconify-icons/mingcute/list-check-line'),\n search: () => import('@iconify-icons/mingcute/search-2-line'),\n hashtag: () => import('@iconify-icons/mingcute/hashtag-line'),\n info: () => import('@iconify-icons/mingcute/information-line'),\n shortcut: () => import('@iconify-icons/mingcute/lightning-line'),\n user: () => import('@iconify-icons/mingcute/user-4-line'),\n following: () => import('@iconify-icons/mingcute/walk-line'),\n pin: () => import('@iconify-icons/mingcute/pin-line'),\n bus: () => import('@iconify-icons/mingcute/bus-2-line'),\n link: () => import('@iconify-icons/mingcute/link-2-line'),\n history: () => import('@iconify-icons/mingcute/history-line'),\n share: () => import('@iconify-icons/mingcute/share-2-line'),\n sparkles: () => import('@iconify-icons/mingcute/sparkles-line'),\n sparkles2: () => import('@iconify-icons/mingcute/sparkles-2-line'),\n exit: () => import('@iconify-icons/mingcute/exit-line'),\n translate: () => import('@iconify-icons/mingcute/translate-line'),\n play: () => import('@iconify-icons/mingcute/play-fill'),\n trash: () => import('@iconify-icons/mingcute/delete-2-line'),\n mute: () => import('@iconify-icons/mingcute/volume-mute-line'),\n unmute: () => import('@iconify-icons/mingcute/volume-line'),\n block: () => import('@iconify-icons/mingcute/forbid-circle-line'),\n unblock: [\n () => import('@iconify-icons/mingcute/forbid-circle-line'),\n '180deg',\n ],\n flag: () => import('@iconify-icons/mingcute/flag-4-line'),\n time: () => import('@iconify-icons/mingcute/time-line'),\n refresh: () => import('@iconify-icons/mingcute/refresh-2-line'),\n emoji2: () => import('@iconify-icons/mingcute/emoji-2-line'),\n filter: () => import('@iconify-icons/mingcute/filter-2-line'),\n chart: () => import('@iconify-icons/mingcute/chart-line-line'),\n react: () => import('@iconify-icons/mingcute/react-line'),\n layout4: () => import('@iconify-icons/mingcute/layout-4-line'),\n layout5: () => import('@iconify-icons/mingcute/layout-5-line'),\n announce: () => import('@iconify-icons/mingcute/announcement-line'),\n alert: () => import('@iconify-icons/mingcute/alert-line'),\n round: () => import('@iconify-icons/mingcute/round-fill'),\n 'arrow-up-circle': () =>\n import('@iconify-icons/mingcute/arrow-up-circle-line'),\n 'arrow-down-circle': () =>\n import('@iconify-icons/mingcute/arrow-down-circle-line'),\n clipboard: () => import('@iconify-icons/mingcute/clipboard-line'),\n 'account-edit': () => import('@iconify-icons/mingcute/user-edit-line'),\n 'account-warning': () => import('@iconify-icons/mingcute/user-warning-line'),\n keyboard: () => import('@iconify-icons/mingcute/keyboard-line'),\n cloud: () => import('@iconify-icons/mingcute/cloud-line'),\n month: () => import('@iconify-icons/mingcute/calendar-month-line'),\n media: () => import('@iconify-icons/mingcute/photo-album-line'),\n speak: () => import('@iconify-icons/mingcute/radar-line'),\n building: () => import('@iconify-icons/mingcute/building-5-line'),\n};\n\nconst ICONDATA = {};\n\nfunction Icon({\n icon,\n size = 'm',\n alt,\n title,\n class: className = '',\n style = {},\n}) {\n if (!icon) return null;\n\n const iconSize = SIZES[size];\n let iconBlock = ICONS[icon];\n let rotate, flip;\n if (Array.isArray(iconBlock)) {\n [iconBlock, rotate, flip] = iconBlock;\n }\n\n const [iconData, setIconData] = useState(ICONDATA[icon]);\n const currentIcon = useRef(icon);\n useEffect(() => {\n if (iconData && currentIcon.current === icon) return;\n (async () => {\n const iconB = await iconBlock();\n setIconData(iconB.default);\n ICONDATA[icon] = iconB.default;\n })();\n currentIcon.current = icon;\n }, [icon]);\n\n return (\n <span\n class={`icon ${className}`}\n title={title || alt}\n style={{\n width: `${iconSize}px`,\n height: `${iconSize}px`,\n ...style,\n }}\n >\n {iconData && (\n <svg\n width={iconSize}\n height={iconSize}\n viewBox={`0 0 ${iconData.width} ${iconData.height}`}\n dangerouslySetInnerHTML={{ __html: iconData.body }}\n style={{\n transform: `${rotate ? `rotate(${rotate})` : ''} ${\n flip ? `scaleX(-1)` : ''\n }`,\n }}\n />\n )}\n </span>\n );\n}\n\nexport default Icon;\n","import { useEffect } from 'preact/hooks';\n\nfunction useCloseWatcher(fn, deps = []) {\n if (!fn || typeof fn !== 'function') return;\n useEffect(() => {\n const watcher = new CloseWatcher();\n watcher.addEventListener('close', fn);\n return () => {\n watcher.destroy();\n };\n }, deps);\n}\n\nexport default window.CloseWatcher ? useCloseWatcher : () => {};\n","import './modal.css';\n\nimport { createPortal } from 'preact/compat';\nimport { useEffect, useRef } from 'preact/hooks';\nimport { useHotkeys } from 'react-hotkeys-hook';\n\nimport useCloseWatcher from '../utils/useCloseWatcher';\n\nconst $modalContainer = document.getElementById('modal-container');\n\nfunction Modal({ children, onClose, onClick, class: className }) {\n if (!children) return null;\n\n const modalRef = useRef();\n useEffect(() => {\n let timer = setTimeout(() => {\n const focusElement = modalRef.current?.querySelector('[tabindex=\"-1\"]');\n if (focusElement) {\n focusElement.focus();\n }\n }, 100);\n return () => clearTimeout(timer);\n }, []);\n\n const supportsCloseWatcher = window.CloseWatcher;\n const escRef = useHotkeys(\n 'esc',\n () => {\n setTimeout(() => {\n onClose?.();\n }, 0);\n },\n {\n enabled: !supportsCloseWatcher && !!onClose,\n // Using keyup and setTimeout above\n // This will run \"later\" to prevent clash with esc handlers from other components\n keydown: false,\n keyup: true,\n },\n [onClose],\n );\n useCloseWatcher(onClose, [onClose]);\n\n const Modal = (\n <div\n ref={(node) => {\n modalRef.current = node;\n escRef.current = node?.querySelector?.('[tabindex=\"-1\"]') || node;\n }}\n className={className}\n onClick={(e) => {\n onClick?.(e);\n if (e.target === e.currentTarget) {\n onClose?.(e);\n }\n }}\n tabIndex=\"-1\"\n onFocus={(e) => {\n if (e.target === e.currentTarget) {\n modalRef.current?.querySelector?.('[tabindex=\"-1\"]')?.focus?.();\n }\n }}\n >\n {children}\n </div>\n );\n\n return createPortal(Modal, $modalContainer);\n\n // return createPortal(children, $modalContainer);\n}\n\nexport default Modal;\n","import './loader.css';\n\nfunction Loader({ abrupt, hidden, ...props }) {\n return (\n <span\n {...props}\n class={`loader-container ${abrupt ? 'abrupt' : ''} ${\n hidden ? 'hidden' : ''\n }`}\n >\n <span class=\"loader\" />\n </span>\n );\n}\n\nexport default Loader;\n","import './avatar.css';\n\nimport { useRef } from 'preact/hooks';\n\nimport mem from '../utils/mem';\n\nconst SIZES = {\n s: 16,\n m: 20,\n l: 24,\n xl: 32,\n xxl: 50,\n xxxl: 64,\n};\n\nconst alphaCache = {};\n\nconst canvas = window.OffscreenCanvas\n ? new OffscreenCanvas(1, 1)\n : document.createElement('canvas');\nconst ctx = canvas.getContext('2d', {\n willReadFrequently: true,\n});\n\nfunction Avatar({ url, size, alt = '', squircle, ...props }) {\n size = SIZES[size] || size || SIZES.m;\n const avatarRef = useRef();\n const isMissing = /missing\\.png$/.test(url);\n return (\n <span\n ref={avatarRef}\n class={`avatar ${squircle ? 'squircle' : ''} ${\n alphaCache[url] ? 'has-alpha' : ''\n }`}\n style={{\n width: size,\n height: size,\n }}\n title={alt}\n {...props}\n >\n {!!url && (\n <img\n src={url}\n width={size}\n height={size}\n alt={alt}\n loading=\"lazy\"\n decoding=\"async\"\n crossOrigin={\n alphaCache[url] === undefined && !isMissing\n ? 'anonymous'\n : undefined\n }\n onError={(e) => {\n if (e.target.crossOrigin) {\n e.target.crossOrigin = null;\n e.target.src = url;\n }\n }}\n onLoad={(e) => {\n if (avatarRef.current) avatarRef.current.dataset.loaded = true;\n if (alphaCache[url] !== undefined) return;\n if (isMissing) return;\n queueMicrotask(() => {\n try {\n // Check if image has alpha channel\n const { width, height } = e.target;\n if (canvas.width !== width) canvas.width = width;\n if (canvas.height !== height) canvas.height = height;\n ctx.drawImage(e.target, 0, 0);\n const allPixels = ctx.getImageData(0, 0, width, height);\n // At least 10% of pixels have alpha <= 128\n const hasAlpha =\n allPixels.data.filter(\n (pixel, i) => i % 4 === 3 && pixel <= 128,\n ).length /\n (allPixels.data.length / 4) >\n 0.1;\n if (hasAlpha) {\n // console.log('hasAlpha', hasAlpha, allPixels.data);\n avatarRef.current.classList.add('has-alpha');\n }\n alphaCache[url] = hasAlpha;\n ctx.clearRect(0, 0, width, height);\n } catch (e) {\n // Silent fail\n alphaCache[url] = false;\n }\n });\n }}\n />\n )}\n </span>\n );\n}\n\nexport default mem(Avatar);\n","import { forwardRef } from 'preact/compat';\nimport { useLocation } from 'react-router-dom';\n\nimport states from '../utils/states';\n\n/* NOTES\n =====\n Initially this uses <NavLink> from react-router-dom, but it doesn't work:\n 1. It interferes with nested <a> inside <a> and it's difficult to preventDefault/stopPropagation from the nested <a>\n 2. isActive doesn't work properly with the weird routes that's set up in this app, due to the faux \"location\" to make the modals work and prevent unmounting\n 3. Not using <Link state/> because it modifies history.state that *persists* across page reloads. I don't need that, so using valtio's states instead.\n*/\n\nconst Link = forwardRef((props, ref) => {\n let routerLocation;\n try {\n routerLocation = useLocation();\n } catch (e) {}\n let hash = (location.hash || '').replace(/^#/, '').trim();\n if (hash === '') hash = '/';\n const { to, ...restProps } = props;\n\n // Handle encodeURIComponent of searchParams values\n if (!!hash && hash !== '/' && hash.includes('?')) {\n try {\n const parsedHash = new URL(hash, location.origin); // Fake base URL\n if (parsedHash.searchParams.size) {\n const searchParamsStr = Array.from(parsedHash.searchParams.entries())\n .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)\n .join('&');\n hash = parsedHash.pathname + '?' + searchParamsStr;\n }\n } catch (e) {}\n }\n\n const isActive = hash === to || decodeURIComponent(hash) === to;\n return (\n <a\n ref={ref}\n href={`#${to}`}\n {...restProps}\n class={`${props.class || ''} ${isActive ? 'is-active' : ''}`}\n onClick={(e) => {\n if (e.currentTarget?.parentNode?.closest('a')) {\n // If this <a> is nested inside another <a>\n e.stopPropagation();\n }\n if (routerLocation) states.prevLocation = routerLocation;\n props.onClick?.(e);\n }}\n />\n );\n});\n\nexport default Link;\n","const root = document.documentElement;\nconst style = getComputedStyle(root);\nconst defaultBoundingBoxPadding = 8;\n\nlet safeAreaInsets = [0, 0, 0, 0];\nfunction getSafeAreaInsets() {\n // Get safe area inset variables from root\n const safeAreaInsetTop = style.getPropertyValue('--sai-top');\n const safeAreaInsetRight = style.getPropertyValue('--sai-right');\n const safeAreaInsetBottom = style.getPropertyValue('--sai-bottom');\n const safeAreaInsetLeft = style.getPropertyValue('--sai-left');\n safeAreaInsets = [\n // top, right, bottom, left (clockwise)\n Math.max(0, parseInt(safeAreaInsetTop, 10)),\n Math.max(0, parseInt(safeAreaInsetRight, 10)),\n Math.max(0, parseInt(safeAreaInsetBottom, 10)),\n Math.max(0, parseInt(safeAreaInsetLeft, 10)),\n ];\n}\nrequestAnimationFrame(getSafeAreaInsets);\n\nfunction safeBoundingBoxPadding(paddings = []) {\n const str = safeAreaInsets\n .map((v, i) => (v || defaultBoundingBoxPadding) + (paddings[i] || 0))\n .join(' ');\n // console.log(str);\n return str;\n}\n\n// Update safe area insets when orientation or resize\nif (CSS.supports('top: env(safe-area-inset-top)')) {\n window.addEventListener('resize', getSafeAreaInsets, { passive: true });\n}\n\nexport default safeBoundingBoxPadding;\n","import { Menu } from '@szhsin/react-menu';\nimport { useWindowSize } from '@uidotdev/usehooks';\nimport { useRef } from 'preact/hooks';\n\nimport safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';\n\n// It's like Menu but with sensible defaults, bug fixes and improvements.\nfunction Menu2(props) {\n const { containerProps, instanceRef: _instanceRef } = props;\n const size = useWindowSize();\n const instanceRef = _instanceRef?.current ? _instanceRef : useRef();\n\n return (\n <Menu\n boundingBoxPadding={safeBoundingBoxPadding()}\n repositionFlag={`${size.width}x${size.height}`}\n unmountOnClose\n {...props}\n instanceRef={instanceRef}\n containerProps={{\n onClick: (e) => {\n if (e.target === e.currentTarget) {\n instanceRef.current?.closeMenu?.();\n }\n containerProps?.onClick?.(e);\n },\n ...containerProps,\n }}\n />\n );\n}\n\nexport default Menu2;\n","import { Menu, MenuItem, SubMenu } from '@szhsin/react-menu';\nimport { cloneElement } from 'preact';\nimport { useRef } from 'preact/hooks';\n\nimport Menu2 from './menu2';\n\nfunction MenuConfirm({\n subMenu = false,\n confirm = true,\n confirmLabel,\n menuItemClassName,\n menuFooter,\n ...props\n}) {\n const { children, onClick, ...restProps } = props;\n if (!confirm) {\n if (subMenu) return <MenuItem {...props} />;\n if (onClick) {\n return cloneElement(children, {\n onClick,\n });\n }\n return children;\n }\n const Parent = subMenu ? SubMenu : Menu2;\n const menuRef = useRef();\n return (\n <Parent\n instanceRef={menuRef}\n openTrigger=\"clickOnly\"\n direction=\"bottom\"\n overflow=\"auto\"\n gap={-8}\n shift={8}\n menuClassName=\"menu-emphasized\"\n {...restProps}\n menuButton={subMenu ? undefined : children}\n label={subMenu ? children : undefined}\n // Test fix for bug; submenus not opening on Android\n itemProps={{\n onPointerMove: (e) => {\n if (e.pointerType === 'touch') {\n menuRef.current?.openMenu?.();\n }\n },\n onPointerLeave: (e) => {\n if (e.pointerType === 'touch') {\n menuRef.current?.openMenu?.();\n }\n },\n }}\n >\n <MenuItem className={menuItemClassName} onClick={onClick}>\n {confirmLabel}\n </MenuItem>\n {menuFooter}\n </Parent>\n );\n}\n\nexport default MenuConfirm;\n","function EmojiText({ text, emojis }) {\n if (!text) return '';\n if (!emojis?.length) return text;\n if (text.indexOf(':') === -1) return text;\n const regex = new RegExp(\n `:(${emojis.map((e) => e.shortcode).join('|')}):`,\n 'g',\n );\n const elements = text.split(regex).map((word) => {\n const emoji = emojis.find((e) => e.shortcode === word);\n if (emoji) {\n const { url, staticUrl } = emoji;\n return (\n <picture>\n <source srcset={staticUrl} media=\"(prefers-reduced-motion: reduce)\" />\n <img\n key={word}\n src={url}\n alt={word}\n class=\"shortcode-emoji emoji\"\n width=\"16\"\n height=\"16\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </picture>\n );\n }\n return word;\n });\n return elements;\n}\n\nexport default EmojiText;\n","import './name-text.css';\n\nimport { memo } from 'preact/compat';\n\nimport states from '../utils/states';\n\nimport Avatar from './avatar';\nimport EmojiText from './emoji-text';\n\nconst nameCollator = new Intl.Collator('en', {\n sensitivity: 'base',\n});\n\nfunction NameText({\n account,\n instance,\n showAvatar,\n showAcct,\n short,\n external,\n onClick,\n}) {\n const { acct, avatar, avatarStatic, id, url, displayName, emojis, bot } =\n account;\n let { username } = account;\n const [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];\n\n const trimmedUsername = username.toLowerCase().trim();\n const trimmedDisplayName = (displayName || '').toLowerCase().trim();\n const shortenedDisplayName = trimmedDisplayName\n .replace(/(\\:(\\w|\\+|\\-)+\\:)(?=|[\\!\\.\\?]|$)/g, '') // Remove shortcodes, regex from https://regex101.com/r/iE9uV0/1\n .replace(/\\s+/g, ''); // E.g. \"My name\" === \"myname\"\n const shortenedAlphaNumericDisplayName = shortenedDisplayName.replace(\n /[^a-z0-9]/gi,\n '',\n ); // Remove non-alphanumeric characters\n\n if (\n !short &&\n (trimmedUsername === trimmedDisplayName ||\n trimmedUsername === shortenedDisplayName ||\n trimmedUsername === shortenedAlphaNumericDisplayName ||\n nameCollator.compare(trimmedUsername, shortenedDisplayName) === 0)\n ) {\n username = null;\n }\n\n return (\n <a\n class={`name-text ${showAcct ? 'show-acct' : ''} ${short ? 'short' : ''}`}\n href={url}\n target={external ? '_blank' : null}\n title={`${displayName ? `${displayName} ` : ''}@${acct}`}\n onClick={(e) => {\n if (external) return;\n e.preventDefault();\n e.stopPropagation();\n if (onClick) return onClick(e);\n states.showAccount = {\n account,\n instance,\n };\n }}\n >\n {showAvatar && (\n <>\n <Avatar url={avatarStatic || avatar} squircle={bot} />{' '}\n </>\n )}\n {displayName && !short ? (\n <>\n <b>\n <EmojiText text={displayName} emojis={emojis} />\n </b>\n {!showAcct && username && (\n <>\n {' '}\n <i>@{username}</i>\n </>\n )}\n </>\n ) : short ? (\n <i>{username}</i>\n ) : (\n <b>{username}</b>\n )}\n {showAcct && (\n <>\n <br />\n <i>\n @{acct1}\n <span class=\"ib\">{acct2}</span>\n </i>\n </>\n )}\n </a>\n );\n}\n\nexport default memo(NameText);\n","// Twitter-style relative time component\n// Seconds = 1s\n// Minutes = 1m\n// Hours = 1h\n// Days = 1d\n// After 7 days, use DD/MM/YYYY or MM/DD/YYYY\nimport dayjs from 'dayjs';\nimport dayjsTwitter from 'dayjs-twitter';\nimport localizedFormat from 'dayjs/plugin/localizedFormat';\nimport relativeTime from 'dayjs/plugin/relativeTime';\n\ndayjs.extend(dayjsTwitter);\ndayjs.extend(localizedFormat);\ndayjs.extend(relativeTime);\n\nconst dtf = new Intl.DateTimeFormat();\n\nexport default function RelativeTime({ datetime, format }) {\n if (!datetime) return null;\n const date = dayjs(datetime);\n let dateStr;\n if (format === 'micro') {\n // If date <= 1 day ago or day is within this year\n const now = dayjs();\n const dayDiff = now.diff(date, 'day');\n if (dayDiff <= 1 || now.year() === date.year()) {\n dateStr = date.twitter();\n } else {\n dateStr = dtf.format(date.toDate());\n }\n } else {\n dateStr = date.fromNow();\n }\n\n return (\n <time datetime={date.toISOString()} title={date.format('LLLL')}>\n {dateStr}\n </time>\n );\n}\n","import { match } from '@formatjs/intl-localematcher';\n\nimport mem from './mem';\n\nfunction _localeMatch(...args) {\n // Wrap in try/catch because localeMatcher throws on invalid locales\n try {\n return match(...args);\n } catch (e) {\n const defaultLocale = args[2];\n return defaultLocale || false;\n }\n}\nconst localeMatch = mem(_localeMatch);\n\nexport default localeMatch;\n","import translationTargetLanguages from '../data/lingva-target-languages';\n\nimport localeMatch from './locale-match';\nimport states from './states';\n\nconst locales = [\n new Intl.DateTimeFormat().resolvedOptions().locale,\n ...navigator.languages,\n];\n\nconst localeTargetLanguages = localeMatch(\n locales,\n translationTargetLanguages.map((l) => l.code.replace('_', '-')), // The underscore will fail Intl.Locale inside `match`\n 'en',\n);\n\nfunction getTranslateTargetLanguage(fromSettings = false) {\n if (fromSettings) {\n const { contentTranslationTargetLanguage } = states.settings;\n if (contentTranslationTargetLanguage) {\n return contentTranslationTargetLanguage;\n }\n }\n return localeTargetLanguages;\n}\n\nexport default getTranslateTargetLanguage;\n","export default function localeCode2Text(code) {\n try {\n return new Intl.DisplayNames(navigator.languages, {\n type: 'language',\n }).of(code);\n } catch (e) {\n console.error(e);\n return null;\n }\n}\n","function emojifyText(text, emojis = []) {\n if (!text) return '';\n if (!emojis.length) return text;\n if (text.indexOf(':') === -1) return text;\n // Replace shortcodes in text with emoji\n // emojis = [{ shortcode: 'smile', url: 'https://example.com/emoji.png' }]\n emojis.forEach((emoji) => {\n const { shortcode, staticUrl, url } = emoji;\n text = text.replace(\n new RegExp(`:${shortcode}:`, 'g'),\n `<picture><source srcset=\"${staticUrl}\" media=\"(prefers-reduced-motion: reduce)\"></source><img class=\"shortcode-emoji emoji\" src=\"${url}\" alt=\":${shortcode}:\" width=\"16\" height=\"16\" loading=\"lazy\" decoding=\"async\" /></picture>`,\n );\n });\n // console.log(text, emojis);\n return text;\n}\n\nexport default emojifyText;\n","import emojifyText from './emojify-text';\nimport mem from './mem';\n\nconst fauxDiv = document.createElement('div');\nconst whitelistLinkClasses = ['u-url', 'mention', 'hashtag'];\n\nfunction _enhanceContent(content, opts = {}) {\n const { emojis, postEnhanceDOM = () => {} } = opts;\n let enhancedContent = content;\n const dom = document.createElement('div');\n dom.innerHTML = enhancedContent;\n const hasLink = /<a/i.test(enhancedContent);\n const hasCodeBlock = enhancedContent.includes('```');\n\n if (hasLink) {\n // Add target=\"_blank\" to all links with no target=\"_blank\"\n // E.g. `note` in `account`\n const noTargetBlankLinks = dom.querySelectorAll('a:not([target=\"_blank\"])');\n noTargetBlankLinks.forEach((link) => {\n link.setAttribute('target', '_blank');\n });\n\n // Remove all classes except `u-url`, `mention`, `hashtag`\n const links = dom.querySelectorAll('a[class]');\n links.forEach((link) => {\n link.classList.forEach((c) => {\n if (!whitelistLinkClasses.includes(c)) {\n link.classList.remove(c);\n }\n });\n });\n }\n\n // Add 'has-url-text' to all links that contains a url\n if (hasLink) {\n const links = dom.querySelectorAll('a[href]');\n links.forEach((link) => {\n if (/^https?:\\/\\//i.test(link.textContent.trim())) {\n link.classList.add('has-url-text');\n }\n });\n }\n\n // Spanify un-spanned mentions\n if (hasLink) {\n const links = dom.querySelectorAll('a[href]');\n const usernames = [];\n links.forEach((link) => {\n const text = link.innerText.trim();\n const hasChildren = link.querySelector('*');\n // If text looks like @username@domain, then it's a mention\n if (/^@[^@]+(@[^@]+)?$/g.test(text)) {\n // Only show @username\n const [_, username, domain] = text.split('@');\n if (!hasChildren) {\n if (\n !usernames.some(([u]) => u === username) ||\n usernames.some(([u, d]) => u === username && d === domain)\n ) {\n link.innerHTML = `@<span>${username}</span>`;\n usernames.push([username, domain]);\n } else {\n link.innerHTML = `@<span>${username}@${domain}</span>`;\n }\n }\n link.classList.add('mention');\n }\n // If text looks like #hashtag, then it's a hashtag\n if (/^#[^#]+$/g.test(text)) {\n if (!hasChildren) link.innerHTML = `#<span>${text.slice(1)}</span>`;\n link.classList.add('mention', 'hashtag');\n }\n });\n }\n\n // EMOJIS\n // ======\n // Convert :shortcode: to <img />\n let textNodes;\n if (enhancedContent.includes(':')) {\n textNodes = extractTextNodes(dom);\n textNodes.forEach((node) => {\n let html = node.nodeValue\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n if (emojis) {\n html = emojifyText(html, emojis);\n }\n fauxDiv.innerHTML = html;\n // const nodes = [...fauxDiv.childNodes];\n node.replaceWith(...fauxDiv.childNodes);\n });\n }\n\n // CODE BLOCKS\n // ===========\n // Convert ```code``` to <pre><code>code</code></pre>\n if (hasCodeBlock) {\n const blocks = [...dom.querySelectorAll('p')].filter((p) =>\n /^```[^]+```$/g.test(p.innerText.trim()),\n );\n blocks.forEach((block) => {\n const pre = document.createElement('pre');\n // Replace <br /> with newlines\n block.querySelectorAll('br').forEach((br) => br.replaceWith('\\n'));\n pre.innerHTML = `<code>${block.innerHTML.trim()}</code>`;\n block.replaceWith(pre);\n });\n }\n\n // Convert multi-paragraph code blocks to <pre><code>code</code></pre>\n if (hasCodeBlock) {\n const paragraphs = [...dom.querySelectorAll('p')];\n // Filter out paragraphs with ``` in beginning only\n const codeBlocks = paragraphs.filter((p) => /^```/g.test(p.innerText));\n // For each codeBlocks, get all paragraphs until the last paragraph with ``` at the end only\n codeBlocks.forEach((block) => {\n const nextParagraphs = [block];\n let hasCodeBlock = false;\n let currentBlock = block;\n while (currentBlock.nextElementSibling) {\n const next = currentBlock.nextElementSibling;\n if (next && next.tagName === 'P') {\n if (/```$/g.test(next.innerText)) {\n nextParagraphs.push(next);\n hasCodeBlock = true;\n break;\n } else {\n nextParagraphs.push(next);\n }\n } else {\n break;\n }\n currentBlock = next;\n }\n if (hasCodeBlock) {\n const pre = document.createElement('pre');\n nextParagraphs.forEach((p) => {\n // Replace <br /> with newlines\n p.querySelectorAll('br').forEach((br) => br.replaceWith('\\n'));\n });\n const codeText = nextParagraphs.map((p) => p.innerHTML).join('\\n\\n');\n pre.innerHTML = `<code tabindex=\"0\">${codeText}</code>`;\n block.replaceWith(pre);\n nextParagraphs.forEach((p) => p.remove());\n }\n });\n }\n\n // INLINE CODE\n // ===========\n // Convert `code` to <code>code</code>\n if (enhancedContent.includes('`')) {\n textNodes = extractTextNodes(dom);\n textNodes.forEach((node) => {\n let html = node.nodeValue\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n if (/`[^`]+`/g.test(html)) {\n html = html.replaceAll(/(`[^]+?`)/g, '<code>$1</code>');\n }\n fauxDiv.innerHTML = html;\n // const nodes = [...fauxDiv.childNodes];\n node.replaceWith(...fauxDiv.childNodes);\n });\n }\n\n // TWITTER USERNAMES\n // =================\n // Convert @username@twitter.com to <a href=\"https://twitter.com/username\">@username@twitter.com</a>\n if (/twitter\\.com/i.test(enhancedContent)) {\n textNodes = extractTextNodes(dom, {\n rejectFilter: ['A'],\n });\n textNodes.forEach((node) => {\n let html = node.nodeValue\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n if (/@[a-zA-Z0-9_]+@twitter\\.com/g.test(html)) {\n html = html.replaceAll(\n /(@([a-zA-Z0-9_]+)@twitter\\.com)/g,\n '<a href=\"https://twitter.com/$2\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">$1</a>',\n );\n }\n fauxDiv.innerHTML = html;\n // const nodes = [...fauxDiv.childNodes];\n node.replaceWith(...fauxDiv.childNodes);\n });\n }\n\n // HASHTAG STUFFING\n // ================\n // Get the <p> that contains a lot of hashtags, add a class to it\n if (enhancedContent.includes('#')) {\n let prevIndex = null;\n const hashtagStuffedParagraphs = [...dom.querySelectorAll('p')].filter(\n (p, index) => {\n let hashtagCount = 0;\n for (let i = 0; i < p.childNodes.length; i++) {\n const node = p.childNodes[i];\n\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent.trim();\n if (text !== '') {\n return false;\n }\n } else if (node.tagName === 'BR') {\n // Ignore <br />\n } else if (node.tagName === 'A') {\n const linkText = node.textContent.trim();\n if (!linkText || !linkText.startsWith('#')) {\n return false;\n } else {\n hashtagCount++;\n }\n } else {\n return false;\n }\n }\n // Only consider \"stuffing\" if:\n // - there are more than 3 hashtags\n // - there are more than 1 hashtag in adjacent paragraphs\n if (hashtagCount > 3) {\n prevIndex = index;\n return true;\n }\n if (hashtagCount > 1 && prevIndex && index === prevIndex + 1) {\n prevIndex = index;\n return true;\n }\n },\n );\n if (hashtagStuffedParagraphs?.length) {\n hashtagStuffedParagraphs.forEach((p) => {\n p.classList.add('hashtag-stuffing');\n p.title = p.innerText;\n });\n }\n }\n\n if (postEnhanceDOM) {\n queueMicrotask(() => postEnhanceDOM(dom));\n // postEnhanceDOM(dom); // mutate dom\n }\n\n enhancedContent = dom.innerHTML;\n\n return enhancedContent;\n}\nconst enhanceContent = mem(_enhanceContent);\n\nconst defaultRejectFilter = [\n // Document metadata\n 'STYLE',\n // Image and multimedia\n 'IMG',\n 'VIDEO',\n 'AUDIO',\n 'AREA',\n 'MAP',\n 'TRACK',\n // Embedded content\n 'EMBED',\n 'IFRAME',\n 'OBJECT',\n 'PICTURE',\n 'PORTAL',\n 'SOURCE',\n // SVG and MathML\n 'SVG',\n 'MATH',\n // Scripting\n 'CANVAS',\n 'NOSCRIPT',\n 'SCRIPT',\n // Forms\n 'INPUT',\n 'OPTION',\n 'TEXTAREA',\n // Web Components\n 'SLOT',\n 'TEMPLATE',\n];\nconst defaultRejectFilterMap = Object.fromEntries(\n defaultRejectFilter.map((nodeName) => [nodeName, true]),\n);\nfunction extractTextNodes(dom, opts = {}) {\n const textNodes = [];\n const rejectFilterMap = Object.assign(\n {},\n defaultRejectFilterMap,\n opts.rejectFilter?.reduce((acc, cur) => {\n acc[cur] = true;\n return acc;\n }, {}),\n );\n const walk = document.createTreeWalker(\n dom,\n NodeFilter.SHOW_TEXT,\n {\n acceptNode(node) {\n if (rejectFilterMap[node.parentNode.nodeName]) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n },\n false,\n );\n let node;\n while ((node = walk.nextNode())) {\n textNodes.push(node);\n }\n return textNodes;\n}\n\nexport default enhanceContent;\n","import mem from './mem';\n\nconst div = document.createElement('div');\nfunction getHTMLText(html) {\n if (!html) return '';\n div.innerHTML = html\n .replace(/<\\/p>/g, '</p>\\n\\n')\n .replace(/<\\/li>/g, '</li>\\n');\n div.querySelectorAll('br').forEach((br) => {\n br.replaceWith('\\n');\n });\n return div.innerText.replace(/[\\r\\n]{3,}/g, '\\n\\n').trim();\n}\n\nexport default mem(getHTMLText);\n","import states from './states';\n\nfunction handleContentLinks(opts) {\n const { mentions = [], instance, previewMode, statusURL } = opts || {};\n return (e) => {\n let { target } = e;\n target = target.closest('a');\n if (!target) return;\n\n // If cmd/ctrl/shift/alt key is pressed or middle-click, let the browser handle it\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.which === 2) {\n return;\n }\n\n const prevText = target.previousSibling?.textContent;\n const textBeforeLinkIsAt = prevText?.endsWith('@');\n const textStartsWithAt = target.innerText.startsWith('@');\n if (\n (target.classList.contains('u-url') && textStartsWithAt) ||\n (textBeforeLinkIsAt && !textStartsWithAt)\n ) {\n const targetText = (\n target.querySelector('span') || target\n ).innerText.trim();\n const username = targetText.replace(/^@/, '');\n const url = target.getAttribute('href');\n const mention = mentions.find(\n (mention) =>\n mention.username === username ||\n mention.acct === username ||\n mention.url === url,\n );\n if (mention) {\n e.preventDefault();\n e.stopPropagation();\n states.showAccount = {\n account: mention.acct,\n instance,\n };\n } else if (!/^http/i.test(targetText)) {\n console.log('mention not found', targetText);\n e.preventDefault();\n e.stopPropagation();\n const href = target.getAttribute('href');\n states.showAccount = {\n account: href,\n instance,\n };\n }\n } else if (!previewMode) {\n const textBeforeLinkIsHash = prevText?.endsWith('#');\n if (target.classList.contains('hashtag') || textBeforeLinkIsHash) {\n e.preventDefault();\n e.stopPropagation();\n const tag = target.innerText.replace(/^#/, '').trim();\n const hashURL = instance ? `#/${instance}/t/${tag}` : `#/t/${tag}`;\n console.log({ hashURL });\n location.hash = hashURL;\n } else if (\n states.unfurledLinks[target.href]?.url &&\n statusURL !== target.href\n ) {\n // If unfurled AND not self-referential\n e.preventDefault();\n e.stopPropagation();\n states.prevLocation = {\n pathname: location.hash.replace(/^#/, ''),\n };\n location.hash = `#${states.unfurledLinks[target.href].url}`;\n }\n }\n };\n}\n\nexport default handleContentLinks;\n","import mem from './mem';\n\nconst { locale } = new Intl.DateTimeFormat().resolvedOptions();\n\nconst _DateTimeFormat = (opts) => {\n const { dateYear, hideTime, formatOpts } = opts || {};\n const currentYear = new Date().getFullYear();\n return Intl.DateTimeFormat(locale, {\n // Show year if not current year\n year: dateYear === currentYear ? undefined : 'numeric',\n month: 'short',\n day: 'numeric',\n // Hide time if requested\n hour: hideTime ? undefined : 'numeric',\n minute: hideTime ? undefined : 'numeric',\n ...formatOpts,\n });\n};\nconst DateTimeFormat = mem(_DateTimeFormat);\n\nfunction niceDateTime(date, dtfOpts) {\n if (!(date instanceof Date)) {\n date = new Date(date);\n }\n const DTF = DateTimeFormat({\n dateYear: date.getFullYear(),\n ...dtfOpts,\n });\n const dateText = DTF.format(date);\n return dateText;\n}\n\nexport default niceDateTime;\n","const { locale } = Intl.NumberFormat().resolvedOptions();\nconst shortenNumber = Intl.NumberFormat(locale, {\n notation: 'compact',\n}).format;\nexport default shortenNumber;\n","import './account-block.css';\n\n// import { useNavigate } from 'react-router-dom';\nimport enhanceContent from '../utils/enhance-content';\nimport niceDateTime from '../utils/nice-date-time';\nimport shortenNumber from '../utils/shorten-number';\nimport states from '../utils/states';\n\nimport Avatar from './avatar';\nimport EmojiText from './emoji-text';\nimport Icon from './icon';\n\nfunction AccountBlock({\n skeleton,\n account,\n avatarSize = 'xl',\n useAvatarStatic = false,\n instance,\n external,\n internal,\n onClick,\n showActivity = false,\n showStats = false,\n accountInstance,\n hideDisplayName = false,\n relationship = {},\n excludeRelationshipAttrs = [],\n}) {\n if (skeleton) {\n return (\n <div class=\"account-block skeleton\">\n <Avatar size={avatarSize} />\n <span>\n <b>████████</b>\n <br />\n <span class=\"account-block-acct\">@██████</span>\n </span>\n </div>\n );\n }\n\n if (!account) {\n return null;\n }\n\n // const navigate = useNavigate();\n\n const {\n id,\n acct,\n avatar,\n avatarStatic,\n displayName,\n username,\n emojis,\n url,\n statusesCount,\n lastStatusAt,\n bot,\n fields,\n note,\n group,\n followersCount,\n } = account;\n let [_, acct1, acct2] = acct.match(/([^@]+)(@.+)/i) || [, acct];\n if (accountInstance) {\n acct2 = `@${accountInstance}`;\n }\n\n const verifiedField = fields?.find((f) => !!f.verifiedAt && !!f.value);\n\n const excludedRelationship = {};\n for (const r in relationship) {\n if (!excludeRelationshipAttrs.includes(r)) {\n excludedRelationship[r] = relationship[r];\n }\n }\n const hasRelationship =\n excludedRelationship.following ||\n excludedRelationship.followedBy ||\n excludedRelationship.requested;\n\n return (\n <a\n class=\"account-block\"\n href={url}\n target={external ? '_blank' : null}\n title={`@${acct}`}\n onClick={(e) => {\n if (external) return;\n e.preventDefault();\n if (onClick) return onClick(e);\n if (internal) {\n // navigate(`/${instance}/a/${id}`);\n location.hash = `/${instance}/a/${id}`;\n } else {\n states.showAccount = {\n account,\n instance,\n };\n }\n }}\n >\n <Avatar\n url={useAvatarStatic ? avatarStatic : avatar || avatarStatic}\n size={avatarSize}\n squircle={bot}\n />\n <span class=\"account-block-content\">\n {!hideDisplayName && (\n <>\n {displayName ? (\n <b>\n <EmojiText text={displayName} emojis={emojis} />\n </b>\n ) : (\n <b>{username}</b>\n )}\n </>\n )}{' '}\n <span class=\"account-block-acct\">\n @{acct1}\n <wbr />\n {acct2}\n </span>\n {showActivity && (\n <>\n <br />\n <small class=\"last-status-at insignificant\">\n Posts: {statusesCount}\n {!!lastStatusAt && (\n <>\n {' '}\n &middot; Last posted:{' '}\n {niceDateTime(lastStatusAt, {\n hideTime: true,\n })}\n </>\n )}\n </small>\n </>\n )}\n {showStats && (\n <div class=\"account-block-stats\">\n {bot && (\n <>\n <span class=\"tag collapsed\">\n <Icon icon=\"bot\" /> Automated\n </span>\n </>\n )}\n {!!group && (\n <>\n <span class=\"tag collapsed\">\n <Icon icon=\"group\" /> Group\n </span>\n </>\n )}\n {hasRelationship && (\n <div key={relationship.id} class=\"shazam-container-horizontal\">\n <div class=\"shazam-container-inner\">\n {excludedRelationship.following &&\n excludedRelationship.followedBy ? (\n <span class=\"tag minimal\">Mutual</span>\n ) : excludedRelationship.requested ? (\n <span class=\"tag minimal\">Requested</span>\n ) : excludedRelationship.following ? (\n <span class=\"tag minimal\">Following</span>\n ) : excludedRelationship.followedBy ? (\n <span class=\"tag minimal\">Follows you</span>\n ) : null}\n </div>\n </div>\n )}\n {!!followersCount && (\n <span class=\"ib\">\n {shortenNumber(followersCount)}{' '}\n {followersCount === 1 ? 'follower' : 'followers'}\n </span>\n )}\n {!!verifiedField && (\n <span class=\"verified-field\">\n <Icon icon=\"check-circle\" size=\"s\" />{' '}\n <span\n dangerouslySetInnerHTML={{\n __html: enhanceContent(verifiedField.value, { emojis }),\n }}\n />\n </span>\n )}\n </div>\n )}\n </span>\n </a>\n );\n}\n\nexport default AccountBlock;\n","import { satisfies } from 'compare-versions';\n\nimport features from '../data/features.json';\n\nimport { getCurrentInstance } from './store-utils';\n\nconst supportsCache = {};\n\nfunction supports(feature) {\n try {\n const { version, domain } = getCurrentInstance();\n const key = `${domain}-${feature}`;\n if (supportsCache[key]) return supportsCache[key];\n const range = features[feature];\n if (!range) return false;\n return (supportsCache[key] = satisfies(version, range, {\n includePrerelease: true,\n loose: true,\n }));\n } catch (e) {\n return false;\n }\n}\n\nexport default supports;\n","import './translation-block.css';\n\nimport pRetry from 'p-retry';\nimport pThrottle from 'p-throttle';\nimport { useEffect, useRef, useState } from 'preact/hooks';\n\nimport sourceLanguages from '../data/lingva-source-languages';\nimport getTranslateTargetLanguage from '../utils/get-translate-target-language';\nimport localeCode2Text from '../utils/localeCode2Text';\nimport pmem from '../utils/pmem';\n\nimport Icon from './icon';\nimport Loader from './loader';\n\nconst { PHANPY_LINGVA_INSTANCES } = import.meta.env;\nconst LINGVA_INSTANCES = PHANPY_LINGVA_INSTANCES\n ? PHANPY_LINGVA_INSTANCES.split(/\\s+/)\n : [];\n\nconst throttle = pThrottle({\n limit: 1,\n interval: 2000,\n});\n\nlet currentLingvaInstance = 0;\n\nfunction _lingvaTranslate(text, source, target) {\n console.log('TRANSLATE', text, source, target);\n const fetchCall = () => {\n let instance = LINGVA_INSTANCES[currentLingvaInstance];\n return fetch(\n `https://${instance}/api/v1/${source}/${target}/${encodeURIComponent(\n text,\n )}`,\n )\n .then((res) => {\n if (!res.ok) throw new Error(res.statusText);\n return res.json();\n })\n .then((res) => {\n return {\n provider: 'lingva',\n content: res.translation,\n detectedSourceLanguage: res.info?.detectedSource,\n info: res.info,\n };\n });\n };\n return pRetry(fetchCall, {\n retries: 3,\n onFailedAttempt: (e) => {\n currentLingvaInstance =\n (currentLingvaInstance + 1) % LINGVA_INSTANCES.length;\n console.log(\n 'Retrying translation with another instance',\n currentLingvaInstance,\n );\n },\n });\n // return masto.v1.statuses.$select(id).translate({\n // lang: DEFAULT_LANG,\n // });\n}\nconst TRANSLATED_MAX_AGE = 1000 * 60 * 60; // 1 hour\nconst lingvaTranslate = pmem(_lingvaTranslate, {\n maxAge: TRANSLATED_MAX_AGE,\n});\nconst throttledLingvaTranslate = pmem(throttle(lingvaTranslate), {\n // I know, this is double-layered memoization\n maxAge: TRANSLATED_MAX_AGE,\n});\n\nfunction TranslationBlock({\n forceTranslate,\n sourceLanguage,\n onTranslate,\n text = '',\n mini,\n}) {\n const targetLang = getTranslateTargetLanguage(true);\n const [uiState, setUIState] = useState('default');\n const [pronunciationContent, setPronunciationContent] = useState(null);\n const [translatedContent, setTranslatedContent] = useState(null);\n const [detectedLang, setDetectedLang] = useState(null);\n const detailsRef = useRef();\n\n const sourceLangText = sourceLanguage\n ? localeCode2Text(sourceLanguage)\n : null;\n const targetLangText = localeCode2Text(targetLang);\n const apiSourceLang = useRef('auto');\n\n if (!onTranslate) {\n onTranslate = mini ? throttledLingvaTranslate : lingvaTranslate;\n }\n\n const translate = async () => {\n setUIState('loading');\n try {\n const { content, detectedSourceLanguage, provider, error, ...props } =\n await onTranslate(text, apiSourceLang.current, targetLang);\n if (content) {\n if (detectedSourceLanguage) {\n const detectedLangText = localeCode2Text(detectedSourceLanguage);\n setDetectedLang(detectedLangText);\n }\n if (provider === 'lingva') {\n const pronunciation = props?.info?.pronunciation?.query;\n if (pronunciation) {\n setPronunciationContent(pronunciation);\n }\n }\n setTranslatedContent(content);\n setUIState('default');\n if (!mini && content.trim() !== text.trim()) {\n detailsRef.current.open = true;\n detailsRef.current.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n });\n }\n } else {\n if (error) console.error(error);\n setUIState('error');\n }\n } catch (e) {\n console.error(e);\n setUIState('error');\n }\n };\n\n useEffect(() => {\n if (forceTranslate) {\n translate();\n }\n }, [forceTranslate]);\n\n if (mini) {\n if (\n !!translatedContent &&\n translatedContent.trim() !== text.trim() &&\n detectedLang !== targetLangText\n ) {\n return (\n <div class=\"shazam-container\">\n <div class=\"shazam-container-inner\">\n <div class=\"status-translation-block-mini\">\n <Icon\n icon=\"translate\"\n alt={`Auto-translated from ${sourceLangText}`}\n />\n <output\n lang={targetLang}\n dir=\"auto\"\n title={pronunciationContent || ''}\n >\n {translatedContent}\n </output>\n </div>\n </div>\n </div>\n );\n }\n return null;\n }\n\n return (\n <div\n class=\"status-translation-block\"\n onClick={(e) => {\n e.preventDefault();\n }}\n >\n <details ref={detailsRef}>\n <summary>\n <button\n type=\"button\"\n onClick={async (e) => {\n e.preventDefault();\n e.stopPropagation();\n detailsRef.current.open = !detailsRef.current.open;\n if (uiState === 'loading') return;\n if (!translatedContent) translate();\n }}\n >\n <Icon icon=\"translate\" />{' '}\n <span>\n {uiState === 'loading'\n ? 'Translating…'\n : sourceLanguage && sourceLangText && !detectedLang\n ? `Translate from ${sourceLangText}`\n : `Translate`}\n </span>\n </button>\n </summary>\n <div class=\"translated-block\">\n <div class=\"translation-info insignificant\">\n <select\n class=\"translated-source-select\"\n disabled={uiState === 'loading'}\n onChange={(e) => {\n apiSourceLang.current = e.target.value;\n translate();\n }}\n >\n {sourceLanguages.map((l) => (\n <option value={l.code}>\n {l.code === 'auto' ? `Auto (${detectedLang ?? '…'})` : l.name}\n </option>\n ))}\n </select>{' '}\n <span>→ {targetLangText}</span>\n <Loader abrupt hidden={uiState !== 'loading'} />\n </div>\n {uiState === 'error' ? (\n <p class=\"ui-state\">Failed to translate</p>\n ) : (\n !!translatedContent && (\n <>\n <output class=\"translated-content\" lang={targetLang} dir=\"auto\">\n {translatedContent}\n </output>\n {!!pronunciationContent && (\n <output\n class=\"translated-pronunciation-content\"\n tabIndex={-1}\n onClick={(e) => {\n e.target.classList.toggle('expand');\n }}\n >\n {pronunciationContent}\n </output>\n )}\n </>\n )\n )}\n </div>\n </details>\n </div>\n );\n}\n\nexport default LINGVA_INSTANCES?.length ? TranslationBlock : () => null;\n","import {\n clear,\n createStore,\n del,\n delMany,\n get,\n getMany,\n keys,\n set,\n} from 'idb-keyval';\n\nconst draftsStore = createStore('drafts-db', 'drafts-store');\n\n// Add additonal `draftsStore` parameter to all methods\n\nconst drafts = {\n set: (key, val) => set(key, val, draftsStore),\n get: (key) => get(key, draftsStore),\n getMany: (keys) => getMany(keys, draftsStore),\n del: (key) => del(key, draftsStore),\n delMany: (keys) => delMany(keys, draftsStore),\n clear: () => clear(draftsStore),\n keys: () => keys(draftsStore),\n};\n\nexport default {\n drafts,\n};\n","export default {\n public: 'earth',\n unlisted: 'group',\n private: 'lock',\n direct: 'message',\n};\n","import { useState } from 'preact/hooks';\n\nimport shortenNumber from '../utils/shorten-number';\n\nimport EmojiText from './emoji-text';\nimport Icon from './icon';\nimport RelativeTime from './relative-time';\n\nexport default function Poll({\n poll,\n lang,\n readOnly,\n refresh = () => {},\n votePoll = () => {},\n}) {\n const [uiState, setUIState] = useState('default');\n const {\n expired,\n expiresAt,\n id,\n multiple,\n options,\n ownVotes,\n voted,\n votersCount,\n votesCount,\n emojis,\n } = poll;\n const expiresAtDate = !!expiresAt && new Date(expiresAt); // Update poll at point of expiry\n // NOTE: Disable this because setTimeout runs immediately if delay is too large\n // https://stackoverflow.com/a/56718027/20838\n // useEffect(() => {\n // let timeout;\n // if (!expired && expiresAtDate) {\n // const ms = expiresAtDate.getTime() - Date.now() + 1; // +1 to give it a little buffer\n // if (ms > 0) {\n // timeout = setTimeout(() => {\n // setUIState('loading');\n // (async () => {\n // // await refresh();\n // setUIState('default');\n // })();\n // }, ms);\n // }\n // }\n // return () => {\n // clearTimeout(timeout);\n // };\n // }, [expired, expiresAtDate]);\n\n const pollVotesCount = votersCount || votesCount;\n let roundPrecision = 0;\n\n if (pollVotesCount <= 1000) {\n roundPrecision = 0;\n } else if (pollVotesCount <= 10000) {\n roundPrecision = 1;\n } else if (pollVotesCount <= 100000) {\n roundPrecision = 2;\n }\n\n const [showResults, setShowResults] = useState(false);\n const optionsHaveVoteCounts = options.every((o) => o.votesCount !== null);\n\n return (\n <div\n lang={lang}\n dir=\"auto\"\n class={`poll ${readOnly ? 'read-only' : ''} ${\n uiState === 'loading' ? 'loading' : ''\n }`}\n >\n {(showResults && optionsHaveVoteCounts) || voted || expired ? (\n <>\n <div class=\"poll-options\">\n {options.map((option, i) => {\n const { title, votesCount: optionVotesCount } = option;\n const percentage = pollVotesCount\n ? ((optionVotesCount / pollVotesCount) * 100).toFixed(\n roundPrecision,\n )\n : 0; // check if current poll choice is the leading one\n\n const isLeading =\n optionVotesCount > 0 &&\n optionVotesCount ===\n Math.max(...options.map((o) => o.votesCount));\n return (\n <div\n key={`${i}-${title}-${optionVotesCount}`}\n class={`poll-option poll-result ${\n isLeading ? 'poll-option-leading' : ''\n }`}\n style={{\n '--percentage': `${percentage}%`,\n }}\n >\n <div class=\"poll-option-title\">\n <span>\n <EmojiText text={title} emojis={emojis} />\n </span>\n {voted && ownVotes.includes(i) && (\n <>\n {' '}\n <Icon icon=\"check-circle\" />\n </>\n )}\n </div>\n <div\n class=\"poll-option-votes\"\n title={`${optionVotesCount} vote${\n optionVotesCount === 1 ? '' : 's'\n }`}\n >\n {percentage}%\n </div>\n </div>\n );\n })}\n </div>\n {!expired && !voted && (\n <button\n class=\"poll-vote-button plain2\"\n disabled={uiState === 'loading'}\n onClick={(e) => {\n e.preventDefault();\n setShowResults(false);\n }}\n >\n <Icon icon=\"arrow-left\" size=\"s\" /> Hide results\n </button>\n )}\n </>\n ) : (\n <form\n onSubmit={async (e) => {\n e.preventDefault();\n const form = e.target;\n const formData = new FormData(form);\n const choices = [];\n formData.forEach((value, key) => {\n if (key === 'poll') {\n choices.push(value);\n }\n });\n if (!choices.length) return;\n setUIState('loading');\n await votePoll(choices);\n setUIState('default');\n }}\n >\n <div class=\"poll-options\">\n {options.map((option, i) => {\n const { title } = option;\n return (\n <div class=\"poll-option\">\n <label class=\"poll-label\">\n <input\n type={multiple ? 'checkbox' : 'radio'}\n name=\"poll\"\n value={i}\n disabled={uiState === 'loading'}\n readOnly={readOnly}\n />\n <span class=\"poll-option-title\">\n <EmojiText text={title} emojis={emojis} />\n </span>\n </label>\n </div>\n );\n })}\n </div>\n {!readOnly && (\n <button\n class=\"poll-vote-button\"\n type=\"submit\"\n disabled={uiState === 'loading'}\n >\n Vote\n </button>\n )}\n </form>\n )}\n <p class=\"poll-meta\">\n {!expired && !readOnly && (\n <button\n type=\"button\"\n class=\"plain small\"\n disabled={uiState === 'loading'}\n style={{\n marginLeft: -8,\n }}\n onClick={(e) => {\n e.preventDefault();\n setUIState('loading');\n\n (async () => {\n await refresh();\n setUIState('default');\n })();\n }}\n title=\"Refresh\"\n >\n <Icon icon=\"refresh\" alt=\"Refresh\" />\n </button>\n )}\n {!voted && !expired && !readOnly && optionsHaveVoteCounts && (\n <button\n type=\"button\"\n class=\"plain small\"\n disabled={uiState === 'loading'}\n onClick={(e) => {\n e.preventDefault();\n setShowResults(!showResults);\n }}\n title={showResults ? 'Hide results' : 'Show results'}\n >\n <Icon\n icon={showResults ? 'eye-open' : 'eye-close'}\n alt={showResults ? 'Hide results' : 'Show results'}\n />{' '}\n </button>\n )}\n {!expired && !readOnly && ' '}\n <span title={votesCount}>{shortenNumber(votesCount)}</span> vote\n {votesCount === 1 ? '' : 's'}\n {!!votersCount && votersCount !== votesCount && (\n <>\n {' '}\n &bull; <span title={votersCount}>\n {shortenNumber(votersCount)}\n </span>{' '}\n voter\n {votersCount === 1 ? '' : 's'}\n </>\n )}{' '}\n &bull; {expired ? 'Ended' : 'Ending'}{' '}\n {!!expiresAtDate && <RelativeTime datetime={expiresAtDate} />}\n </p>{' '}\n </div>\n );\n}\n","import { createContext } from 'preact';\n\nconst FilterContext = createContext();\nexport default FilterContext;\n","import mem from './mem';\nimport store from './store';\n\nfunction _isFiltered(filtered, filterContext) {\n if (!filtered?.length) return false;\n const appliedFilters = filtered.filter((f) => {\n const { filter } = f;\n const hasContext = filter.context.includes(filterContext);\n if (!hasContext) return false;\n if (!filter.expiresAt) return hasContext;\n return new Date(filter.expiresAt) > new Date();\n });\n if (!appliedFilters.length) return false;\n const isHidden = appliedFilters.some((f) => f.filter.filterAction === 'hide');\n if (isHidden)\n return {\n action: 'hide',\n };\n const isWarn = appliedFilters.some((f) => f.filter.filterAction === 'warn');\n if (isWarn) {\n const filterTitles = appliedFilters.map((f) => f.filter.title);\n return {\n action: 'warn',\n titles: filterTitles,\n titlesStr: filterTitles.join(' • '),\n };\n }\n return false;\n}\nexport const isFiltered = mem(_isFiltered);\n\nexport function filteredItem(item, filterContext, currentAccountID) {\n const { filtered } = item;\n if (!filtered?.length) return true;\n const isSelf = currentAccountID && item.account?.id === currentAccountID;\n if (isSelf) return true;\n const filterState = isFiltered(filtered, filterContext);\n if (!filterState) return true;\n if (filterState.action === 'hide') return false;\n // item._filtered = filterState;\n return true;\n}\nexport function filteredItems(items, filterContext) {\n if (!items?.length) return [];\n if (!filterContext) return items;\n const currentAccountID = store.session.get('currentAccount');\n return items.filter((item) =>\n filteredItem(item, filterContext, currentAccountID),\n );\n}\n","const div = document.createElement('div');\nexport default function htmlContentLength(html) {\n if (!html) return 0;\n div.innerHTML = html;\n // .invisible spans for links\n // e.g. <span class=\"invisible\">https://</span>mastodon.social\n div.querySelectorAll('.invisible').forEach((el) => {\n el.remove();\n });\n return div.innerText.length;\n}\n","export const supportsTTS = 'speechSynthesis' in window;\n\nexport function speak(text, lang) {\n if (!supportsTTS) return;\n try {\n if (speechSynthesis.speaking) {\n speechSynthesis.cancel();\n }\n const utterance = new SpeechSynthesisUtterance(text);\n if (lang) utterance.lang = lang;\n speechSynthesis.speak(utterance);\n } catch (e) {\n alert(e);\n }\n}\n","import getHTMLText from './getHTMLText';\n\nfunction statusPeek(status) {\n const { spoilerText, content, poll, mediaAttachments } = status;\n let text = '';\n if (spoilerText?.trim()) {\n text += spoilerText;\n } else {\n text += getHTMLText(content);\n }\n text = text.trim();\n if (poll) {\n text += ' 📊';\n }\n if (mediaAttachments?.length) {\n text +=\n ' ' +\n mediaAttachments\n .map(\n (m) =>\n ({\n image: '🖼️',\n gifv: '🎞️',\n video: '📹',\n audio: '🎵',\n unknown: '',\n }[m.type] || ''),\n )\n .join('');\n }\n return text;\n}\n\nexport default statusPeek;\n","import { useRef } from 'preact/hooks';\nimport { useThrottledCallback } from 'use-debounce';\nimport useResizeObserver from 'use-resize-observer';\n\nexport default function useTruncated({ className = 'truncated' } = {}) {\n const ref = useRef();\n const onResize = useThrottledCallback(({ height }) => {\n if (ref.current) {\n const { scrollHeight } = ref.current;\n let truncated = scrollHeight > height;\n if (truncated) {\n const { height: _height, maxHeight } = getComputedStyle(ref.current);\n const computedHeight = parseInt(maxHeight || _height, 10);\n truncated = scrollHeight > computedHeight;\n }\n ref.current.classList.toggle(className, truncated);\n }\n }, 300);\n useResizeObserver({\n ref,\n box: 'border-box',\n onResize,\n });\n return ref;\n}\n","import { getBlurHashAverageColor } from 'fast-blurhash';\nimport { Fragment } from 'preact';\nimport {\n useCallback,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'preact/hooks';\nimport QuickPinchZoom, { make3dTransformValue } from 'react-quick-pinch-zoom';\n\nimport mem from '../utils/mem';\nimport states from '../utils/states';\n\nimport Icon from './icon';\nimport Link from './link';\nimport { formatDuration } from './status';\n\nconst isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // https://stackoverflow.com/a/23522755\n\n/*\nMedia type\n===\nunknown = unsupported or unrecognized file type\nimage = Static image\ngifv = Looping, soundless animation\nvideo = Video clip\naudio = Audio track\n*/\n\nconst dataAltLabel = 'ALT';\nconst AltBadge = (props) => {\n const { alt, lang, index, ...rest } = props;\n if (!alt || !alt.trim()) return null;\n return (\n <button\n type=\"button\"\n class=\"alt-badge clickable\"\n {...rest}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n states.showMediaAlt = {\n alt,\n lang,\n };\n }}\n title=\"Media description\"\n >\n {dataAltLabel}\n {!!index && <sup>{index}</sup>}\n </button>\n );\n};\n\nconst MEDIA_CAPTION_LIMIT = 140;\nexport const isMediaCaptionLong = mem((caption) =>\n caption?.length\n ? caption.length > MEDIA_CAPTION_LIMIT ||\n /[\\n\\r].*[\\n\\r]/.test(caption.trim())\n : false,\n);\n\nfunction Media({\n class: className = '',\n media,\n to,\n lang,\n showOriginal,\n autoAnimate,\n showCaption,\n altIndex,\n onClick = () => {},\n}) {\n const {\n blurhash,\n description,\n meta,\n previewRemoteUrl,\n previewUrl,\n remoteUrl,\n url,\n type,\n } = media;\n const { original = {}, small, focus } = meta || {};\n\n const width = showOriginal ? original?.width : small?.width;\n const height = showOriginal ? original?.height : small?.height;\n const mediaURL = showOriginal ? url : previewUrl || url;\n const remoteMediaURL = showOriginal\n ? remoteUrl\n : previewRemoteUrl || remoteUrl;\n const orientation = width >= height ? 'landscape' : 'portrait';\n\n const rgbAverageColor = blurhash ? getBlurHashAverageColor(blurhash) : null;\n\n const videoRef = useRef();\n\n let focalPosition;\n if (focus) {\n // Convert focal point to CSS background position\n // Formula from jquery-focuspoint\n // x = -1, y = 1 => 0% 0%\n // x = 0, y = 0 => 50% 50%\n // x = 1, y = -1 => 100% 100%\n const x = ((focus.x + 1) / 2) * 100;\n const y = ((1 - focus.y) / 2) * 100;\n focalPosition = `${x.toFixed(0)}% ${y.toFixed(0)}%`;\n }\n\n const mediaRef = useRef();\n const onUpdate = useCallback(({ x, y, scale }) => {\n const { current: media } = mediaRef;\n\n if (media) {\n const value = make3dTransformValue({ x, y, scale });\n\n if (scale === 1) {\n media.style.removeProperty('transform');\n } else {\n media.style.setProperty('transform', value);\n }\n\n media.closest('.media-zoom').style.touchAction =\n scale <= 1.01 ? 'pan-x' : '';\n }\n }, []);\n\n const [pinchZoomEnabled, setPinchZoomEnabled] = useState(false);\n const quickPinchZoomProps = {\n enabled: pinchZoomEnabled,\n draggableUnZoomed: false,\n inertiaFriction: 0.9,\n doubleTapZoomOutOnMaxScale: true,\n containerProps: {\n className: 'media-zoom',\n style: {\n overflow: 'visible',\n // width: 'inherit',\n // height: 'inherit',\n // justifyContent: 'inherit',\n // alignItems: 'inherit',\n // display: 'inherit',\n },\n },\n onUpdate,\n };\n\n const Parent = useMemo(\n () => (to ? (props) => <Link to={to} {...props} /> : 'div'),\n [to],\n );\n\n const isVideoMaybe =\n type === 'unknown' &&\n /\\.(mp4|m4a|m4p|m4b|m4r|m4v|mov|webm)$/i.test(remoteMediaURL);\n const isImage =\n type === 'image' || (type === 'unknown' && previewUrl && !isVideoMaybe);\n\n const parentRef = useRef();\n const [imageSmallerThanParent, setImageSmallerThanParent] = useState(false);\n useLayoutEffect(() => {\n if (!isImage) return;\n if (!showOriginal) return;\n if (!parentRef.current) return;\n const { offsetWidth, offsetHeight } = parentRef.current;\n const smaller = width < offsetWidth && height < offsetHeight;\n if (smaller) setImageSmallerThanParent(smaller);\n }, [width, height]);\n\n const maxAspectHeight =\n window.innerHeight * (orientation === 'portrait' ? 0.45 : 0.33);\n const maxHeight = orientation === 'portrait' ? 0 : 160;\n const averageColorStyle = {\n '--average-color': rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,\n };\n const mediaStyles =\n width && height\n ? {\n '--width': `${width}px`,\n '--height': `${height}px`,\n // Calculate '--aspectWidth' based on aspect ratio calculated from '--width' and '--height', max height has to be 160px\n '--aspectWidth': `${\n (width / height) * Math.max(maxHeight, maxAspectHeight)\n }px`,\n aspectRatio: `${width} / ${height}`,\n ...averageColorStyle,\n }\n : {\n ...averageColorStyle,\n };\n\n const longDesc = isMediaCaptionLong(description);\n const showInlineDesc =\n !!showCaption && !showOriginal && !!description && !longDesc;\n const Figure = !showInlineDesc\n ? Fragment\n : (props) => {\n const { children, ...restProps } = props;\n return (\n <figure {...restProps}>\n {children}\n <figcaption\n class=\"media-caption\"\n lang={lang}\n dir=\"auto\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n states.showMediaAlt = {\n alt: description,\n lang,\n };\n }}\n >\n {description}\n </figcaption>\n </figure>\n );\n };\n\n if (isImage) {\n // Note: type: unknown might not have width/height\n quickPinchZoomProps.containerProps.style.display = 'inherit';\n\n useLayoutEffect(() => {\n if (!isSafari) return;\n if (!showOriginal) return;\n (async () => {\n try {\n await fetch(mediaURL, { mode: 'no-cors' });\n mediaRef.current.src = mediaURL;\n } catch (e) {\n // Ignore\n }\n })();\n }, [mediaURL]);\n\n return (\n <Figure>\n <Parent\n ref={parentRef}\n class={`media media-image ${className}`}\n onClick={onClick}\n data-orientation={orientation}\n data-has-alt={!showInlineDesc}\n style={\n showOriginal\n ? {\n backgroundImage: `url(${previewUrl})`,\n backgroundSize: imageSmallerThanParent\n ? `${width}px ${height}px`\n : undefined,\n ...averageColorStyle,\n }\n : mediaStyles\n }\n >\n {showOriginal ? (\n <QuickPinchZoom {...quickPinchZoomProps}>\n <img\n ref={mediaRef}\n src={mediaURL}\n alt={description}\n width={width}\n height={height}\n data-orientation={orientation}\n loading=\"eager\"\n decoding=\"sync\"\n onLoad={(e) => {\n e.target.closest('.media-image').style.backgroundImage = '';\n e.target.closest('.media-zoom').style.display = '';\n setPinchZoomEnabled(true);\n }}\n onError={(e) => {\n const { src } = e.target;\n if (src === mediaURL && mediaURL !== remoteMediaURL) {\n e.target.src = remoteMediaURL;\n }\n }}\n />\n </QuickPinchZoom>\n ) : (\n <>\n <img\n src={mediaURL}\n alt={showInlineDesc ? '' : description}\n width={width}\n height={height}\n data-orientation={orientation}\n loading=\"lazy\"\n style={{\n // backgroundColor:\n // rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,\n // backgroundPosition: focalBackgroundPosition || 'center',\n // Duration based on width or height in pixels\n objectPosition: focalPosition || 'center',\n // 100px per second (rough estimate)\n // Clamp between 5s and 120s\n '--anim-duration': `${Math.min(\n Math.max(Math.max(width, height) / 100, 5),\n 120,\n )}s`,\n }}\n onLoad={(e) => {\n // e.target.closest('.media-image').style.backgroundImage = '';\n e.target.dataset.loaded = true;\n }}\n onError={(e) => {\n const { src } = e.target;\n if (src === mediaURL && mediaURL !== remoteMediaURL) {\n e.target.src = remoteMediaURL;\n }\n }}\n />\n {!showInlineDesc && (\n <AltBadge alt={description} lang={lang} index={altIndex} />\n )}\n </>\n )}\n </Parent>\n </Figure>\n );\n } else if (type === 'gifv' || type === 'video' || isVideoMaybe) {\n const shortDuration = original.duration < 31;\n const isGIF = type === 'gifv' && shortDuration;\n // If GIF is too long, treat it as a video\n const loopable = original.duration < 61;\n const formattedDuration = formatDuration(original.duration);\n const hoverAnimate = !showOriginal && !autoAnimate && isGIF;\n const autoGIFAnimate = !showOriginal && autoAnimate && isGIF;\n const showProgress = original.duration > 5;\n\n const videoHTML = `\n <video\n src=\"${url}\"\n poster=\"${previewUrl}\"\n width=\"${width}\"\n height=\"${height}\"\n data-orientation=\"${orientation}\"\n preload=\"auto\"\n autoplay\n muted=\"${isGIF}\"\n ${isGIF ? '' : 'controls'}\n playsinline\n loop=\"${loopable}\"\n ${isGIF ? 'ondblclick=\"this.paused ? this.play() : this.pause()\"' : ''}\n ${\n isGIF && showProgress\n ? \"ontimeupdate=\\\"this.closest('.media-gif') && this.closest('.media-gif').style.setProperty('--progress', `${~~((this.currentTime / this.duration) * 100)}%`)\\\"\"\n : ''\n }\n ></video>\n `;\n\n return (\n <Figure>\n <Parent\n class={`media ${className} media-${isGIF ? 'gif' : 'video'} ${\n autoGIFAnimate ? 'media-contain' : ''\n } ${hoverAnimate ? 'media-hover-animate' : ''}`}\n data-orientation={orientation}\n data-formatted-duration={\n !showOriginal ? formattedDuration : undefined\n }\n data-label={isGIF && !showOriginal && !autoGIFAnimate ? 'GIF' : ''}\n data-has-alt={!showInlineDesc}\n // style={{\n // backgroundColor:\n // rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,\n // }}\n style={!showOriginal && mediaStyles}\n onClick={(e) => {\n if (hoverAnimate) {\n try {\n videoRef.current.pause();\n } catch (e) {}\n }\n onClick(e);\n }}\n onMouseEnter={() => {\n if (hoverAnimate) {\n try {\n videoRef.current.play();\n } catch (e) {}\n }\n }}\n onMouseLeave={() => {\n if (hoverAnimate) {\n try {\n videoRef.current.pause();\n } catch (e) {}\n }\n }}\n onFocus={() => {\n if (hoverAnimate) {\n try {\n videoRef.current.play();\n } catch (e) {}\n }\n }}\n onBlur={() => {\n if (hoverAnimate) {\n try {\n videoRef.current.pause();\n } catch (e) {}\n }\n }}\n >\n {showOriginal || autoGIFAnimate ? (\n isGIF && showOriginal ? (\n <QuickPinchZoom {...quickPinchZoomProps} enabled>\n <div\n ref={mediaRef}\n dangerouslySetInnerHTML={{\n __html: videoHTML,\n }}\n />\n </QuickPinchZoom>\n ) : (\n <div\n class=\"video-container\"\n dangerouslySetInnerHTML={{\n __html: videoHTML,\n }}\n />\n )\n ) : isGIF ? (\n <video\n ref={videoRef}\n src={url}\n poster={previewUrl}\n width={width}\n height={height}\n data-orientation={orientation}\n preload=\"auto\"\n // controls\n playsinline\n loop\n muted\n onTimeUpdate={\n showProgress\n ? (e) => {\n const { target } = e;\n const container = target?.closest('.media-gif');\n if (container) {\n const percentage =\n (target.currentTime / target.duration) * 100;\n container.style.setProperty(\n '--progress',\n `${percentage}%`,\n );\n }\n }\n : undefined\n }\n />\n ) : (\n <>\n <img\n src={previewUrl}\n alt={showInlineDesc ? '' : description}\n width={width}\n height={height}\n data-orientation={orientation}\n loading=\"lazy\"\n />\n <div class=\"media-play\">\n <Icon icon=\"play\" size=\"xl\" />\n </div>\n </>\n )}\n {!showOriginal && !showInlineDesc && (\n <AltBadge alt={description} lang={lang} index={altIndex} />\n )}\n </Parent>\n </Figure>\n );\n } else if (type === 'audio') {\n const formattedDuration = formatDuration(original.duration);\n return (\n <Figure>\n <Parent\n class={`media media-audio ${className}`}\n data-formatted-duration={\n !showOriginal ? formattedDuration : undefined\n }\n data-has-alt={!showInlineDesc}\n onClick={onClick}\n style={!showOriginal && mediaStyles}\n >\n {showOriginal ? (\n <audio src={remoteUrl || url} preload=\"none\" controls autoplay />\n ) : previewUrl ? (\n <img\n src={previewUrl}\n alt={showInlineDesc ? '' : description}\n width={width}\n height={height}\n data-orientation={orientation}\n loading=\"lazy\"\n />\n ) : null}\n {!showOriginal && (\n <>\n <div class=\"media-play\">\n <Icon icon=\"play\" size=\"xl\" />\n </div>\n {!showInlineDesc && (\n <AltBadge alt={description} lang={lang} index={altIndex} />\n )}\n </>\n )}\n </Parent>\n </Figure>\n );\n }\n}\n\nexport default Media;\n","import { FocusableItem } from '@szhsin/react-menu';\n\nimport Link from './link';\n\nfunction MenuLink(props) {\n return (\n <FocusableItem>\n {({ ref, closeMenu }) => (\n <Link\n {...props}\n ref={ref}\n onClick={({ detail }) =>\n closeMenu(detail === 0 ? 'Enter' : undefined)\n }\n />\n )}\n </FocusableItem>\n );\n}\n\nexport default MenuLink;\n","import './status.css';\n\nimport '@justinribeiro/lite-youtube';\nimport {\n ControlledMenu,\n Menu,\n MenuDivider,\n MenuHeader,\n MenuItem,\n} from '@szhsin/react-menu';\nimport { decodeBlurHash, getBlurHashAverageColor } from 'fast-blurhash';\nimport { memo } from 'preact/compat';\nimport {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'preact/hooks';\nimport { useHotkeys } from 'react-hotkeys-hook';\nimport { useLongPress } from 'use-long-press';\nimport { useSnapshot } from 'valtio';\n\nimport AccountBlock from '../components/account-block';\nimport EmojiText from '../components/emoji-text';\nimport Loader from '../components/loader';\nimport Menu2 from '../components/menu2';\nimport MenuConfirm from '../components/menu-confirm';\nimport Modal from '../components/modal';\nimport NameText from '../components/name-text';\nimport Poll from '../components/poll';\nimport { api } from '../utils/api';\nimport emojifyText from '../utils/emojify-text';\nimport enhanceContent from '../utils/enhance-content';\nimport FilterContext from '../utils/filter-context';\nimport { isFiltered } from '../utils/filters';\nimport getTranslateTargetLanguage from '../utils/get-translate-target-language';\nimport getHTMLText from '../utils/getHTMLText';\nimport handleContentLinks from '../utils/handle-content-links';\nimport htmlContentLength from '../utils/html-content-length';\nimport isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe';\nimport localeMatch from '../utils/locale-match';\nimport niceDateTime from '../utils/nice-date-time';\nimport openCompose from '../utils/open-compose';\nimport pmem from '../utils/pmem';\nimport safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';\nimport shortenNumber from '../utils/shorten-number';\nimport showToast from '../utils/show-toast';\nimport { speak, supportsTTS } from '../utils/speech';\nimport states, { getStatus, saveStatus, statusKey } from '../utils/states';\nimport statusPeek from '../utils/status-peek';\nimport store from '../utils/store';\nimport unfurlMastodonLink from '../utils/unfurl-link';\nimport useTruncated from '../utils/useTruncated';\nimport visibilityIconsMap from '../utils/visibility-icons-map';\n\nimport Avatar from './avatar';\nimport Icon from './icon';\nimport Link from './link';\nimport Media from './media';\nimport { isMediaCaptionLong } from './media';\nimport MenuLink from './menu-link';\nimport RelativeTime from './relative-time';\nimport TranslationBlock from './translation-block';\n\nconst SHOW_COMMENT_COUNT_LIMIT = 280;\nconst INLINE_TRANSLATE_LIMIT = 140;\n\nfunction fetchAccount(id, masto) {\n return masto.v1.accounts.$select(id).fetch();\n}\nconst memFetchAccount = pmem(fetchAccount);\n\nconst visibilityText = {\n public: 'Public',\n unlisted: 'Unlisted',\n private: 'Followers only',\n direct: 'Private mention',\n};\n\nconst isIOS =\n window.ontouchstart !== undefined &&\n /iPad|iPhone|iPod/.test(navigator.userAgent);\n\nconst REACTIONS_LIMIT = 80;\n\nfunction getPollText(poll) {\n if (!poll?.options?.length) return '';\n return `📊:\\n${poll.options\n .map(\n (option) =>\n `- ${option.title}${\n option.votesCount >= 0 ? ` (${option.votesCount})` : ''\n }`,\n )\n .join('\\n')}`;\n}\nfunction getPostText(status) {\n const { spoilerText, content, poll } = status;\n return (\n (spoilerText ? `${spoilerText}\\n\\n` : '') +\n getHTMLText(content) +\n getPollText(poll)\n );\n}\n\nfunction Status({\n statusID,\n status,\n instance: propInstance,\n size = 'm',\n contentTextWeight,\n readOnly,\n enableCommentHint,\n withinContext,\n skeleton,\n enableTranslate,\n forceTranslate: _forceTranslate,\n previewMode,\n // allowFilters,\n onMediaClick,\n quoted,\n onStatusLinkClick = () => {},\n showFollowedTags,\n}) {\n if (skeleton) {\n return (\n <div class=\"status skeleton\">\n <Avatar size=\"xxl\" />\n <div class=\"container\">\n <div class=\"meta\">███ ████████</div>\n <div class=\"content-container\">\n <div class=\"content\">\n <p>████ ████████</p>\n </div>\n </div>\n </div>\n </div>\n );\n }\n const { masto, instance, authenticated } = api({ instance: propInstance });\n const { instance: currentInstance } = api();\n const sameInstance = instance === currentInstance;\n\n let sKey = statusKey(statusID || status?.id, instance);\n const snapStates = useSnapshot(states);\n if (!status) {\n status = snapStates.statuses[sKey] || snapStates.statuses[statusID];\n sKey = statusKey(status?.id, instance);\n }\n if (!status) {\n return null;\n }\n\n const {\n account: {\n acct,\n avatar,\n avatarStatic,\n id: accountId,\n url: accountURL,\n displayName,\n username,\n emojis: accountEmojis,\n bot,\n group,\n },\n id,\n repliesCount,\n reblogged,\n reblogsCount,\n favourited,\n favouritesCount,\n bookmarked,\n poll,\n muted,\n sensitive,\n spoilerText,\n visibility, // public, unlisted, private, direct\n language,\n editedAt,\n filtered,\n card,\n createdAt,\n inReplyToId,\n inReplyToAccountId,\n content,\n mentions,\n mediaAttachments,\n reblog,\n uri,\n url,\n emojis,\n tags,\n // Non-API props\n _deleted,\n _pinned,\n // _filtered,\n } = status;\n\n const currentAccount = useMemo(() => {\n return store.session.get('currentAccount');\n }, []);\n const isSelf = useMemo(() => {\n return currentAccount && currentAccount === accountId;\n }, [accountId, currentAccount]);\n\n const filterContext = useContext(FilterContext);\n const filterInfo =\n !isSelf && !readOnly && !previewMode && isFiltered(filtered, filterContext);\n\n if (filterInfo?.action === 'hide') {\n return null;\n }\n\n console.debug('RENDER Status', id, status?.account.displayName, quoted);\n\n const debugHover = (e) => {\n if (e.shiftKey) {\n console.log({\n ...status,\n });\n }\n };\n\n if (/*allowFilters && */ size !== 'l' && filterInfo) {\n return (\n <FilteredStatus\n status={status}\n filterInfo={filterInfo}\n instance={instance}\n containerProps={{\n onMouseEnter: debugHover,\n }}\n showFollowedTags\n />\n );\n }\n\n const createdAtDate = new Date(createdAt);\n const editedAtDate = new Date(editedAt);\n\n let inReplyToAccountRef = mentions?.find(\n (mention) => mention.id === inReplyToAccountId,\n );\n if (!inReplyToAccountRef && inReplyToAccountId === id) {\n inReplyToAccountRef = { url: accountURL, username, displayName };\n }\n const [inReplyToAccount, setInReplyToAccount] = useState(inReplyToAccountRef);\n if (!withinContext && !inReplyToAccount && inReplyToAccountId) {\n const account = states.accounts[inReplyToAccountId];\n if (account) {\n setInReplyToAccount(account);\n } else {\n memFetchAccount(inReplyToAccountId, masto)\n .then((account) => {\n setInReplyToAccount(account);\n states.accounts[account.id] = account;\n })\n .catch((e) => {});\n }\n }\n const mentionSelf =\n inReplyToAccountId === currentAccount ||\n mentions?.find((mention) => mention.id === currentAccount);\n\n const readingExpandSpoilers = useMemo(() => {\n const prefs = store.account.get('preferences') || {};\n return !!prefs['reading:expand:spoilers'];\n }, []);\n const readingExpandMedia = useMemo(() => {\n // default | show_all | hide_all\n // Ignore hide_all because it means hide *ALL* media including non-sensitive ones\n const prefs = store.account.get('preferences') || {};\n return prefs['reading:expand:media'] || 'default';\n }, []);\n // FOR TESTING:\n // const readingExpandSpoilers = true;\n // const readingExpandMedia = 'show_all';\n const showSpoiler =\n previewMode || readingExpandSpoilers || !!snapStates.spoilers[id];\n const showSpoilerMedia =\n previewMode ||\n readingExpandMedia === 'show_all' ||\n !!snapStates.spoilersMedia[id];\n\n if (reblog) {\n // If has statusID, means useItemID (cached in states)\n\n if (group) {\n return (\n <div\n data-state-post-id={sKey}\n class=\"status-group\"\n onMouseEnter={debugHover}\n >\n <div class=\"status-pre-meta\">\n <Icon icon=\"group\" size=\"l\" alt=\"Group\" />{' '}\n <NameText account={status.account} instance={instance} showAvatar />\n </div>\n <Status\n status={statusID ? null : reblog}\n statusID={statusID ? reblog.id : null}\n instance={instance}\n size={size}\n contentTextWeight={contentTextWeight}\n readOnly={readOnly}\n />\n </div>\n );\n }\n\n return (\n <div\n data-state-post-id={sKey}\n class=\"status-reblog\"\n onMouseEnter={debugHover}\n >\n <div class=\"status-pre-meta\">\n <Icon icon=\"rocket\" size=\"l\" />{' '}\n <NameText account={status.account} instance={instance} showAvatar />{' '}\n <span>boosted</span>\n </div>\n <Status\n status={statusID ? null : reblog}\n statusID={statusID ? reblog.id : null}\n instance={instance}\n size={size}\n contentTextWeight={contentTextWeight}\n readOnly={readOnly}\n enableCommentHint\n />\n </div>\n );\n }\n\n // Check followedTags\n if (showFollowedTags && !!snapStates.statusFollowedTags[sKey]?.length) {\n return (\n <div\n data-state-post-id={sKey}\n class=\"status-followed-tags\"\n onMouseEnter={debugHover}\n >\n <div class=\"status-pre-meta\">\n <Icon icon=\"hashtag\" size=\"l\" />{' '}\n {snapStates.statusFollowedTags[sKey].slice(0, 3).map((tag) => (\n <Link\n key={tag}\n to={instance ? `/${instance}/t/${tag}` : `/t/${tag}`}\n class=\"status-followed-tag-item\"\n >\n {tag}\n </Link>\n ))}\n </div>\n <Status\n status={statusID ? null : status}\n statusID={statusID ? status.id : null}\n instance={instance}\n size={size}\n contentTextWeight={contentTextWeight}\n readOnly={readOnly}\n enableCommentHint\n />\n </div>\n );\n }\n\n const isSizeLarge = size === 'l';\n\n const [forceTranslate, setForceTranslate] = useState(_forceTranslate);\n const targetLanguage = getTranslateTargetLanguage(true);\n const contentTranslationHideLanguages =\n snapStates.settings.contentTranslationHideLanguages || [];\n const { contentTranslation, contentTranslationAutoInline } =\n snapStates.settings;\n if (!contentTranslation) enableTranslate = false;\n const inlineTranslate = useMemo(() => {\n if (\n !contentTranslation ||\n !contentTranslationAutoInline ||\n readOnly ||\n (withinContext && !isSizeLarge) ||\n previewMode ||\n spoilerText ||\n sensitive ||\n poll ||\n card ||\n mediaAttachments?.length\n ) {\n return false;\n }\n const contentLength = htmlContentLength(content);\n return contentLength > 0 && contentLength <= INLINE_TRANSLATE_LIMIT;\n }, [\n contentTranslation,\n contentTranslationAutoInline,\n readOnly,\n withinContext,\n isSizeLarge,\n previewMode,\n spoilerText,\n sensitive,\n poll,\n card,\n mediaAttachments,\n content,\n ]);\n\n const [showEdited, setShowEdited] = useState(false);\n\n const spoilerContentRef = useTruncated();\n const contentRef = useTruncated();\n const mediaContainerRef = useTruncated();\n const readMoreText = 'Read more →';\n\n const statusRef = useRef(null);\n\n const unauthInteractionErrorMessage = `Sorry, your current logged-in instance can't interact with this post from another instance.`;\n\n const textWeight = useCallback(\n () =>\n Math.max(\n Math.round((spoilerText.length + htmlContentLength(content)) / 140) ||\n 1,\n 1,\n ),\n [spoilerText, content],\n );\n\n const createdDateText = niceDateTime(createdAtDate);\n const editedDateText = editedAt && niceDateTime(editedAtDate);\n\n // Can boost if:\n // - authenticated AND\n // - visibility != direct OR\n // - visibility = private AND isSelf\n let canBoost =\n authenticated && visibility !== 'direct' && visibility !== 'private';\n if (visibility === 'private' && isSelf) {\n canBoost = true;\n }\n\n const replyStatus = (e) => {\n if (!sameInstance || !authenticated) {\n return alert(unauthInteractionErrorMessage);\n }\n // syntheticEvent comes from MenuItem\n if (e?.shiftKey || e?.syntheticEvent?.shiftKey) {\n const newWin = openCompose({\n replyToStatus: status,\n });\n if (newWin) return;\n }\n states.showCompose = {\n replyToStatus: status,\n };\n };\n\n // Check if media has no descriptions\n const mediaNoDesc = useMemo(() => {\n return mediaAttachments.some(\n (attachment) => !attachment.description?.trim?.(),\n );\n }, [mediaAttachments]);\n const boostStatus = async () => {\n if (!sameInstance || !authenticated) {\n alert(unauthInteractionErrorMessage);\n return false;\n }\n try {\n if (!reblogged) {\n let confirmText = 'Boost this post?';\n if (mediaNoDesc) {\n confirmText += '\\n\\n⚠ Some media have no descriptions.';\n }\n const yes = confirm(confirmText);\n if (!yes) {\n return false;\n }\n }\n // Optimistic\n states.statuses[sKey] = {\n ...status,\n reblogged: !reblogged,\n reblogsCount: reblogsCount + (reblogged ? -1 : 1),\n };\n if (reblogged) {\n const newStatus = await masto.v1.statuses.$select(id).unreblog();\n saveStatus(newStatus, instance);\n return true;\n } else {\n const newStatus = await masto.v1.statuses.$select(id).reblog();\n saveStatus(newStatus, instance);\n return true;\n }\n } catch (e) {\n console.error(e);\n // Revert optimistism\n states.statuses[sKey] = status;\n return false;\n }\n };\n const confirmBoostStatus = async () => {\n if (!sameInstance || !authenticated) {\n alert(unauthInteractionErrorMessage);\n return false;\n }\n try {\n // Optimistic\n states.statuses[sKey] = {\n ...status,\n reblogged: !reblogged,\n reblogsCount: reblogsCount + (reblogged ? -1 : 1),\n };\n if (reblogged) {\n const newStatus = await masto.v1.statuses.$select(id).unreblog();\n saveStatus(newStatus, instance);\n return true;\n } else {\n const newStatus = await masto.v1.statuses.$select(id).reblog();\n saveStatus(newStatus, instance);\n return true;\n }\n } catch (e) {\n console.error(e);\n // Revert optimistism\n states.statuses[sKey] = status;\n return false;\n }\n };\n\n const favouriteStatus = async () => {\n if (!sameInstance || !authenticated) {\n return alert(unauthInteractionErrorMessage);\n }\n try {\n // Optimistic\n states.statuses[sKey] = {\n ...status,\n favourited: !favourited,\n favouritesCount: favouritesCount + (favourited ? -1 : 1),\n };\n if (favourited) {\n const newStatus = await masto.v1.statuses.$select(id).unfavourite();\n saveStatus(newStatus, instance);\n } else {\n const newStatus = await masto.v1.statuses.$select(id).favourite();\n saveStatus(newStatus, instance);\n }\n } catch (e) {\n console.error(e);\n // Revert optimistism\n states.statuses[sKey] = status;\n }\n };\n\n const bookmarkStatus = async () => {\n if (!sameInstance || !authenticated) {\n return alert(unauthInteractionErrorMessage);\n }\n try {\n // Optimistic\n states.statuses[sKey] = {\n ...status,\n bookmarked: !bookmarked,\n };\n if (bookmarked) {\n const newStatus = await masto.v1.statuses.$select(id).unbookmark();\n saveStatus(newStatus, instance);\n } else {\n const newStatus = await masto.v1.statuses.$select(id).bookmark();\n saveStatus(newStatus, instance);\n }\n } catch (e) {\n console.error(e);\n // Revert optimistism\n states.statuses[sKey] = status;\n }\n };\n\n const differentLanguage =\n !!language &&\n language !== targetLanguage &&\n !localeMatch([language], [targetLanguage]) &&\n !contentTranslationHideLanguages.find(\n (l) => language === l || localeMatch([language], [l]),\n );\n\n const reblogIterator = useRef();\n const favouriteIterator = useRef();\n async function fetchBoostedLikedByAccounts(firstLoad) {\n if (firstLoad) {\n reblogIterator.current = masto.v1.statuses\n .$select(statusID)\n .rebloggedBy.list({\n limit: REACTIONS_LIMIT,\n });\n favouriteIterator.current = masto.v1.statuses\n .$select(statusID)\n .favouritedBy.list({\n limit: REACTIONS_LIMIT,\n });\n }\n const [{ value: reblogResults }, { value: favouriteResults }] =\n await Promise.allSettled([\n reblogIterator.current.next(),\n favouriteIterator.current.next(),\n ]);\n if (reblogResults.value?.length || favouriteResults.value?.length) {\n const accounts = [];\n if (reblogResults.value?.length) {\n accounts.push(\n ...reblogResults.value.map((a) => {\n a._types = ['reblog'];\n return a;\n }),\n );\n }\n if (favouriteResults.value?.length) {\n accounts.push(\n ...favouriteResults.value.map((a) => {\n a._types = ['favourite'];\n return a;\n }),\n );\n }\n return {\n value: accounts,\n done: reblogResults.done && favouriteResults.done,\n };\n }\n return {\n value: [],\n done: true,\n };\n }\n\n const menuInstanceRef = useRef();\n const StatusMenuItems = (\n <>\n {!isSizeLarge && (\n <>\n <MenuHeader>\n <span class=\"ib\">\n <Icon icon={visibilityIconsMap[visibility]} size=\"s\" />{' '}\n <span>{visibilityText[visibility]}</span>\n </span>{' '}\n <span class=\"ib\">\n {repliesCount > 0 && (\n <span>\n <Icon icon=\"comment2\" alt=\"Replies\" size=\"s\" />{' '}\n <span>{shortenNumber(repliesCount)}</span>\n </span>\n )}{' '}\n {reblogsCount > 0 && (\n <span>\n <Icon icon=\"rocket\" alt=\"Boosts\" size=\"s\" />{' '}\n <span>{shortenNumber(reblogsCount)}</span>\n </span>\n )}{' '}\n {favouritesCount > 0 && (\n <span>\n <Icon icon=\"heart\" alt=\"Likes\" size=\"s\" />{' '}\n <span>{shortenNumber(favouritesCount)}</span>\n </span>\n )}\n </span>\n <br />\n {createdDateText}\n </MenuHeader>\n <MenuLink\n to={instance ? `/${instance}/s/${id}` : `/s/${id}`}\n onClick={(e) => {\n onStatusLinkClick(e, status);\n }}\n >\n <Icon icon=\"arrow-right\" />\n <span>View post by @{username || acct}</span>\n </MenuLink>\n </>\n )}\n {!!editedAt && (\n <MenuItem\n onClick={() => {\n setShowEdited(id);\n }}\n >\n <Icon icon=\"history\" />\n <span>\n Show Edit History\n <br />\n <small class=\"more-insignificant\">Edited: {editedDateText}</small>\n </span>\n </MenuItem>\n )}\n {(!isSizeLarge || !!editedAt) && <MenuDivider />}\n {isSizeLarge && (\n <MenuItem\n onClick={() => {\n states.showGenericAccounts = {\n heading: 'Boosted/Liked by…',\n fetchAccounts: fetchBoostedLikedByAccounts,\n instance,\n showReactions: true,\n };\n }}\n >\n <Icon icon=\"react\" />\n <span>\n Boosted/Liked by<span class=\"more-insignificant\">…</span>\n </span>\n </MenuItem>\n )}\n {!isSizeLarge && sameInstance && (\n <>\n <div class=\"menu-horizontal\">\n <MenuConfirm\n subMenu\n confirmLabel={\n <>\n <Icon icon=\"rocket\" />\n <span>{reblogged ? 'Unboost?' : 'Boost to everyone?'}</span>\n </>\n }\n menuFooter={\n mediaNoDesc &&\n !reblogged && (\n <div class=\"footer\">\n <Icon icon=\"alert\" />\n Some media have no descriptions.\n </div>\n )\n }\n disabled={!canBoost}\n onClick={async () => {\n try {\n const done = await confirmBoostStatus();\n if (!isSizeLarge && done) {\n showToast(\n reblogged\n ? `Unboosted @${username || acct}'s post`\n : `Boosted @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n }}\n >\n <Icon\n icon=\"rocket\"\n style={{\n color: reblogged && 'var(--reblog-color)',\n }}\n />\n <span>{reblogged ? 'Unboost' : 'Boost…'}</span>\n </MenuConfirm>\n <MenuItem\n onClick={() => {\n try {\n favouriteStatus();\n if (!isSizeLarge) {\n showToast(\n favourited\n ? `Unliked @${username || acct}'s post`\n : `Liked @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n }}\n >\n <Icon\n icon=\"heart\"\n style={{\n color: favourited && 'var(--favourite-color)',\n }}\n />\n <span>{favourited ? 'Unlike' : 'Like'}</span>\n </MenuItem>\n </div>\n <div class=\"menu-horizontal\">\n <MenuItem onClick={replyStatus}>\n <Icon icon=\"reply\" />\n <span>Reply</span>\n </MenuItem>\n <MenuItem\n onClick={() => {\n try {\n bookmarkStatus();\n if (!isSizeLarge) {\n showToast(\n bookmarked\n ? `Unbookmarked @${username || acct}'s post`\n : `Bookmarked @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n }}\n >\n <Icon\n icon=\"bookmark\"\n style={{\n color: bookmarked && 'var(--link-color)',\n }}\n />\n <span>{bookmarked ? 'Unbookmark' : 'Bookmark'}</span>\n </MenuItem>\n </div>\n </>\n )}\n {enableTranslate ? (\n <div class={supportsTTS ? 'menu-horizontal' : ''}>\n <MenuItem\n disabled={forceTranslate}\n onClick={() => {\n setForceTranslate(true);\n }}\n >\n <Icon icon=\"translate\" />\n <span>Translate</span>\n </MenuItem>\n {supportsTTS && (\n <MenuItem\n onClick={() => {\n const postText = getPostText(status);\n if (postText) {\n speak(postText, language);\n }\n }}\n >\n <Icon icon=\"speak\" />\n <span>Speak</span>\n </MenuItem>\n )}\n </div>\n ) : (\n (!language || differentLanguage) && (\n <div class={supportsTTS ? 'menu-horizontal' : ''}>\n <MenuLink\n to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}\n >\n <Icon icon=\"translate\" />\n <span>Translate</span>\n </MenuLink>\n {supportsTTS && (\n <MenuItem\n onClick={() => {\n const postText = getPostText(status);\n if (postText) {\n speak(postText, language);\n }\n }}\n >\n <Icon icon=\"speak\" />\n <span>Speak</span>\n </MenuItem>\n )}\n </div>\n )\n )}\n {((!isSizeLarge && sameInstance) || enableTranslate) && <MenuDivider />}\n <MenuItem href={url} target=\"_blank\">\n <Icon icon=\"external\" />\n <small class=\"menu-double-lines\">{nicePostURL(url)}</small>\n </MenuItem>\n <div class=\"menu-horizontal\">\n <MenuItem\n onClick={() => {\n // Copy url to clipboard\n try {\n navigator.clipboard.writeText(url);\n showToast('Link copied');\n } catch (e) {\n console.error(e);\n showToast('Unable to copy link');\n }\n }}\n >\n <Icon icon=\"link\" />\n <span>Copy</span>\n </MenuItem>\n {navigator?.share &&\n navigator?.canShare?.({\n url,\n }) && (\n <MenuItem\n onClick={() => {\n try {\n navigator.share({\n url,\n });\n } catch (e) {\n console.error(e);\n alert(\"Sharing doesn't seem to work.\");\n }\n }}\n >\n <Icon icon=\"share\" />\n <span>Share…</span>\n </MenuItem>\n )}\n </div>\n {(isSelf || mentionSelf) && <MenuDivider />}\n {(isSelf || mentionSelf) && (\n <MenuItem\n onClick={async () => {\n try {\n const newStatus = await masto.v1.statuses\n .$select(id)\n [muted ? 'unmute' : 'mute']();\n saveStatus(newStatus, instance);\n showToast(muted ? 'Conversation unmuted' : 'Conversation muted');\n } catch (e) {\n console.error(e);\n showToast(\n muted\n ? 'Unable to unmute conversation'\n : 'Unable to mute conversation',\n );\n }\n }}\n >\n {muted ? (\n <>\n <Icon icon=\"unmute\" />\n <span>Unmute conversation</span>\n </>\n ) : (\n <>\n <Icon icon=\"mute\" />\n <span>Mute conversation</span>\n </>\n )}\n </MenuItem>\n )}\n {isSelf && (\n <div class=\"menu-horizontal\">\n <MenuItem\n onClick={() => {\n states.showCompose = {\n editStatus: status,\n };\n }}\n >\n <Icon icon=\"pencil\" />\n <span>Edit</span>\n </MenuItem>\n {isSizeLarge && (\n <MenuConfirm\n subMenu\n confirmLabel={\n <>\n <Icon icon=\"trash\" />\n <span>Delete this post?</span>\n </>\n }\n menuItemClassName=\"danger\"\n onClick={() => {\n // const yes = confirm('Delete this post?');\n // if (yes) {\n (async () => {\n try {\n await masto.v1.statuses.$select(id).remove();\n const cachedStatus = getStatus(id, instance);\n cachedStatus._deleted = true;\n showToast('Deleted');\n } catch (e) {\n console.error(e);\n showToast('Unable to delete');\n }\n })();\n // }\n }}\n >\n <Icon icon=\"trash\" />\n <span>Delete…</span>\n </MenuConfirm>\n )}\n </div>\n )}\n </>\n );\n\n const contextMenuRef = useRef();\n const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);\n const [contextMenuProps, setContextMenuProps] = useState({});\n\n const showContextMenu = !isSizeLarge && !previewMode && !_deleted && !quoted;\n\n // Only iOS/iPadOS browsers don't support contextmenu\n // Some comments report iPadOS might support contextmenu if a mouse is connected\n const bindLongPressContext = useLongPress(\n isIOS && showContextMenu\n ? (e) => {\n if (e.pointerType === 'mouse') return;\n // There's 'pen' too, but not sure if contextmenu event would trigger from a pen\n\n const { clientX, clientY } = e.touches?.[0] || e;\n // link detection copied from onContextMenu because here it works\n const link = e.target.closest('a');\n if (link && /^https?:\\/\\//.test(link.getAttribute('href'))) return;\n e.preventDefault();\n setContextMenuProps({\n anchorPoint: {\n x: clientX,\n y: clientY,\n },\n direction: 'right',\n });\n setIsContextMenuOpen(true);\n }\n : null,\n {\n threshold: 600,\n captureEvent: true,\n detect: 'touch',\n cancelOnMovement: 2, // true allows movement of up to 25 pixels\n },\n );\n\n const hotkeysEnabled = !readOnly && !previewMode && !quoted;\n const rRef = useHotkeys('r, shift+r', replyStatus, {\n enabled: hotkeysEnabled,\n });\n const fRef = useHotkeys(\n 'f, l',\n () => {\n try {\n favouriteStatus();\n if (!isSizeLarge) {\n showToast(\n favourited\n ? `Unliked @${username || acct}'s post`\n : `Liked @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n },\n {\n enabled: hotkeysEnabled,\n },\n );\n const dRef = useHotkeys(\n 'd',\n () => {\n try {\n bookmarkStatus();\n if (!isSizeLarge) {\n showToast(\n bookmarked\n ? `Unbookmarked @${username || acct}'s post`\n : `Bookmarked @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n },\n {\n enabled: hotkeysEnabled,\n },\n );\n const bRef = useHotkeys(\n 'shift+b',\n () => {\n (async () => {\n try {\n const done = await confirmBoostStatus();\n if (!isSizeLarge && done) {\n showToast(\n reblogged\n ? `Unboosted @${username || acct}'s post`\n : `Boosted @${username || acct}'s post`,\n );\n }\n } catch (e) {}\n })();\n },\n {\n enabled: hotkeysEnabled && canBoost,\n },\n );\n const xRef = useHotkeys('x', (e) => {\n const activeStatus = document.activeElement.closest(\n '.status-link, .status-focus',\n );\n if (activeStatus) {\n const spoilerButton = activeStatus.querySelector(\n '.spoiler-button:not(.spoiling)',\n );\n if (spoilerButton) {\n e.stopPropagation();\n spoilerButton.click();\n } else {\n const spoilerMediaButton = activeStatus.querySelector(\n '.spoiler-media-button:not(.spoiling)',\n );\n if (spoilerMediaButton) {\n e.stopPropagation();\n spoilerMediaButton.click();\n }\n }\n }\n });\n\n const displayedMediaAttachments = mediaAttachments.slice(\n 0,\n isSizeLarge ? undefined : 4,\n );\n const showMultipleMediaCaptions =\n mediaAttachments.length > 1 &&\n displayedMediaAttachments.some(\n (media) => !!media.description && !isMediaCaptionLong(media.description),\n );\n const captionChildren = useMemo(() => {\n if (!showMultipleMediaCaptions) return null;\n const attachments = [];\n displayedMediaAttachments.forEach((media, i) => {\n if (!media.description) return;\n const index = attachments.findIndex(\n (attachment) => attachment.media.description === media.description,\n );\n if (index === -1) {\n attachments.push({\n media,\n indices: [i],\n });\n } else {\n attachments[index].indices.push(i);\n }\n });\n return attachments.map(({ media, indices }) => (\n <div\n key={media.id}\n data-caption-index={indices.map((i) => i + 1).join(' ')}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n states.showMediaAlt = {\n alt: media.description,\n lang: language,\n };\n }}\n title={media.description}\n >\n <sup>{indices.map((i) => i + 1).join(' ')}</sup> {media.description}\n </div>\n ));\n\n // return displayedMediaAttachments.map(\n // (media, i) =>\n // !!media.description && (\n // <div\n // key={media.id}\n // data-caption-index={i + 1}\n // onClick={(e) => {\n // e.preventDefault();\n // e.stopPropagation();\n // states.showMediaAlt = {\n // alt: media.description,\n // lang: language,\n // };\n // }}\n // title={media.description}\n // >\n // <sup>{i + 1}</sup> {media.description}\n // </div>\n // ),\n // );\n }, [showMultipleMediaCaptions, displayedMediaAttachments, language]);\n\n const isThread = useMemo(() => {\n return (\n (!!inReplyToId && inReplyToAccountId === status.account?.id) ||\n !!snapStates.statusThreadNumber[sKey]\n );\n }, [\n inReplyToId,\n inReplyToAccountId,\n status.account?.id,\n snapStates.statusThreadNumber[sKey],\n ]);\n\n const showCommentHint = useMemo(() => {\n return (\n enableCommentHint &&\n !isThread &&\n !withinContext &&\n !inReplyToId &&\n visibility === 'public' &&\n repliesCount > 0\n );\n }, [\n enableCommentHint,\n isThread,\n withinContext,\n inReplyToId,\n repliesCount,\n visibility,\n ]);\n const showCommentCount = useMemo(() => {\n if (\n card ||\n poll ||\n sensitive ||\n spoilerText ||\n mediaAttachments?.length ||\n isThread ||\n withinContext ||\n inReplyToId ||\n repliesCount <= 0\n ) {\n return false;\n }\n const questionRegex = /[???︖❓❔⁇⁈⁉¿‽؟]/;\n const containsQuestion = questionRegex.test(content);\n if (!containsQuestion) return false;\n const contentLength = htmlContentLength(content);\n if (contentLength > 0 && contentLength <= SHOW_COMMENT_COUNT_LIMIT) {\n return true;\n }\n }, [\n card,\n poll,\n sensitive,\n spoilerText,\n mediaAttachments,\n reblog,\n isThread,\n withinContext,\n inReplyToId,\n repliesCount,\n content,\n ]);\n\n return (\n <article\n data-state-post-id={sKey}\n ref={(node) => {\n statusRef.current = node;\n // Use parent node if it's in focus\n // Use case: <a><status /></a>\n // When navigating (j/k), the <a> is focused instead of <status />\n // Hotkey binding doesn't bubble up thus this hack\n const nodeRef =\n node?.closest?.(\n '.timeline-item, .timeline-item-alt, .status-link, .status-focus',\n ) || node;\n rRef.current = nodeRef;\n fRef.current = nodeRef;\n dRef.current = nodeRef;\n bRef.current = nodeRef;\n xRef.current = nodeRef;\n }}\n tabindex=\"-1\"\n class={`status ${\n !withinContext && inReplyToId && inReplyToAccount\n ? 'status-reply-to'\n : ''\n } visibility-${visibility} ${_pinned ? 'status-pinned' : ''} ${\n {\n s: 'small',\n m: 'medium',\n l: 'large',\n }[size]\n } ${_deleted ? 'status-deleted' : ''} ${quoted ? 'status-card' : ''}`}\n onMouseEnter={debugHover}\n onContextMenu={(e) => {\n // FIXME: this code isn't getting called on Chrome at all?\n if (!showContextMenu) return;\n if (e.metaKey) return;\n // console.log('context menu', e);\n const link = e.target.closest('a');\n if (link && /^https?:\\/\\//.test(link.getAttribute('href'))) return;\n e.preventDefault();\n setContextMenuProps({\n anchorPoint: {\n x: e.clientX,\n y: e.clientY,\n },\n direction: 'right',\n });\n setIsContextMenuOpen(true);\n }}\n {...(showContextMenu ? bindLongPressContext() : {})}\n >\n {showContextMenu && (\n <ControlledMenu\n ref={contextMenuRef}\n state={isContextMenuOpen ? 'open' : undefined}\n {...contextMenuProps}\n onClose={(e) => {\n setIsContextMenuOpen(false);\n // statusRef.current?.focus?.();\n if (e?.reason === 'click') {\n statusRef.current?.closest('[tabindex]')?.focus?.();\n }\n }}\n portal={{\n target: document.body,\n }}\n containerProps={{\n style: {\n // Higher than the backdrop\n zIndex: 1001,\n },\n onClick: () => {\n contextMenuRef.current?.closeMenu?.();\n },\n }}\n overflow=\"auto\"\n boundingBoxPadding={safeBoundingBoxPadding()}\n unmountOnClose\n >\n {StatusMenuItems}\n </ControlledMenu>\n )}\n {size !== 'l' && (\n <div class=\"status-badge\">\n {reblogged && <Icon class=\"reblog\" icon=\"rocket\" size=\"s\" />}\n {favourited && <Icon class=\"favourite\" icon=\"heart\" size=\"s\" />}\n {bookmarked && <Icon class=\"bookmark\" icon=\"bookmark\" size=\"s\" />}\n {_pinned && <Icon class=\"pin\" icon=\"pin\" size=\"s\" />}\n </div>\n )}\n {size !== 's' && (\n <a\n href={accountURL}\n tabindex=\"-1\"\n // target=\"_blank\"\n title={`@${acct}`}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n states.showAccount = {\n account: status.account,\n instance,\n };\n }}\n >\n <Avatar url={avatarStatic || avatar} size=\"xxl\" squircle={bot} />\n </a>\n )}\n <div class=\"container\">\n <div class=\"meta\">\n <span class=\"meta-name\">\n <NameText\n account={status.account}\n instance={instance}\n showAvatar={size === 's'}\n showAcct={isSizeLarge}\n />\n </span>\n {/* {inReplyToAccount && !withinContext && size !== 's' && (\n <>\n {' '}\n <span class=\"ib\">\n <Icon icon=\"arrow-right\" class=\"arrow\" />{' '}\n <NameText account={inReplyToAccount} instance={instance} short />\n </span>\n </>\n )} */}\n {/* </span> */}{' '}\n {size !== 'l' &&\n (_deleted ? (\n <span class=\"status-deleted-tag\">Deleted</span>\n ) : url && !previewMode && !quoted ? (\n <Link\n to={instance ? `/${instance}/s/${id}` : `/s/${id}`}\n onClick={(e) => {\n if (\n e.metaKey ||\n e.ctrlKey ||\n e.shiftKey ||\n e.altKey ||\n e.which === 2\n ) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n onStatusLinkClick?.(e, status);\n setContextMenuProps({\n anchorRef: {\n current: e.currentTarget,\n },\n align: 'end',\n direction: 'bottom',\n gap: 4,\n });\n setIsContextMenuOpen(true);\n }}\n class={`time ${\n isContextMenuOpen && contextMenuProps?.anchorRef\n ? 'is-open'\n : ''\n }`}\n >\n {showCommentHint && !showCommentCount ? (\n <Icon\n icon=\"comment2\"\n size=\"s\"\n alt={`${repliesCount} ${\n repliesCount === 1 ? 'reply' : 'replies'\n }`}\n />\n ) : (\n <Icon\n icon={visibilityIconsMap[visibility]}\n alt={visibilityText[visibility]}\n size=\"s\"\n />\n )}{' '}\n <RelativeTime datetime={createdAtDate} format=\"micro\" />\n </Link>\n ) : (\n // <Menu\n // instanceRef={menuInstanceRef}\n // portal={{\n // target: document.body,\n // }}\n // containerProps={{\n // style: {\n // // Higher than the backdrop\n // zIndex: 1001,\n // },\n // onClick: (e) => {\n // if (e.target === e.currentTarget)\n // menuInstanceRef.current?.closeMenu?.();\n // },\n // }}\n // align=\"end\"\n // gap={4}\n // overflow=\"auto\"\n // viewScroll=\"close\"\n // boundingBoxPadding=\"8 8 8 8\"\n // unmountOnClose\n // menuButton={({ open }) => (\n // <Link\n // to={instance ? `/${instance}/s/${id}` : `/s/${id}`}\n // onClick={(e) => {\n // e.preventDefault();\n // e.stopPropagation();\n // onStatusLinkClick?.(e, status);\n // }}\n // class={`time ${open ? 'is-open' : ''}`}\n // >\n // <Icon\n // icon={visibilityIconsMap[visibility]}\n // alt={visibilityText[visibility]}\n // size=\"s\"\n // />{' '}\n // <RelativeTime datetime={createdAtDate} format=\"micro\" />\n // </Link>\n // )}\n // >\n // {StatusMenuItems}\n // </Menu>\n <span class=\"time\">\n <Icon\n icon={visibilityIconsMap[visibility]}\n alt={visibilityText[visibility]}\n size=\"s\"\n />{' '}\n <RelativeTime datetime={createdAtDate} format=\"micro\" />\n </span>\n ))}\n </div>\n {visibility === 'direct' && (\n <>\n <div class=\"status-direct-badge\">Private mention</div>{' '}\n </>\n )}\n {!withinContext && (\n <>\n {isThread ? (\n <div class=\"status-thread-badge\">\n <Icon icon=\"thread\" size=\"s\" />\n Thread\n {snapStates.statusThreadNumber[sKey]\n ? ` ${snapStates.statusThreadNumber[sKey]}/X`\n : ''}\n </div>\n ) : (\n !!inReplyToId &&\n !!inReplyToAccount &&\n (!!spoilerText ||\n !mentions.find((mention) => {\n return mention.id === inReplyToAccountId;\n })) && (\n <div class=\"status-reply-badge\">\n <Icon icon=\"reply\" />{' '}\n <NameText\n account={inReplyToAccount}\n instance={instance}\n short\n />\n </div>\n )\n )}\n </>\n )}\n <div\n class={`content-container ${\n spoilerText || sensitive ? 'has-spoiler' : ''\n } ${showSpoiler ? 'show-spoiler' : ''} ${\n showSpoilerMedia ? 'show-media' : ''\n }`}\n data-content-text-weight={contentTextWeight ? textWeight() : null}\n style={\n (isSizeLarge || contentTextWeight) && {\n '--content-text-weight': textWeight(),\n }\n }\n >\n {!!spoilerText && (\n <>\n <div\n class=\"content spoiler-content\"\n lang={language}\n dir=\"auto\"\n ref={spoilerContentRef}\n data-read-more={readMoreText}\n >\n <p>\n <EmojiText text={spoilerText} emojis={emojis} />\n </p>\n </div>\n {readingExpandSpoilers || previewMode ? (\n <div class=\"spoiler-divider\">\n <Icon icon=\"eye-open\" /> Content warning\n </div>\n ) : (\n <button\n class={`light spoiler-button ${\n showSpoiler ? 'spoiling' : ''\n }`}\n type=\"button\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (showSpoiler) {\n delete states.spoilers[id];\n if (!readingExpandSpoilers) {\n delete states.spoilersMedia[id];\n }\n } else {\n states.spoilers[id] = true;\n if (!readingExpandSpoilers) {\n states.spoilersMedia[id] = true;\n }\n }\n }}\n >\n <Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}\n {showSpoiler ? 'Show less' : 'Show content'}\n </button>\n )}\n </>\n )}\n {!!content && (\n <div class=\"content\" ref={contentRef} data-read-more={readMoreText}>\n <div\n lang={language}\n dir=\"auto\"\n class=\"inner-content\"\n onClick={handleContentLinks({\n mentions,\n instance,\n previewMode,\n statusURL: url,\n })}\n dangerouslySetInnerHTML={{\n __html: enhanceContent(content, {\n emojis,\n postEnhanceDOM: (dom) => {\n // Remove target=\"_blank\" from links\n dom\n .querySelectorAll('a.u-url[target=\"_blank\"]')\n .forEach((a) => {\n if (!/http/i.test(a.innerText.trim())) {\n a.removeAttribute('target');\n }\n });\n // if (previewMode) return;\n // Unfurl Mastodon links\n // Array.from(\n // dom.querySelectorAll(\n // 'a[href]:not(.u-url):not(.mention):not(.hashtag)',\n // ),\n // )\n // .filter((a) => {\n // const url = a.href;\n // const isPostItself =\n // url === status.url || url === status.uri;\n // return !isPostItself && isMastodonLinkMaybe(url);\n // })\n // .forEach((a, i) => {\n // unfurlMastodonLink(currentInstance, a.href).then(\n // (result) => {\n // if (!result) return;\n // a.removeAttribute('target');\n // if (!sKey) return;\n // if (!Array.isArray(states.statusQuotes[sKey])) {\n // states.statusQuotes[sKey] = [];\n // }\n // if (!states.statusQuotes[sKey][i]) {\n // states.statusQuotes[sKey].splice(i, 0, result);\n // }\n // },\n // );\n // });\n },\n }),\n }}\n />\n <QuoteStatuses id={id} instance={instance} level={quoted} />\n </div>\n )}\n {!!poll && (\n <Poll\n lang={language}\n poll={poll}\n readOnly={readOnly || !sameInstance || !authenticated}\n onUpdate={(newPoll) => {\n states.statuses[sKey].poll = newPoll;\n }}\n refresh={() => {\n return masto.v1.polls\n .$select(poll.id)\n .fetch()\n .then((pollResponse) => {\n states.statuses[sKey].poll = pollResponse;\n })\n .catch((e) => {}); // Silently fail\n }}\n votePoll={(choices) => {\n return masto.v1.polls\n .$select(poll.id)\n .votes.create({\n choices,\n })\n .then((pollResponse) => {\n states.statuses[sKey].poll = pollResponse;\n })\n .catch((e) => {}); // Silently fail\n }}\n />\n )}\n {(((enableTranslate || inlineTranslate) &&\n !!content.trim() &&\n !!getHTMLText(emojifyText(content, emojis)) &&\n differentLanguage) ||\n forceTranslate) && (\n <TranslationBlock\n forceTranslate={forceTranslate || inlineTranslate}\n mini={!isSizeLarge && !withinContext}\n sourceLanguage={language}\n text={getPostText(status)}\n />\n )}\n {!previewMode &&\n sensitive &&\n !!mediaAttachments.length &&\n readingExpandMedia !== 'show_all' && (\n <button\n class={`plain spoiler-media-button ${\n showSpoilerMedia ? 'spoiling' : ''\n }`}\n type=\"button\"\n hidden={!readingExpandSpoilers && !!spoilerText}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (showSpoilerMedia) {\n delete states.spoilersMedia[id];\n } else {\n states.spoilersMedia[id] = true;\n }\n }}\n >\n <Icon icon={showSpoilerMedia ? 'eye-open' : 'eye-close'} />{' '}\n {showSpoilerMedia ? 'Show less' : 'Show media'}\n </button>\n )}\n {!!mediaAttachments.length && (\n <MultipleMediaFigure\n lang={language}\n enabled={showMultipleMediaCaptions}\n captionChildren={captionChildren}\n >\n <div\n ref={mediaContainerRef}\n class={`media-container media-eq${mediaAttachments.length} ${\n mediaAttachments.length > 2 ? 'media-gt2' : ''\n } ${mediaAttachments.length > 4 ? 'media-gt4' : ''}`}\n >\n {displayedMediaAttachments.map((media, i) => (\n <Media\n key={media.id}\n media={media}\n autoAnimate={isSizeLarge}\n showCaption={mediaAttachments.length === 1}\n lang={language}\n altIndex={\n showMultipleMediaCaptions && !!media.description && i + 1\n }\n to={`/${instance}/s/${id}?${\n withinContext ? 'media' : 'media-only'\n }=${i + 1}`}\n onClick={\n onMediaClick\n ? (e) => {\n onMediaClick(e, i, media, status);\n }\n : undefined\n }\n />\n ))}\n </div>\n </MultipleMediaFigure>\n )}\n {!!card &&\n /^https/i.test(card?.url) &&\n !sensitive &&\n !spoilerText &&\n !poll &&\n !mediaAttachments.length &&\n !snapStates.statusQuotes[sKey] && (\n <Card\n card={card}\n selfReferential={\n card?.url === status.url || card?.url === status.uri\n }\n instance={currentInstance}\n />\n )}\n </div>\n {!isSizeLarge && showCommentCount && (\n <div class=\"content-comment-hint insignificant\">\n <Icon icon=\"comment2\" alt=\"Replies\" /> {repliesCount}\n </div>\n )}\n {isSizeLarge && (\n <>\n <div class=\"extra-meta\">\n {_deleted ? (\n <span class=\"status-deleted-tag\">Deleted</span>\n ) : (\n <>\n <Icon\n icon={visibilityIconsMap[visibility]}\n alt={visibilityText[visibility]}\n />{' '}\n <a href={url} target=\"_blank\" rel=\"noopener noreferrer\">\n <time\n class=\"created\"\n datetime={createdAtDate.toISOString()}\n title={createdAtDate.toLocaleString()}\n >\n {createdDateText}\n </time>\n </a>\n {editedAt && (\n <>\n {' '}\n &bull; <Icon icon=\"pencil\" alt=\"Edited\" />{' '}\n <time\n tabIndex=\"0\"\n class=\"edited\"\n datetime={editedAtDate.toISOString()}\n onClick={() => {\n setShowEdited(id);\n }}\n >\n {editedDateText}\n </time>\n </>\n )}\n </>\n )}\n </div>\n <div class={`actions ${_deleted ? 'disabled' : ''}`}>\n <div class=\"action has-count\">\n <StatusButton\n title=\"Reply\"\n alt=\"Comments\"\n class=\"reply-button\"\n icon=\"comment\"\n count={repliesCount}\n onClick={replyStatus}\n />\n </div>\n {/* <div class=\"action has-count\">\n <StatusButton\n checked={reblogged}\n title={['Boost', 'Unboost']}\n alt={['Boost', 'Boosted']}\n class=\"reblog-button\"\n icon=\"rocket\"\n count={reblogsCount}\n onClick={boostStatus}\n disabled={!canBoost}\n />\n </div> */}\n <MenuConfirm\n disabled={!canBoost}\n onClick={confirmBoostStatus}\n confirmLabel={\n <>\n <Icon icon=\"rocket\" />\n <span>{reblogged ? 'Unboost?' : 'Boost to everyone?'}</span>\n </>\n }\n menuFooter={\n mediaNoDesc &&\n !reblogged && (\n <div class=\"footer\">\n <Icon icon=\"alert\" />\n Some media have no descriptions.\n </div>\n )\n }\n >\n <div class=\"action has-count\">\n <StatusButton\n checked={reblogged}\n title={['Boost', 'Unboost']}\n alt={['Boost', 'Boosted']}\n class=\"reblog-button\"\n icon=\"rocket\"\n count={reblogsCount}\n // onClick={boostStatus}\n disabled={!canBoost}\n />\n </div>\n </MenuConfirm>\n <div class=\"action has-count\">\n <StatusButton\n checked={favourited}\n title={['Like', 'Unlike']}\n alt={['Like', 'Liked']}\n class=\"favourite-button\"\n icon=\"heart\"\n count={favouritesCount}\n onClick={favouriteStatus}\n />\n </div>\n <div class=\"action\">\n <StatusButton\n checked={bookmarked}\n title={['Bookmark', 'Unbookmark']}\n alt={['Bookmark', 'Bookmarked']}\n class=\"bookmark-button\"\n icon=\"bookmark\"\n onClick={bookmarkStatus}\n />\n </div>\n <Menu2\n portal={{\n target:\n document.querySelector('.status-deck') || document.body,\n }}\n align=\"end\"\n gap={4}\n overflow=\"auto\"\n viewScroll=\"close\"\n menuButton={\n <div class=\"action\">\n <button\n type=\"button\"\n title=\"More\"\n class=\"plain more-button\"\n >\n <Icon icon=\"more\" size=\"l\" alt=\"More\" />\n </button>\n </div>\n }\n >\n {StatusMenuItems}\n </Menu2>\n </div>\n </>\n )}\n </div>\n {!!showEdited && (\n <Modal\n class=\"light\"\n onClick={(e) => {\n if (e.target === e.currentTarget) {\n setShowEdited(false);\n // statusRef.current?.focus();\n }\n }}\n >\n <EditedAtModal\n statusID={showEdited}\n instance={instance}\n fetchStatusHistory={() => {\n return masto.v1.statuses.$select(showEdited).history.list();\n }}\n onClose={() => {\n setShowEdited(false);\n statusRef.current?.focus();\n }}\n />\n </Modal>\n )}\n </article>\n );\n}\n\nfunction MultipleMediaFigure(props) {\n const { enabled, children, lang, captionChildren } = props;\n if (!enabled || !captionChildren) return children;\n return (\n <figure class=\"media-figure-multiple\">\n {children}\n <figcaption lang={lang} dir=\"auto\">\n {captionChildren}\n </figcaption>\n </figure>\n );\n}\n\nfunction Card({ card, selfReferential, instance }) {\n const snapStates = useSnapshot(states);\n const {\n blurhash,\n title,\n description,\n html,\n providerName,\n providerUrl,\n authorName,\n authorUrl,\n width,\n height,\n image,\n imageDescription,\n url,\n type,\n embedUrl,\n language,\n publishedAt,\n } = card;\n\n /* type\n link = Link OEmbed\n photo = Photo OEmbed\n video = Video OEmbed\n rich = iframe OEmbed. Not currently accepted, so wont show up in practice.\n */\n\n const hasText = title || providerName || authorName;\n const isLandscape = width / height >= 1.2;\n const size = isLandscape ? 'large' : '';\n\n const [cardStatusURL, setCardStatusURL] = useState(null);\n // const [cardStatusID, setCardStatusID] = useState(null);\n useEffect(() => {\n if (hasText && image && !selfReferential && isMastodonLinkMaybe(url)) {\n unfurlMastodonLink(instance, url).then((result) => {\n if (!result) return;\n const { id, url } = result;\n setCardStatusURL('#' + url);\n\n // NOTE: This is for quote post\n // (async () => {\n // const { masto } = api({ instance });\n // const status = await masto.v1.statuses.$select(id).fetch();\n // saveStatus(status, instance);\n // setCardStatusID(id);\n // })();\n });\n }\n }, [hasText, image, selfReferential]);\n\n // if (cardStatusID) {\n // return (\n // <Status statusID={cardStatusID} instance={instance} size=\"s\" readOnly />\n // );\n // }\n\n if (snapStates.unfurledLinks[url]) return null;\n\n if (hasText && (image || (type === 'photo' && blurhash))) {\n const domain = new URL(url).hostname\n .replace(/^www\\./, '')\n .replace(/\\/$/, '');\n let blurhashImage;\n const rgbAverageColor =\n image && blurhash ? getBlurHashAverageColor(blurhash) : null;\n if (!image) {\n const w = 44;\n const h = 44;\n const blurhashPixels = decodeBlurHash(blurhash, w, h);\n const canvas = document.createElement('canvas');\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext('2d');\n const imageData = ctx.createImageData(w, h);\n imageData.data.set(blurhashPixels);\n ctx.putImageData(imageData, 0, 0);\n blurhashImage = canvas.toDataURL();\n }\n return (\n <a\n href={cardStatusURL || url}\n target={cardStatusURL ? null : '_blank'}\n rel=\"nofollow noopener noreferrer\"\n class={`card link ${blurhashImage ? '' : size}`}\n lang={language}\n dir=\"auto\"\n style={{\n '--average-color':\n rgbAverageColor && `rgb(${rgbAverageColor.join(',')})`,\n }}\n >\n <div class=\"card-image\">\n <img\n src={image || blurhashImage}\n width={width}\n height={height}\n loading=\"lazy\"\n alt={imageDescription || ''}\n onError={(e) => {\n try {\n e.target.style.display = 'none';\n } catch (e) {}\n }}\n />\n </div>\n <div class=\"meta-container\">\n <p class=\"meta domain\" dir=\"auto\">\n {domain}\n </p>\n <p class=\"title\" dir=\"auto\">\n {title}\n </p>\n <p class=\"meta\" dir=\"auto\">\n {description ||\n (!!publishedAt && (\n <RelativeTime datetime={publishedAt} format=\"micro\" />\n ))}\n </p>\n </div>\n </a>\n );\n } else if (type === 'photo') {\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"nofollow noopener noreferrer\"\n class=\"card photo\"\n >\n <img\n src={embedUrl}\n width={width}\n height={height}\n alt={title || description}\n loading=\"lazy\"\n style={{\n height: 'auto',\n aspectRatio: `${width}/${height}`,\n }}\n />\n </a>\n );\n } else if (type === 'video') {\n if (/youtube/i.test(providerName)) {\n // Get ID from e.g. https://www.youtube.com/watch?v=[VIDEO_ID]\n const videoID = url.match(/watch\\?v=([^&]+)/)?.[1];\n if (videoID) {\n return <lite-youtube videoid={videoID} nocookie></lite-youtube>;\n }\n }\n return (\n <div\n class=\"card video\"\n style={{\n aspectRatio: `${width}/${height}`,\n }}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n } else if (hasText && !image) {\n const domain = new URL(url).hostname.replace(/^www\\./, '');\n return (\n <a\n href={cardStatusURL || url}\n target={cardStatusURL ? null : '_blank'}\n rel=\"nofollow noopener noreferrer\"\n class={`card link no-image`}\n lang={language}\n >\n <div class=\"meta-container\">\n <p class=\"meta domain\">\n <Icon icon=\"link\" size=\"s\" /> <span>{domain}</span>\n </p>\n <p class=\"title\">{title}</p>\n <p class=\"meta\">{description || providerName || authorName}</p>\n </div>\n </a>\n );\n }\n}\n\nfunction EditedAtModal({\n statusID,\n instance,\n fetchStatusHistory = () => {},\n onClose,\n}) {\n const [uiState, setUIState] = useState('default');\n const [editHistory, setEditHistory] = useState([]);\n\n useEffect(() => {\n setUIState('loading');\n (async () => {\n try {\n const editHistory = await fetchStatusHistory();\n console.log(editHistory);\n setEditHistory(editHistory);\n setUIState('default');\n } catch (e) {\n console.error(e);\n setUIState('error');\n }\n })();\n }, []);\n\n return (\n <div id=\"edit-history\" class=\"sheet\">\n {!!onClose && (\n <button type=\"button\" class=\"sheet-close\" onClick={onClose}>\n <Icon icon=\"x\" />\n </button>\n )}\n <header>\n <h2>Edit History</h2>\n {uiState === 'error' && <p>Failed to load history</p>}\n {uiState === 'loading' && (\n <p>\n <Loader abrupt /> Loading&hellip;\n </p>\n )}\n </header>\n <main tabIndex=\"-1\">\n {editHistory.length > 0 && (\n <ol>\n {editHistory.map((status) => {\n const { createdAt } = status;\n const createdAtDate = new Date(createdAt);\n return (\n <li key={createdAt} class=\"history-item\">\n <h3>\n <time>\n {niceDateTime(createdAtDate, {\n formatOpts: {\n weekday: 'short',\n second: 'numeric',\n },\n })}\n </time>\n </h3>\n <Status\n status={status}\n instance={instance}\n size=\"s\"\n withinContext\n readOnly\n previewMode\n />\n </li>\n );\n })}\n </ol>\n )}\n </main>\n </div>\n );\n}\n\nfunction StatusButton({\n checked,\n count,\n class: className,\n title,\n alt,\n icon,\n onClick,\n ...props\n}) {\n if (typeof title === 'string') {\n title = [title, title];\n }\n if (typeof alt === 'string') {\n alt = [alt, alt];\n }\n\n const [buttonTitle, setButtonTitle] = useState(title[0] || '');\n const [iconAlt, setIconAlt] = useState(alt[0] || '');\n\n useEffect(() => {\n if (checked) {\n setButtonTitle(title[1] || '');\n setIconAlt(alt[1] || '');\n } else {\n setButtonTitle(title[0] || '');\n setIconAlt(alt[0] || '');\n }\n }, [checked, title, alt]);\n\n return (\n <button\n type=\"button\"\n title={buttonTitle}\n class={`plain ${className} ${checked ? 'checked' : ''}`}\n onClick={(e) => {\n if (!onClick) return;\n e.preventDefault();\n e.stopPropagation();\n onClick(e);\n }}\n {...props}\n >\n <Icon icon={icon} size=\"l\" alt={iconAlt} />\n {!!count && (\n <>\n {' '}\n <small title={count}>{shortenNumber(count)}</small>\n </>\n )}\n </button>\n );\n}\n\nexport function formatDuration(time) {\n if (!time) return;\n let hours = Math.floor(time / 3600);\n let minutes = Math.floor((time % 3600) / 60);\n let seconds = Math.round(time % 60);\n\n if (hours === 0) {\n return `${minutes}:${seconds.toString().padStart(2, '0')}`;\n } else {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds\n .toString()\n .padStart(2, '0')}`;\n }\n}\n\nfunction nicePostURL(url) {\n if (!url) return;\n const urlObj = new URL(url);\n const { host, pathname } = urlObj;\n const path = pathname.replace(/\\/$/, '');\n // split only first slash\n const [_, username, restPath] = path.match(/\\/(@[^\\/]+)\\/(.*)/) || [];\n return (\n <>\n {host}\n {username ? (\n <>\n /{username}\n <wbr />\n <span class=\"more-insignificant\">/{restPath}</span>\n </>\n ) : (\n <span class=\"more-insignificant\">{path}</span>\n )}\n </>\n );\n}\n\nfunction FilteredStatus({\n status,\n filterInfo,\n instance,\n containerProps = {},\n showFollowedTags,\n}) {\n const snapStates = useSnapshot(states);\n const {\n id: statusID,\n account: { avatar, avatarStatic, bot, group },\n createdAt,\n visibility,\n reblog,\n } = status;\n const isReblog = !!reblog;\n const filterTitleStr = filterInfo?.titlesStr || '';\n const createdAtDate = new Date(createdAt);\n const statusPeekText = statusPeek(status.reblog || status);\n\n const [showPeek, setShowPeek] = useState(false);\n const bindLongPressPeek = useLongPress(\n () => {\n setShowPeek(true);\n },\n {\n threshold: 600,\n captureEvent: true,\n detect: 'touch',\n cancelOnMovement: 2, // true allows movement of up to 25 pixels\n },\n );\n\n const statusPeekRef = useTruncated();\n const sKey = statusKey(status.id, instance);\n const ssKey =\n statusKey(status.id, instance) +\n ' ' +\n (statusKey(reblog?.id, instance) || '');\n\n const actualStatusID = reblog?.id || statusID;\n const url = instance\n ? `/${instance}/s/${actualStatusID}`\n : `/s/${actualStatusID}`;\n const isFollowedTags =\n showFollowedTags && !!snapStates.statusFollowedTags[sKey]?.length;\n\n return (\n <div\n class={\n isReblog\n ? group\n ? 'status-group'\n : 'status-reblog'\n : isFollowedTags\n ? 'status-followed-tags'\n : ''\n }\n {...containerProps}\n title={statusPeekText}\n onContextMenu={(e) => {\n e.preventDefault();\n setShowPeek(true);\n }}\n {...bindLongPressPeek()}\n >\n <article data-state-post-id={ssKey} class=\"status filtered\" tabindex=\"-1\">\n <b\n class=\"status-filtered-badge clickable badge-meta\"\n title={filterTitleStr}\n onClick={(e) => {\n e.preventDefault();\n setShowPeek(true);\n }}\n >\n <span>Filtered</span>\n <span>{filterTitleStr}</span>\n </b>{' '}\n <Avatar url={avatarStatic || avatar} squircle={bot} />\n <span class=\"status-filtered-info\">\n <span class=\"status-filtered-info-1\">\n <NameText account={status.account} instance={instance} />{' '}\n <Icon\n icon={visibilityIconsMap[visibility]}\n alt={visibilityText[visibility]}\n size=\"s\"\n />{' '}\n {isReblog ? (\n 'boosted'\n ) : isFollowedTags ? (\n <span>\n {snapStates.statusFollowedTags[sKey].slice(0, 3).map((tag) => (\n <span key={tag} class=\"status-followed-tag-item\">\n #{tag}\n </span>\n ))}\n </span>\n ) : (\n <RelativeTime datetime={createdAtDate} format=\"micro\" />\n )}\n </span>\n <span class=\"status-filtered-info-2\">\n {isReblog && (\n <>\n <Avatar\n url={reblog.account.avatarStatic || reblog.account.avatar}\n squircle={bot}\n />{' '}\n </>\n )}\n {statusPeekText}\n </span>\n </span>\n </article>\n {!!showPeek && (\n <Modal\n class=\"light\"\n onClick={(e) => {\n if (e.target === e.currentTarget) {\n setShowPeek(false);\n }\n }}\n >\n <div id=\"filtered-status-peek\" class=\"sheet\">\n <button\n type=\"button\"\n class=\"sheet-close\"\n onClick={() => setShowPeek(false)}\n >\n <Icon icon=\"x\" />\n </button>\n <header>\n <b class=\"status-filtered-badge\">Filtered</b> {filterTitleStr}\n </header>\n <main tabIndex=\"-1\">\n <Link\n ref={statusPeekRef}\n class=\"status-link\"\n to={url}\n onClick={() => {\n setShowPeek(false);\n }}\n data-read-more=\"Read more →\"\n >\n <Status status={status} instance={instance} size=\"s\" readOnly />\n </Link>\n </main>\n </div>\n </Modal>\n )}\n </div>\n );\n}\n\nconst QuoteStatuses = memo(({ id, instance, level = 0 }) => {\n if (!id || !instance) return;\n const snapStates = useSnapshot(states);\n const sKey = statusKey(id, instance);\n const quotes = snapStates.statusQuotes[sKey];\n const uniqueQuotes = quotes?.filter(\n (q, i, arr) => arr.findIndex((q2) => q2.url === q.url) === i,\n );\n\n if (!uniqueQuotes?.length) return;\n if (level > 2) return;\n\n return uniqueQuotes.map((q) => {\n return (\n <Link\n key={q.instance + q.id}\n to={`${q.instance ? `/${q.instance}` : ''}/s/${q.id}`}\n class=\"status-card-link\"\n data-read-more=\"Read more →\"\n >\n <Status\n statusID={q.id}\n instance={q.instance}\n size=\"s\"\n quoted={level + 1}\n enableCommentHint\n />\n </Link>\n );\n });\n});\n\nexport default memo(Status);\n","import './compose.css';\n\nimport '@github/text-expander-element';\nimport { MenuItem } from '@szhsin/react-menu';\nimport equal from 'fast-deep-equal';\nimport { forwardRef } from 'preact/compat';\nimport { useEffect, useMemo, useRef, useState } from 'preact/hooks';\nimport { useHotkeys } from 'react-hotkeys-hook';\nimport { substring } from 'runes2';\nimport stringLength from 'string-length';\nimport { uid } from 'uid/single';\nimport { useDebouncedCallback, useThrottledCallback } from 'use-debounce';\nimport { useSnapshot } from 'valtio';\n\nimport Menu2 from '../components/menu2';\nimport supportedLanguages from '../data/status-supported-languages';\nimport urlRegex from '../data/url-regex';\nimport { api } from '../utils/api';\nimport db from '../utils/db';\nimport emojifyText from '../utils/emojify-text';\nimport localeMatch from '../utils/locale-match';\nimport openCompose from '../utils/open-compose';\nimport shortenNumber from '../utils/shorten-number';\nimport showToast from '../utils/show-toast';\nimport states, { saveStatus } from '../utils/states';\nimport store from '../utils/store';\nimport {\n getCurrentAccount,\n getCurrentAccountNS,\n getCurrentInstance,\n getCurrentInstanceConfiguration,\n} from '../utils/store-utils';\nimport supports from '../utils/supports';\nimport useCloseWatcher from '../utils/useCloseWatcher';\nimport useInterval from '../utils/useInterval';\nimport visibilityIconsMap from '../utils/visibility-icons-map';\n\nimport AccountBlock from './account-block';\n// import Avatar from './avatar';\nimport Icon from './icon';\nimport Loader from './loader';\nimport Modal from './modal';\nimport Status from './status';\n\nconst { PHANPY_IMG_ALT_API_URL: IMG_ALT_API_URL } = import.meta.env;\n\nconst supportedLanguagesMap = supportedLanguages.reduce((acc, l) => {\n const [code, common, native] = l;\n acc[code] = {\n common,\n native,\n };\n return acc;\n}, {});\n\n/* NOTES:\n - Max character limit includes BOTH status text and Content Warning text\n*/\n\nconst expiryOptions = {\n '5 minutes': 5 * 60,\n '30 minutes': 30 * 60,\n '1 hour': 60 * 60,\n '6 hours': 6 * 60 * 60,\n '12 hours': 12 * 60 * 60,\n '1 day': 24 * 60 * 60,\n '3 days': 3 * 24 * 60 * 60,\n '7 days': 7 * 24 * 60 * 60,\n};\nconst expirySeconds = Object.values(expiryOptions);\nconst oneDay = 24 * 60 * 60;\n\nconst expiresInFromExpiresAt = (expiresAt) => {\n if (!expiresAt) return oneDay;\n const delta = (new Date(expiresAt).getTime() - Date.now()) / 1000;\n return expirySeconds.find((s) => s >= delta) || oneDay;\n};\n\nconst menu = document.createElement('ul');\nmenu.role = 'listbox';\nmenu.className = 'text-expander-menu';\n\n// Set IntersectionObserver on menu, reposition it because text-expander doesn't handle it\nconst windowMargin = 16;\nconst observer = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n const { left, width } = entry.boundingClientRect;\n const { innerWidth } = window;\n if (left + width > innerWidth) {\n menu.style.left = innerWidth - width - windowMargin + 'px';\n }\n }\n });\n});\nobserver.observe(menu);\n\nconst DEFAULT_LANG = localeMatch(\n [new Intl.DateTimeFormat().resolvedOptions().locale, ...navigator.languages],\n supportedLanguages.map((l) => l[0]),\n 'en',\n);\n\n// https://github.com/mastodon/mastodon/blob/c4a429ed47e85a6bbf0d470a41cc2f64cf120c19/app/javascript/mastodon/features/compose/util/counter.js\nconst urlRegexObj = new RegExp(urlRegex.source, urlRegex.flags);\nconst usernameRegex = /(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)/gi;\nconst urlPlaceholder = '$2xxxxxxxxxxxxxxxxxxxxxxx';\nfunction countableText(inputText) {\n return inputText\n .replace(urlRegexObj, urlPlaceholder)\n .replace(usernameRegex, '$1@$3');\n}\n\n// https://github.com/mastodon/mastodon/blob/c03bd2a238741a012aa4b98dc4902d6cf948ab63/app/models/account.rb#L69\nconst USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i;\nconst MENTION_RE = new RegExp(\n `(^|[^=\\\\/\\\\w])(@${USERNAME_RE.source}(?:@[\\\\p{L}\\\\w.-]+[\\\\w]+)?)`,\n 'uig',\n);\n\n// AI-generated, all other regexes are too complicated\nconst HASHTAG_RE = new RegExp(\n `(^|[^=\\\\/\\\\w])(#[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?)(?![\\\\/\\\\w])`,\n 'ig',\n);\n\n// https://github.com/mastodon/mastodon/blob/23e32a4b3031d1da8b911e0145d61b4dd47c4f96/app/models/custom_emoji.rb#L31\nconst SHORTCODE_RE_FRAGMENT = '[a-zA-Z0-9_]{2,}';\nconst SCAN_RE = new RegExp(\n `([^A-Za-z0-9_:\\\\n]|^)(:${SHORTCODE_RE_FRAGMENT}:)(?=[^A-Za-z0-9_:]|$)`,\n 'g',\n);\n\nfunction highlightText(text, { maxCharacters = Infinity }) {\n // Accept text string, return formatted HTML string\n let html = text;\n // Exceeded characters limit\n const { composerCharacterCount } = states;\n let leftoverHTML = '';\n if (composerCharacterCount > maxCharacters) {\n // NOTE: runes2 substring considers surrogate pairs\n // const leftoverCount = composerCharacterCount - maxCharacters;\n // Highlight exceeded characters\n leftoverHTML =\n '<mark class=\"compose-highlight-exceeded\">' +\n // html.slice(-leftoverCount) +\n substring(html, maxCharacters) +\n '</mark>';\n // html = html.slice(0, -leftoverCount);\n html = substring(html, 0, maxCharacters);\n return html + leftoverHTML;\n }\n\n return html\n .replace(urlRegexObj, '$2<mark class=\"compose-highlight-url\">$3</mark>') // URLs\n .replace(MENTION_RE, '$1<mark class=\"compose-highlight-mention\">$2</mark>') // Mentions\n .replace(HASHTAG_RE, '$1<mark class=\"compose-highlight-hashtag\">$2</mark>') // Hashtags\n .replace(\n SCAN_RE,\n '$1<mark class=\"compose-highlight-emoji-shortcode\">$2</mark>',\n ); // Emoji shortcodes\n}\n\nfunction Compose({\n onClose,\n replyToStatus,\n editStatus,\n draftStatus,\n standalone,\n hasOpener,\n}) {\n console.warn('RENDER COMPOSER');\n const { masto, instance } = api();\n const [uiState, setUIState] = useState('default');\n const UID = useRef(draftStatus?.uid || uid());\n console.log('Compose UID', UID.current);\n\n const currentAccount = getCurrentAccount();\n const currentAccountInfo = currentAccount.info;\n\n const configuration = getCurrentInstanceConfiguration();\n console.log('⚙️ Configuration', configuration);\n\n const {\n statuses: {\n maxCharacters,\n maxMediaAttachments,\n charactersReservedPerUrl,\n } = {},\n mediaAttachments: {\n supportedMimeTypes = [],\n imageSizeLimit,\n imageMatrixLimit,\n videoSizeLimit,\n videoMatrixLimit,\n videoFrameRateLimit,\n } = {},\n polls: {\n maxOptions,\n maxCharactersPerOption,\n maxExpiration,\n minExpiration,\n } = {},\n } = configuration || {};\n\n const textareaRef = useRef();\n const spoilerTextRef = useRef();\n const [visibility, setVisibility] = useState('public');\n const [sensitive, setSensitive] = useState(false);\n const [language, setLanguage] = useState(\n store.session.get('currentLanguage') || DEFAULT_LANG,\n );\n const prevLanguage = useRef(language);\n const [mediaAttachments, setMediaAttachments] = useState([]);\n const [poll, setPoll] = useState(null);\n\n const prefs = store.account.get('preferences') || {};\n\n const oninputTextarea = () => {\n if (!textareaRef.current) return;\n textareaRef.current.dispatchEvent(new Event('input'));\n };\n const focusTextarea = () => {\n setTimeout(() => {\n console.debug('FOCUS textarea');\n textareaRef.current?.focus();\n }, 300);\n };\n\n useEffect(() => {\n if (replyToStatus) {\n const { spoilerText, visibility, language, sensitive } = replyToStatus;\n if (spoilerText && spoilerTextRef.current) {\n spoilerTextRef.current.value = spoilerText;\n }\n const mentions = new Set([\n replyToStatus.account.acct,\n ...replyToStatus.mentions.map((m) => m.acct),\n ]);\n const allMentions = [...mentions].filter(\n (m) => m !== currentAccountInfo.acct,\n );\n if (allMentions.length > 0) {\n textareaRef.current.value = `${allMentions\n .map((m) => `@${m}`)\n .join(' ')} `;\n oninputTextarea();\n }\n focusTextarea();\n setVisibility(\n visibility === 'public' && prefs['posting:default:visibility']\n ? prefs['posting:default:visibility']\n : visibility,\n );\n setLanguage(language || prefs.postingDefaultLanguage || DEFAULT_LANG);\n setSensitive(sensitive && !!spoilerText);\n } else if (editStatus) {\n const { visibility, language, sensitive, poll, mediaAttachments } =\n editStatus;\n const composablePoll = !!poll?.options && {\n ...poll,\n options: poll.options.map((o) => o?.title || o),\n expiresIn: poll?.expiresIn || expiresInFromExpiresAt(poll.expiresAt),\n };\n setUIState('loading');\n (async () => {\n try {\n const statusSource = await masto.v1.statuses\n .$select(editStatus.id)\n .source.fetch();\n console.log({ statusSource });\n const { text, spoilerText } = statusSource;\n textareaRef.current.value = text;\n textareaRef.current.dataset.source = text;\n oninputTextarea();\n focusTextarea();\n spoilerTextRef.current.value = spoilerText;\n setVisibility(visibility);\n setLanguage(language || presf.postingDefaultLanguage || DEFAULT_LANG);\n setSensitive(sensitive);\n setPoll(composablePoll);\n setMediaAttachments(mediaAttachments);\n setUIState('default');\n } catch (e) {\n console.error(e);\n alert(e?.reason || e);\n setUIState('error');\n }\n })();\n } else {\n focusTextarea();\n console.log('Apply prefs', prefs);\n if (prefs['posting:default:visibility']) {\n setVisibility(prefs['posting:default:visibility']);\n }\n if (prefs['posting:default:language']) {\n setLanguage(prefs['posting:default:language']);\n }\n if (prefs['posting:default:sensitive']) {\n setSensitive(prefs['posting:default:sensitive']);\n }\n }\n if (draftStatus) {\n const {\n status,\n spoilerText,\n visibility,\n language,\n sensitive,\n poll,\n mediaAttachments,\n } = draftStatus;\n const composablePoll = !!poll?.options && {\n ...poll,\n options: poll.options.map((o) => o?.title || o),\n expiresIn: poll?.expiresIn || expiresInFromExpiresAt(poll.expiresAt),\n };\n textareaRef.current.value = status;\n oninputTextarea();\n focusTextarea();\n if (spoilerText) spoilerTextRef.current.value = spoilerText;\n if (visibility) setVisibility(visibility);\n setLanguage(language || prefs.postingDefaultLanguage || DEFAULT_LANG);\n if (sensitive !== null) setSensitive(sensitive);\n if (composablePoll) setPoll(composablePoll);\n if (mediaAttachments) setMediaAttachments(mediaAttachments);\n }\n }, [draftStatus, editStatus, replyToStatus]);\n\n const formRef = useRef();\n\n const beforeUnloadCopy = 'You have unsaved changes. Discard this post?';\n const canClose = () => {\n const { value, dataset } = textareaRef.current;\n\n // check if loading\n if (uiState === 'loading') {\n console.log('canClose', { uiState });\n return false;\n }\n\n // check for status and media attachments\n const hasMediaAttachments = mediaAttachments.length > 0;\n if (!value && !hasMediaAttachments) {\n console.log('canClose', { value, mediaAttachments });\n return true;\n }\n\n // check if all media attachments have IDs\n const hasIDMediaAttachments =\n mediaAttachments.length > 0 &&\n mediaAttachments.every((media) => media.id);\n if (hasIDMediaAttachments) {\n console.log('canClose', { hasIDMediaAttachments });\n return true;\n }\n\n // check if status contains only \"@acct\", if replying\n const isSelf = replyToStatus?.account.id === currentAccountInfo.id;\n const hasOnlyAcct =\n replyToStatus && value.trim() === `@${replyToStatus.account.acct}`;\n // TODO: check for mentions, or maybe just generic \"@username<space>\", including multiple mentions like \"@username1<space>@username2<space>\"\n if (!isSelf && hasOnlyAcct) {\n console.log('canClose', { isSelf, hasOnlyAcct });\n return true;\n }\n\n // check if status is same with source\n const sameWithSource = value === dataset?.source;\n if (sameWithSource) {\n console.log('canClose', { sameWithSource });\n return true;\n }\n\n console.log('canClose', {\n value,\n hasMediaAttachments,\n hasIDMediaAttachments,\n poll,\n isSelf,\n hasOnlyAcct,\n sameWithSource,\n uiState,\n });\n\n return false;\n };\n\n const confirmClose = () => {\n if (!canClose()) {\n const yes = confirm(beforeUnloadCopy);\n return yes;\n }\n return true;\n };\n\n useEffect(() => {\n // Show warning if user tries to close window with unsaved changes\n const handleBeforeUnload = (e) => {\n if (!canClose()) {\n e.preventDefault();\n e.returnValue = beforeUnloadCopy;\n }\n };\n window.addEventListener('beforeunload', handleBeforeUnload, {\n capture: true,\n });\n return () =>\n window.removeEventListener('beforeunload', handleBeforeUnload, {\n capture: true,\n });\n }, []);\n\n const getCharCount = () => {\n const { value } = textareaRef.current;\n const { value: spoilerText } = spoilerTextRef.current;\n return stringLength(countableText(value)) + stringLength(spoilerText);\n };\n const updateCharCount = () => {\n const count = getCharCount();\n states.composerCharacterCount = count;\n };\n useEffect(updateCharCount, []);\n\n const supportsCloseWatcher = window.CloseWatcher;\n const escDownRef = useRef(false);\n useHotkeys(\n 'esc',\n () => {\n escDownRef.current = true;\n // This won't be true if this event is already handled and not propagated 🤞\n },\n {\n enabled: !supportsCloseWatcher,\n enableOnFormTags: true,\n },\n );\n useHotkeys(\n 'esc',\n () => {\n if (!standalone && escDownRef.current && confirmClose()) {\n onClose();\n }\n escDownRef.current = false;\n },\n {\n enabled: !supportsCloseWatcher,\n enableOnFormTags: true,\n // Use keyup because Esc keydown will close the confirm dialog on Safari\n keyup: true,\n ignoreEventWhen: (e) => {\n const modals = document.querySelectorAll('#modal-container > *');\n const hasModal = !!modals;\n const hasOnlyComposer =\n modals.length === 1 && modals[0].querySelector('#compose-container');\n return hasModal && !hasOnlyComposer;\n },\n },\n );\n useCloseWatcher(() => {\n if (!standalone && confirmClose()) {\n onClose();\n }\n }, [standalone, confirmClose, onClose]);\n\n const prevBackgroundDraft = useRef({});\n const draftKey = () => {\n const ns = getCurrentAccountNS();\n return `${ns}#${UID.current}`;\n };\n const saveUnsavedDraft = () => {\n // Not enabling this for editing status\n // I don't think this warrant a draft mode for a status that's already posted\n // Maybe it could be a big edit change but it should be rare\n if (editStatus) return;\n const key = draftKey();\n const backgroundDraft = {\n key,\n replyTo: replyToStatus\n ? {\n /* Smaller payload of replyToStatus. Reasons:\n - No point storing whole thing\n - Could have media attachments\n - Could be deleted/edited later\n */\n id: replyToStatus.id,\n account: {\n id: replyToStatus.account.id,\n username: replyToStatus.account.username,\n acct: replyToStatus.account.acct,\n },\n }\n : null,\n draftStatus: {\n uid: UID.current,\n status: textareaRef.current.value,\n spoilerText: spoilerTextRef.current.value,\n visibility,\n language,\n sensitive,\n poll,\n mediaAttachments,\n },\n };\n if (!equal(backgroundDraft, prevBackgroundDraft.current) && !canClose()) {\n console.debug('not equal', backgroundDraft, prevBackgroundDraft.current);\n db.drafts\n .set(key, {\n ...backgroundDraft,\n state: 'unsaved',\n updatedAt: Date.now(),\n })\n .then(() => {\n console.debug('DRAFT saved', key, backgroundDraft);\n })\n .catch((e) => {\n console.error('DRAFT failed', key, e);\n });\n prevBackgroundDraft.current = structuredClone(backgroundDraft);\n }\n };\n useInterval(saveUnsavedDraft, 5000); // background save every 5s\n useEffect(() => {\n saveUnsavedDraft();\n // If unmounted, means user discarded the draft\n // Also means pop-out 🙈, but it's okay because the pop-out will persist the ID and re-create the draft\n return () => {\n db.drafts.del(draftKey());\n };\n }, []);\n\n useEffect(() => {\n const handleItems = (e) => {\n const { items } = e.clipboardData || e.dataTransfer;\n const files = [];\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (item.kind === 'file') {\n const file = item.getAsFile();\n if (file && supportedMimeTypes.includes(file.type)) {\n files.push(file);\n }\n }\n }\n if (files.length > 0 && mediaAttachments.length >= maxMediaAttachments) {\n alert(`You can only attach up to ${maxMediaAttachments} files.`);\n return;\n }\n console.log({ files });\n if (files.length > 0) {\n e.preventDefault();\n e.stopPropagation();\n // Auto-cut-off files to avoid exceeding maxMediaAttachments\n const max = maxMediaAttachments - mediaAttachments.length;\n const allowedFiles = files.slice(0, max);\n if (allowedFiles.length <= 0) {\n alert(`You can only attach up to ${maxMediaAttachments} files.`);\n return;\n }\n const mediaFiles = allowedFiles.map((file) => ({\n file,\n type: file.type,\n size: file.size,\n url: URL.createObjectURL(file),\n id: null,\n description: null,\n }));\n setMediaAttachments([...mediaAttachments, ...mediaFiles]);\n }\n };\n window.addEventListener('paste', handleItems);\n const handleDragover = (e) => {\n // Prevent default if there's files\n if (e.dataTransfer.items.length > 0) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n window.addEventListener('dragover', handleDragover);\n window.addEventListener('drop', handleItems);\n return () => {\n window.removeEventListener('paste', handleItems);\n window.removeEventListener('dragover', handleDragover);\n window.removeEventListener('drop', handleItems);\n };\n }, [mediaAttachments]);\n\n const [showEmoji2Picker, setShowEmoji2Picker] = useState(false);\n\n const [topSupportedLanguages, restSupportedLanguages] = useMemo(() => {\n const topLanguages = [];\n const restLanguages = [];\n const { contentTranslationHideLanguages = [] } = states.settings;\n supportedLanguages.forEach((l) => {\n const [code] = l;\n if (\n code === language ||\n code === prevLanguage.current ||\n code === DEFAULT_LANG ||\n contentTranslationHideLanguages.includes(code)\n ) {\n topLanguages.push(l);\n } else {\n restLanguages.push(l);\n }\n });\n topLanguages.sort(([codeA, commonA], [codeB, commonB]) => {\n if (codeA === language) return -1;\n if (codeB === language) return 1;\n return commonA.localeCompare(commonB);\n });\n restLanguages.sort(([codeA, commonA], [codeB, commonB]) =>\n commonA.localeCompare(commonB),\n );\n return [topLanguages, restLanguages];\n }, [language]);\n\n return (\n <div id=\"compose-container-outer\">\n <div id=\"compose-container\" class={standalone ? 'standalone' : ''}>\n <div class=\"compose-top\">\n {currentAccountInfo?.avatarStatic && (\n // <Avatar\n // url={currentAccountInfo.avatarStatic}\n // size=\"xl\"\n // alt={currentAccountInfo.username}\n // squircle={currentAccountInfo?.bot}\n // />\n <AccountBlock\n account={currentAccountInfo}\n accountInstance={currentAccount.instanceURL}\n hideDisplayName\n useAvatarStatic\n />\n )}\n {!standalone ? (\n <span>\n <button\n type=\"button\"\n class=\"light pop-button\"\n disabled={uiState === 'loading'}\n onClick={() => {\n // If there are non-ID media attachments (not yet uploaded), show confirmation dialog because they are not going to be passed to the new window\n // const containNonIDMediaAttachments =\n // mediaAttachments.length > 0 &&\n // mediaAttachments.some((media) => !media.id);\n // if (containNonIDMediaAttachments) {\n // const yes = confirm(\n // 'You have media attachments that are not yet uploaded. Opening a new window will discard them and you will need to re-attach them. Are you sure you want to continue?',\n // );\n // if (!yes) {\n // return;\n // }\n // }\n\n // const mediaAttachmentsWithIDs = mediaAttachments.filter(\n // (media) => media.id,\n // );\n\n const newWin = openCompose({\n editStatus,\n replyToStatus,\n draftStatus: {\n uid: UID.current,\n status: textareaRef.current.value,\n spoilerText: spoilerTextRef.current.value,\n visibility,\n language,\n sensitive,\n poll,\n mediaAttachments,\n },\n });\n\n if (!newWin) {\n return;\n }\n\n onClose();\n }}\n >\n <Icon icon=\"popout\" alt=\"Pop out\" />\n </button>{' '}\n <button\n type=\"button\"\n class=\"light close-button\"\n disabled={uiState === 'loading'}\n onClick={() => {\n if (confirmClose()) {\n onClose();\n }\n }}\n >\n <Icon icon=\"x\" />\n </button>\n </span>\n ) : (\n hasOpener && (\n <button\n type=\"button\"\n class=\"light pop-button\"\n disabled={uiState === 'loading'}\n onClick={() => {\n // If there are non-ID media attachments (not yet uploaded), show confirmation dialog because they are not going to be passed to the new window\n // const containNonIDMediaAttachments =\n // mediaAttachments.length > 0 &&\n // mediaAttachments.some((media) => !media.id);\n // if (containNonIDMediaAttachments) {\n // const yes = confirm(\n // 'You have media attachments that are not yet uploaded. Opening a new window will discard them and you will need to re-attach them. Are you sure you want to continue?',\n // );\n // if (!yes) {\n // return;\n // }\n // }\n\n if (!window.opener) {\n alert('Looks like you closed the parent window.');\n return;\n }\n\n if (window.opener.__STATES__.showCompose) {\n const yes = confirm(\n 'Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?',\n );\n if (!yes) return;\n }\n\n // const mediaAttachmentsWithIDs = mediaAttachments.filter(\n // (media) => media.id,\n // );\n\n onClose({\n fn: () => {\n const passData = {\n editStatus,\n replyToStatus,\n draftStatus: {\n uid: UID.current,\n status: textareaRef.current.value,\n spoilerText: spoilerTextRef.current.value,\n visibility,\n language,\n sensitive,\n poll,\n mediaAttachments,\n },\n };\n window.opener.__COMPOSE__ = passData; // Pass it here instead of `showCompose` due to some weird proxy issue again\n window.opener.__STATES__.showCompose = true;\n },\n });\n }}\n >\n <Icon icon=\"popin\" alt=\"Pop in\" />\n </button>\n )\n )}\n </div>\n {!!replyToStatus && (\n <div class=\"status-preview\">\n <Status status={replyToStatus} size=\"s\" previewMode />\n <div class=\"status-preview-legend reply-to\">\n Replying to @\n {replyToStatus.account.acct || replyToStatus.account.username}\n &rsquo;s post\n </div>\n </div>\n )}\n {!!editStatus && (\n <div class=\"status-preview\">\n <Status status={editStatus} size=\"s\" previewMode />\n <div class=\"status-preview-legend\">Editing source post</div>\n </div>\n )}\n <form\n ref={formRef}\n class={`form-visibility-${visibility}`}\n style={{\n pointerEvents: uiState === 'loading' ? 'none' : 'auto',\n opacity: uiState === 'loading' ? 0.5 : 1,\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {\n formRef.current.dispatchEvent(\n new Event('submit', { cancelable: true }),\n );\n }\n }}\n onSubmit={(e) => {\n e.preventDefault();\n\n const formData = new FormData(e.target);\n const entries = Object.fromEntries(formData.entries());\n console.log('ENTRIES', entries);\n let { status, visibility, sensitive, spoilerText } = entries;\n\n // Pre-cleanup\n sensitive = sensitive === 'on'; // checkboxes return \"on\" if checked\n\n // Validation\n /* Let the backend validate this\n if (stringLength(status) > maxCharacters) {\n alert(`Status is too long! Max characters: ${maxCharacters}`);\n return;\n }\n if (\n sensitive &&\n stringLength(status) + stringLength(spoilerText) > maxCharacters\n ) {\n alert(\n `Status and content warning is too long! Max characters: ${maxCharacters}`,\n );\n return;\n }\n */\n if (poll) {\n if (poll.options.length < 2) {\n alert('Poll must have at least 2 options');\n return;\n }\n if (poll.options.some((option) => option === '')) {\n alert('Some poll choices are empty');\n return;\n }\n }\n // TODO: check for URLs and use `charactersReservedPerUrl` to calculate max characters\n\n if (mediaAttachments.length > 0) {\n // If there are media attachments, check if they have no descriptions\n const hasNoDescriptions = mediaAttachments.some(\n (media) => !media.description?.trim?.(),\n );\n if (hasNoDescriptions) {\n const yes = confirm(\n 'Some media have no descriptions. Continue?',\n );\n if (!yes) return;\n }\n }\n\n // Post-cleanup\n spoilerText = (sensitive && spoilerText) || undefined;\n status = status === '' ? undefined : status;\n\n setUIState('loading');\n (async () => {\n try {\n console.log('MEDIA ATTACHMENTS', mediaAttachments);\n if (mediaAttachments.length > 0) {\n // Upload media attachments first\n const mediaPromises = mediaAttachments.map((attachment) => {\n const { file, description, id } = attachment;\n console.log('UPLOADING', attachment);\n if (id) {\n // If already uploaded\n return attachment;\n } else {\n const params = removeNullUndefined({\n file,\n description,\n });\n return masto.v2.media.create(params).then((res) => {\n if (res.id) {\n attachment.id = res.id;\n }\n return res;\n });\n }\n });\n const results = await Promise.allSettled(mediaPromises);\n\n // If any failed, return\n if (\n results.some((result) => {\n return result.status === 'rejected' || !result.value?.id;\n })\n ) {\n setUIState('error');\n // Alert all the reasons\n results.forEach((result) => {\n if (result.status === 'rejected') {\n console.error(result);\n alert(result.reason || `Attachment #${i} failed`);\n }\n });\n return;\n }\n\n console.log({ results, mediaAttachments });\n }\n\n /* NOTE:\n Using snakecase here because masto.js's `isObject` returns false for `params`, ONLY happens when opening in pop-out window. This is maybe due to `window.masto` variable being passed from the parent window. The check that failed is `x.constructor === Object`, so maybe the `Object` in new window is different than parent window's?\n Code: https://github.com/neet/masto.js/blob/dd0d649067b6a2b6e60fbb0a96597c373a255b00/src/serializers/is-object.ts#L2\n\n // TODO: Note above is no longer true in Masto.js v6. Revisit this.\n */\n let params = {\n status,\n // spoilerText,\n spoiler_text: spoilerText,\n language,\n sensitive,\n poll,\n // mediaIds: mediaAttachments.map((attachment) => attachment.id),\n media_ids: mediaAttachments.map(\n (attachment) => attachment.id,\n ),\n };\n if (editStatus && supports('@mastodon/edit-media-attributes')) {\n params.media_attributes = mediaAttachments.map(\n (attachment) => {\n return {\n id: attachment.id,\n description: attachment.description,\n // focus\n // thumbnail\n };\n },\n );\n } else if (!editStatus) {\n params.visibility = visibility;\n // params.inReplyToId = replyToStatus?.id || undefined;\n params.in_reply_to_id = replyToStatus?.id || undefined;\n }\n params = removeNullUndefined(params);\n console.log('POST', params);\n\n let newStatus;\n if (editStatus) {\n newStatus = await masto.v1.statuses\n .$select(editStatus.id)\n .update(params);\n saveStatus(newStatus, instance, {\n skipThreading: true,\n });\n } else {\n try {\n newStatus = await masto.v1.statuses.create(params, {\n idempotencyKey: UID.current,\n });\n } catch (_) {\n // If idempotency key fails, try again without it\n newStatus = await masto.v1.statuses.create(params);\n }\n }\n setUIState('default');\n\n // Close\n onClose({\n // type: post, reply, edit\n type: editStatus ? 'edit' : replyToStatus ? 'reply' : 'post',\n newStatus,\n instance,\n });\n } catch (e) {\n console.error(e);\n alert(e?.reason || e);\n setUIState('error');\n }\n })();\n }}\n >\n <div class=\"toolbar stretch\">\n <input\n ref={spoilerTextRef}\n type=\"text\"\n name=\"spoilerText\"\n placeholder=\"Content warning\"\n disabled={uiState === 'loading'}\n class=\"spoiler-text-field\"\n lang={language}\n spellCheck=\"true\"\n dir=\"auto\"\n style={{\n opacity: sensitive ? 1 : 0,\n pointerEvents: sensitive ? 'auto' : 'none',\n }}\n onInput={() => {\n updateCharCount();\n }}\n />\n <label\n class={`toolbar-button ${sensitive ? 'highlight' : ''}`}\n title=\"Content warning or sensitive media\"\n >\n <input\n name=\"sensitive\"\n type=\"checkbox\"\n checked={sensitive}\n disabled={uiState === 'loading'}\n onChange={(e) => {\n const sensitive = e.target.checked;\n setSensitive(sensitive);\n if (sensitive) {\n spoilerTextRef.current?.focus();\n } else {\n textareaRef.current?.focus();\n }\n }}\n />\n <Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />\n </label>{' '}\n <label\n class={`toolbar-button ${\n visibility !== 'public' && !sensitive ? 'show-field' : ''\n } ${visibility !== 'public' ? 'highlight' : ''}`}\n title={`Visibility: ${visibility}`}\n >\n <Icon icon={visibilityIconsMap[visibility]} alt={visibility} />\n <select\n name=\"visibility\"\n value={visibility}\n onChange={(e) => {\n setVisibility(e.target.value);\n }}\n disabled={uiState === 'loading' || !!editStatus}\n >\n <option value=\"public\">\n Public <Icon icon=\"earth\" />\n </option>\n <option value=\"unlisted\">Unlisted</option>\n <option value=\"private\">Followers only</option>\n <option value=\"direct\">Private mention</option>\n </select>\n </label>{' '}\n </div>\n <Textarea\n ref={textareaRef}\n placeholder={\n replyToStatus\n ? 'Post your reply'\n : editStatus\n ? 'Edit your post'\n : 'What are you doing?'\n }\n required={mediaAttachments?.length === 0}\n disabled={uiState === 'loading'}\n lang={language}\n onInput={() => {\n updateCharCount();\n }}\n maxCharacters={maxCharacters}\n performSearch={(params) => {\n const { type, q, limit } = params;\n if (type === 'accounts') {\n return masto.v1.accounts.search.list({\n q,\n limit,\n resolve: false,\n });\n }\n return masto.v2.search.fetch(params);\n }}\n />\n {mediaAttachments?.length > 0 && (\n <div class=\"media-attachments\">\n {mediaAttachments.map((attachment, i) => {\n const { id, file } = attachment;\n const fileID = file?.size + file?.type + file?.name;\n return (\n <MediaAttachment\n key={id || fileID || i}\n attachment={attachment}\n disabled={uiState === 'loading'}\n lang={language}\n onDescriptionChange={(value) => {\n setMediaAttachments((attachments) => {\n const newAttachments = [...attachments];\n newAttachments[i].description = value;\n return newAttachments;\n });\n }}\n onRemove={() => {\n setMediaAttachments((attachments) => {\n return attachments.filter((_, j) => j !== i);\n });\n }}\n />\n );\n })}\n <label class=\"media-sensitive\">\n <input\n name=\"sensitive\"\n type=\"checkbox\"\n checked={sensitive}\n disabled={uiState === 'loading'}\n onChange={(e) => {\n const sensitive = e.target.checked;\n setSensitive(sensitive);\n }}\n />{' '}\n <span>Mark media as sensitive</span>{' '}\n <Icon icon={`eye-${sensitive ? 'close' : 'open'}`} />\n </label>\n </div>\n )}\n {!!poll && (\n <Poll\n lang={language}\n maxOptions={maxOptions}\n maxExpiration={maxExpiration}\n minExpiration={minExpiration}\n maxCharactersPerOption={maxCharactersPerOption}\n poll={poll}\n disabled={uiState === 'loading'}\n onInput={(poll) => {\n if (poll) {\n const newPoll = { ...poll };\n setPoll(newPoll);\n } else {\n setPoll(null);\n }\n }}\n />\n )}\n <div\n class=\"toolbar wrap\"\n style={{\n justifyContent: 'flex-end',\n }}\n >\n <span>\n <label class=\"toolbar-button\">\n <input\n type=\"file\"\n accept={supportedMimeTypes.join(',')}\n multiple={mediaAttachments.length < maxMediaAttachments - 1}\n disabled={\n uiState === 'loading' ||\n mediaAttachments.length >= maxMediaAttachments ||\n !!poll\n }\n onChange={(e) => {\n const files = e.target.files;\n if (!files) return;\n\n const mediaFiles = Array.from(files).map((file) => ({\n file,\n type: file.type,\n size: file.size,\n url: URL.createObjectURL(file),\n id: null, // indicate uploaded state\n description: null,\n }));\n console.log('MEDIA ATTACHMENTS', files, mediaFiles);\n\n // Validate max media attachments\n if (\n mediaAttachments.length + mediaFiles.length >\n maxMediaAttachments\n ) {\n alert(\n `You can only attach up to ${maxMediaAttachments} files.`,\n );\n } else {\n setMediaAttachments((attachments) => {\n return attachments.concat(mediaFiles);\n });\n }\n // Reset\n e.target.value = '';\n }}\n />\n <Icon icon=\"attachment\" />\n </label>{' '}\n <button\n type=\"button\"\n class=\"toolbar-button\"\n disabled={\n uiState === 'loading' || !!poll || !!mediaAttachments.length\n }\n onClick={() => {\n setPoll({\n options: ['', ''],\n expiresIn: 24 * 60 * 60, // 1 day\n multiple: false,\n });\n }}\n >\n <Icon icon=\"poll\" alt=\"Add poll\" />\n </button>{' '}\n <button\n type=\"button\"\n class=\"toolbar-button\"\n disabled={uiState === 'loading'}\n onClick={() => {\n setShowEmoji2Picker(true);\n }}\n >\n <Icon icon=\"emoji2\" />\n </button>\n </span>\n <div class=\"spacer\" />\n {uiState === 'loading' ? (\n <Loader abrupt />\n ) : (\n <CharCountMeter\n maxCharacters={maxCharacters}\n hidden={uiState === 'loading'}\n />\n )}\n <label\n class={`toolbar-button ${\n language !== prevLanguage.current ? 'highlight' : ''\n }`}\n >\n <span class=\"icon-text\">\n {supportedLanguagesMap[language]?.native}\n </span>\n <select\n name=\"language\"\n value={language}\n onChange={(e) => {\n const { value } = e.target;\n setLanguage(value || DEFAULT_LANG);\n store.session.set('currentLanguage', value || DEFAULT_LANG);\n }}\n disabled={uiState === 'loading'}\n >\n {topSupportedLanguages.map(([code, common, native]) => (\n <option value={code} key={code}>\n {common} ({native})\n </option>\n ))}\n <hr />\n {restSupportedLanguages.map(([code, common, native]) => (\n <option value={code} key={code}>\n {common} ({native})\n </option>\n ))}\n </select>\n </label>{' '}\n <button\n type=\"submit\"\n class=\"large\"\n disabled={uiState === 'loading'}\n >\n {replyToStatus ? 'Reply' : editStatus ? 'Update' : 'Post'}\n </button>\n </div>\n </form>\n </div>\n {showEmoji2Picker && (\n <Modal\n class=\"light\"\n onClick={(e) => {\n if (e.target === e.currentTarget) {\n setShowEmoji2Picker(false);\n }\n }}\n >\n <CustomEmojisModal\n masto={masto}\n instance={instance}\n onClose={() => {\n setShowEmoji2Picker(false);\n }}\n onSelect={(emoji) => {\n const emojiWithSpace = ` ${emoji} `;\n const textarea = textareaRef.current;\n if (!textarea) return;\n const { selectionStart, selectionEnd } = textarea;\n const text = textarea.value;\n const newText =\n text.slice(0, selectionStart) +\n emojiWithSpace +\n text.slice(selectionEnd);\n textarea.value = newText;\n textarea.selectionStart = textarea.selectionEnd =\n selectionEnd + emojiWithSpace.length;\n textarea.focus();\n textarea.dispatchEvent(new Event('input'));\n }}\n />\n </Modal>\n )}\n </div>\n );\n}\n\nfunction autoResizeTextarea(textarea) {\n if (!textarea) return;\n const { value, offsetHeight, scrollHeight, clientHeight } = textarea;\n if (offsetHeight < window.innerHeight) {\n // NOTE: This check is needed because the offsetHeight return 50000 (really large number) on first render\n // No idea why it does that, will re-investigate in far future\n const offset = offsetHeight - clientHeight;\n const height = value ? scrollHeight + offset + 'px' : null;\n textarea.style.height = height;\n }\n}\n\nconst Textarea = forwardRef((props, ref) => {\n const { masto } = api();\n const [text, setText] = useState(ref.current?.value || '');\n const { maxCharacters, performSearch = () => {}, ...textareaProps } = props;\n // const snapStates = useSnapshot(states);\n // const charCount = snapStates.composerCharacterCount;\n\n const customEmojis = useRef();\n useEffect(() => {\n (async () => {\n try {\n const emojis = await masto.v1.customEmojis.list();\n console.log({ emojis });\n customEmojis.current = emojis;\n } catch (e) {\n // silent fail\n console.error(e);\n }\n })();\n }, []);\n\n const textExpanderRef = useRef();\n const textExpanderTextRef = useRef('');\n useEffect(() => {\n let handleChange, handleValue, handleCommited;\n if (textExpanderRef.current) {\n handleChange = (e) => {\n // console.log('text-expander-change', e);\n const { key, provide, text } = e.detail;\n textExpanderTextRef.current = text;\n\n if (text === '') {\n provide(\n Promise.resolve({\n matched: false,\n }),\n );\n return;\n }\n\n if (key === ':') {\n // const emojis = customEmojis.current.filter((emoji) =>\n // emoji.shortcode.startsWith(text),\n // );\n const emojis = filterShortcodes(customEmojis.current, text);\n let html = '';\n emojis.forEach((emoji) => {\n const { shortcode, url } = emoji;\n html += `\n <li role=\"option\" data-value=\"${encodeHTML(shortcode)}\">\n <img src=\"${encodeHTML(\n url,\n )}\" width=\"16\" height=\"16\" alt=\"\" loading=\"lazy\" /> \n :${encodeHTML(shortcode)}:\n </li>`;\n });\n // console.log({ emojis, html });\n menu.innerHTML = html;\n provide(\n Promise.resolve({\n matched: emojis.length > 0,\n fragment: menu,\n }),\n );\n return;\n }\n\n const type = {\n '@': 'accounts',\n '#': 'hashtags',\n }[key];\n provide(\n new Promise((resolve) => {\n const searchResults = performSearch({\n type,\n q: text,\n limit: 5,\n });\n searchResults.then((value) => {\n if (text !== textExpanderTextRef.current) {\n return;\n }\n console.log({ value, type, v: value[type] });\n const results = value[type] || value;\n console.log('RESULTS', value, results);\n let html = '';\n results.forEach((result) => {\n const {\n name,\n avatarStatic,\n displayName,\n username,\n acct,\n emojis,\n history,\n } = result;\n const displayNameWithEmoji = emojifyText(displayName, emojis);\n // const item = menuItem.cloneNode();\n if (acct) {\n html += `\n <li role=\"option\" data-value=\"${encodeHTML(acct)}\">\n <span class=\"avatar\">\n <img src=\"${encodeHTML(\n avatarStatic,\n )}\" width=\"16\" height=\"16\" alt=\"\" loading=\"lazy\" />\n </span>\n <span>\n <b>${displayNameWithEmoji || username}</b>\n <br>@${encodeHTML(acct)}\n </span>\n </li>\n `;\n } else {\n const total = history?.reduce?.(\n (acc, cur) => acc + +cur.uses,\n 0,\n );\n html += `\n <li role=\"option\" data-value=\"${encodeHTML(name)}\">\n <span class=\"grow\">#<b>${encodeHTML(name)}</b></span>\n ${\n total\n ? `<span class=\"count\">${shortenNumber(total)}</span>`\n : ''\n }\n </li>\n `;\n }\n menu.innerHTML = html;\n });\n console.log('MENU', results, menu);\n resolve({\n matched: results.length > 0,\n fragment: menu,\n });\n });\n }),\n );\n };\n\n textExpanderRef.current.addEventListener(\n 'text-expander-change',\n handleChange,\n );\n\n handleValue = (e) => {\n const { key, item } = e.detail;\n if (key === ':') {\n e.detail.value = `:${item.dataset.value}:`;\n } else {\n e.detail.value = `${key}${item.dataset.value}`;\n }\n };\n\n textExpanderRef.current.addEventListener(\n 'text-expander-value',\n handleValue,\n );\n\n handleCommited = (e) => {\n const { input } = e.detail;\n setText(input.value);\n // fire input event\n if (ref.current) {\n const event = new Event('input', { bubbles: true });\n ref.current.dispatchEvent(event);\n }\n };\n\n textExpanderRef.current.addEventListener(\n 'text-expander-committed',\n handleCommited,\n );\n }\n\n return () => {\n if (textExpanderRef.current) {\n textExpanderRef.current.removeEventListener(\n 'text-expander-change',\n handleChange,\n );\n textExpanderRef.current.removeEventListener(\n 'text-expander-value',\n handleValue,\n );\n textExpanderRef.current.removeEventListener(\n 'text-expander-committed',\n handleCommited,\n );\n }\n };\n }, []);\n\n useEffect(() => {\n // Resize observer for textarea\n const textarea = ref.current;\n if (!textarea) return;\n const resizeObserver = new ResizeObserver(() => {\n // Get height of textarea, set height to textExpander\n if (textExpanderRef.current) {\n const { height } = textarea.getBoundingClientRect();\n textExpanderRef.current.style.height = height + 'px';\n }\n });\n resizeObserver.observe(textarea);\n }, []);\n\n const slowHighlightPerf = useRef(0); // increment if slow\n const composeHighlightRef = useRef();\n const throttleHighlightText = useThrottledCallback((text) => {\n if (!composeHighlightRef.current) return;\n if (slowHighlightPerf.current > 3) {\n // After 3 times of lag, disable highlighting\n composeHighlightRef.current.innerHTML = '';\n composeHighlightRef.current = null; // Destroy the whole thing\n throttleHighlightText?.cancel?.();\n return;\n }\n let start;\n let end;\n if (slowHighlightPerf.current <= 3) start = Date.now();\n composeHighlightRef.current.innerHTML =\n highlightText(text, {\n maxCharacters,\n }) + '\\n';\n if (slowHighlightPerf.current <= 3) end = Date.now();\n console.debug('HIGHLIGHT PERF', { start, end, diff: end - start });\n if (start && end && end - start > 50) {\n // if slow, increment\n slowHighlightPerf.current++;\n }\n // Newline to prevent multiple line breaks at the end from being collapsed, no idea why\n }, 500);\n\n return (\n <text-expander\n ref={textExpanderRef}\n keys=\"@ # :\"\n class=\"compose-field-container\"\n >\n <textarea\n class=\"compose-field\"\n autoCapitalize=\"sentences\"\n autoComplete=\"on\"\n autoCorrect=\"on\"\n spellCheck=\"true\"\n dir=\"auto\"\n rows=\"6\"\n cols=\"50\"\n {...textareaProps}\n ref={ref}\n name=\"status\"\n value={text}\n onKeyDown={(e) => {\n // Get line before cursor position after pressing 'Enter'\n const { key, target } = e;\n if (key === 'Enter') {\n try {\n const { value, selectionStart } = target;\n const textBeforeCursor = value.slice(0, selectionStart);\n const lastLine = textBeforeCursor.split('\\n').slice(-1)[0];\n if (lastLine) {\n // If line starts with \"- \" or \"12. \"\n if (/^\\s*(-|\\d+\\.)\\s/.test(lastLine)) {\n // insert \"- \" at cursor position\n const [_, preSpaces, bullet, postSpaces, anything] =\n lastLine.match(/^(\\s*)(-|\\d+\\.)(\\s+)(.+)?/) || [];\n if (anything) {\n e.preventDefault();\n const [number] = bullet.match(/\\d+/) || [];\n const newBullet = number ? `${+number + 1}.` : '-';\n const text = `\\n${preSpaces}${newBullet}${postSpaces}`;\n target.setRangeText(text, selectionStart, selectionStart);\n const pos = selectionStart + text.length;\n target.setSelectionRange(pos, pos);\n } else {\n // trim the line before the cursor, then insert new line\n const pos = selectionStart - lastLine.length;\n target.setRangeText('', pos, selectionStart);\n }\n autoResizeTextarea(target);\n target.dispatchEvent(new Event('input'));\n }\n }\n } catch (e) {\n // silent fail\n console.error(e);\n }\n }\n if (composeHighlightRef.current) {\n composeHighlightRef.current.scrollTop = target.scrollTop;\n }\n }}\n onInput={(e) => {\n const { target } = e;\n const text = target.value;\n setText(text);\n autoResizeTextarea(target);\n props.onInput?.(e);\n throttleHighlightText(text);\n }}\n style={{\n width: '100%',\n height: '4em',\n // '--text-weight': (1 + charCount / 140).toFixed(1) || 1,\n }}\n onScroll={(e) => {\n if (composeHighlightRef.current) {\n const { scrollTop } = e.target;\n composeHighlightRef.current.scrollTop = scrollTop;\n }\n }}\n />\n <div\n ref={composeHighlightRef}\n class=\"compose-highlight\"\n aria-hidden=\"true\"\n />\n </text-expander>\n );\n});\n\nfunction CharCountMeter({ maxCharacters = 500, hidden }) {\n const snapStates = useSnapshot(states);\n const charCount = snapStates.composerCharacterCount;\n const leftChars = maxCharacters - charCount;\n if (hidden) {\n return <meter class=\"donut\" hidden />;\n }\n return (\n <meter\n class={`donut ${\n leftChars <= -10\n ? 'explode'\n : leftChars <= 0\n ? 'danger'\n : leftChars <= 20\n ? 'warning'\n : ''\n }`}\n value={charCount}\n max={maxCharacters}\n data-left={leftChars}\n title={`${leftChars}/${maxCharacters}`}\n style={{\n '--percentage': (charCount / maxCharacters) * 100,\n }}\n />\n );\n}\n\nfunction MediaAttachment({\n attachment,\n disabled,\n lang,\n onDescriptionChange = () => {},\n onRemove = () => {},\n}) {\n const [uiState, setUIState] = useState('default');\n const supportsEdit = supports('@mastodon/edit-media-attributes');\n const { type, id, file } = attachment;\n const url = useMemo(\n () => (file ? URL.createObjectURL(file) : attachment.url),\n [file, attachment.url],\n );\n console.log({ attachment });\n const [description, setDescription] = useState(attachment.description);\n const [suffixType, subtype] = type.split('/');\n const debouncedOnDescriptionChange = useDebouncedCallback(\n onDescriptionChange,\n 250,\n );\n\n const [showModal, setShowModal] = useState(false);\n const textareaRef = useRef(null);\n useEffect(() => {\n let timer;\n if (showModal && textareaRef.current) {\n timer = setTimeout(() => {\n textareaRef.current.focus();\n }, 100);\n }\n return () => {\n clearTimeout(timer);\n };\n }, [showModal]);\n\n const descTextarea = (\n <>\n {!!id && !supportsEdit ? (\n <div class=\"media-desc\">\n <span class=\"tag\">Uploaded</span>\n <p title={description}>\n {attachment.description || <i>No description</i>}\n </p>\n </div>\n ) : (\n <textarea\n ref={textareaRef}\n value={description || ''}\n lang={lang}\n placeholder={\n {\n image: 'Image description',\n video: 'Video description',\n audio: 'Audio description',\n }[suffixType]\n }\n autoCapitalize=\"sentences\"\n autoComplete=\"on\"\n autoCorrect=\"on\"\n spellCheck=\"true\"\n dir=\"auto\"\n disabled={disabled || uiState === 'loading'}\n class={uiState === 'loading' ? 'loading' : ''}\n maxlength=\"1500\" // Not unicode-aware :(\n // TODO: Un-hard-code this maxlength, ref: https://github.com/mastodon/mastodon/blob/b59fb28e90bc21d6fd1a6bafd13cfbd81ab5be54/app/models/media_attachment.rb#L39\n onInput={(e) => {\n const { value } = e.target;\n setDescription(value);\n debouncedOnDescriptionChange(value);\n }}\n ></textarea>\n )}\n </>\n );\n\n const toastRef = useRef(null);\n useEffect(() => {\n return () => {\n toastRef.current?.hideToast?.();\n };\n }, []);\n\n return (\n <>\n <div class=\"media-attachment\">\n <div\n class=\"media-preview\"\n tabIndex=\"0\"\n onClick={() => {\n setShowModal(true);\n }}\n >\n {suffixType === 'image' ? (\n <img src={url} alt=\"\" />\n ) : suffixType === 'video' || suffixType === 'gifv' ? (\n <video src={url} playsinline muted />\n ) : suffixType === 'audio' ? (\n <audio src={url} controls />\n ) : null}\n </div>\n {descTextarea}\n <div class=\"media-aside\">\n <button\n type=\"button\"\n class=\"plain close-button\"\n disabled={disabled}\n onClick={onRemove}\n >\n <Icon icon=\"x\" />\n </button>\n </div>\n </div>\n {showModal && (\n <Modal\n class=\"light\"\n onClick={(e) => {\n if (e.target === e.currentTarget) {\n setShowModal(false);\n }\n }}\n >\n <div id=\"media-sheet\" class=\"sheet sheet-max\">\n <button\n type=\"button\"\n class=\"sheet-close\"\n onClick={() => {\n setShowModal(false);\n }}\n >\n <Icon icon=\"x\" />\n </button>\n <header>\n <h2>\n {\n {\n image: 'Edit image description',\n video: 'Edit video description',\n audio: 'Edit audio description',\n }[suffixType]\n }\n </h2>\n </header>\n <main tabIndex=\"-1\">\n <div class=\"media-preview\">\n {suffixType === 'image' ? (\n <img src={url} alt=\"\" />\n ) : suffixType === 'video' || suffixType === 'gifv' ? (\n <video src={url} playsinline controls />\n ) : suffixType === 'audio' ? (\n <audio src={url} controls />\n ) : null}\n </div>\n <div class=\"media-form\">\n {descTextarea}\n <footer>\n {suffixType === 'image' &&\n /^(png|jpe?g|gif|webp)$/i.test(subtype) &&\n !!states.settings.mediaAltGenerator &&\n !!IMG_ALT_API_URL && (\n <Menu2\n portal={{\n target: document.body,\n }}\n containerProps={{\n style: {\n zIndex: 1001,\n },\n }}\n align=\"center\"\n position=\"anchor\"\n overflow=\"auto\"\n menuButton={\n <button type=\"button\" title=\"More\" class=\"plain\">\n <Icon icon=\"more\" size=\"l\" alt=\"More\" />\n </button>\n }\n >\n <MenuItem\n disabled={uiState === 'loading'}\n onClick={() => {\n setUIState('loading');\n toastRef.current = showToast({\n text: 'Generating description. Please wait...',\n duration: -1,\n });\n // POST with multipart\n (async function () {\n try {\n const body = new FormData();\n body.append('image', file);\n const response = await fetch(IMG_ALT_API_URL, {\n method: 'POST',\n body,\n }).then((r) => r.json());\n setDescription(response.description);\n } catch (e) {\n console.error(e);\n showToast('Failed to generate description');\n } finally {\n setUIState('default');\n toastRef.current?.hideToast?.();\n }\n })();\n }}\n >\n <Icon icon=\"sparkles2\" />\n <span>Generate description…</span>\n </MenuItem>\n </Menu2>\n )}\n <button\n type=\"button\"\n class=\"light block\"\n onClick={() => {\n setShowModal(false);\n }}\n disabled={uiState === 'loading'}\n >\n Done\n </button>\n </footer>\n </div>\n </main>\n </div>\n </Modal>\n )}\n </>\n );\n}\n\nfunction Poll({\n lang,\n poll,\n disabled,\n onInput = () => {},\n maxOptions,\n maxExpiration,\n minExpiration,\n maxCharactersPerOption,\n}) {\n const { options, expiresIn, multiple } = poll;\n\n return (\n <div class={`poll ${multiple ? 'multiple' : ''}`}>\n <div class=\"poll-choices\">\n {options.map((option, i) => (\n <div class=\"poll-choice\" key={i}>\n <input\n required\n type=\"text\"\n value={option}\n disabled={disabled}\n maxlength={maxCharactersPerOption}\n placeholder={`Choice ${i + 1}`}\n lang={lang}\n spellCheck=\"true\"\n dir=\"auto\"\n onInput={(e) => {\n const { value } = e.target;\n options[i] = value;\n onInput(poll);\n }}\n />\n <button\n type=\"button\"\n class=\"plain2 poll-button\"\n disabled={disabled || options.length <= 1}\n onClick={() => {\n options.splice(i, 1);\n onInput(poll);\n }}\n >\n <Icon icon=\"x\" size=\"s\" />\n </button>\n </div>\n ))}\n </div>\n <div class=\"poll-toolbar\">\n <button\n type=\"button\"\n class=\"plain2 poll-button\"\n disabled={disabled || options.length >= maxOptions}\n onClick={() => {\n options.push('');\n onInput(poll);\n }}\n >\n +\n </button>{' '}\n <label class=\"multiple-choices\">\n <input\n type=\"checkbox\"\n checked={multiple}\n disabled={disabled}\n onChange={(e) => {\n const { checked } = e.target;\n poll.multiple = checked;\n onInput(poll);\n }}\n />{' '}\n Multiple choices\n </label>\n <label class=\"expires-in\">\n Duration{' '}\n <select\n value={expiresIn}\n disabled={disabled}\n onChange={(e) => {\n const { value } = e.target;\n poll.expiresIn = value;\n onInput(poll);\n }}\n >\n {Object.entries(expiryOptions)\n .filter(([label, value]) => {\n return value >= minExpiration && value <= maxExpiration;\n })\n .map(([label, value]) => (\n <option value={value} key={value}>\n {label}\n </option>\n ))}\n </select>\n </label>\n </div>\n <div class=\"poll-toolbar\">\n <button\n type=\"button\"\n class=\"plain remove-poll-button\"\n disabled={disabled}\n onClick={() => {\n onInput(null);\n }}\n >\n Remove poll\n </button>\n </div>\n </div>\n );\n}\n\nfunction filterShortcodes(emojis, searchTerm) {\n searchTerm = searchTerm.toLowerCase();\n\n // Return an array of shortcodes that start with or contain the search term, sorted by relevance and limited to the first 5\n return emojis\n .sort((a, b) => {\n let aLower = a.shortcode.toLowerCase();\n let bLower = b.shortcode.toLowerCase();\n\n let aStartsWith = aLower.startsWith(searchTerm);\n let bStartsWith = bLower.startsWith(searchTerm);\n let aContains = aLower.includes(searchTerm);\n let bContains = bLower.includes(searchTerm);\n let bothStartWith = aStartsWith && bStartsWith;\n let bothContain = aContains && bContains;\n\n return bothStartWith\n ? a.length - b.length\n : aStartsWith\n ? -1\n : bStartsWith\n ? 1\n : bothContain\n ? a.length - b.length\n : aContains\n ? -1\n : bContains\n ? 1\n : 0;\n })\n .slice(0, 5);\n}\n\nfunction encodeHTML(str) {\n return str.replace(/[&<>\"']/g, function (char) {\n return '&#' + char.charCodeAt(0) + ';';\n });\n}\n\nfunction removeNullUndefined(obj) {\n for (let key in obj) {\n if (obj[key] === null || obj[key] === undefined) {\n delete obj[key];\n }\n }\n return obj;\n}\n\nfunction CustomEmojisModal({\n masto,\n instance,\n onClose = () => {},\n onSelect = () => {},\n}) {\n const [uiState, setUIState] = useState('default');\n const customEmojisList = useRef([]);\n const [customEmojis, setCustomEmojis] = useState({});\n const recentlyUsedCustomEmojis = useMemo(\n () => store.account.get('recentlyUsedCustomEmojis') || [],\n );\n useEffect(() => {\n setUIState('loading');\n (async () => {\n try {\n const emojis = await masto.v1.customEmojis.list();\n // Group emojis by category\n const emojisCat = {\n '--recent--': recentlyUsedCustomEmojis.filter((emoji) =>\n emojis.find((e) => e.shortcode === emoji.shortcode),\n ),\n };\n const othersCat = [];\n emojis.forEach((emoji) => {\n if (!emoji.visibleInPicker) return;\n customEmojisList.current?.push?.(emoji);\n if (!emoji.category) {\n othersCat.push(emoji);\n return;\n }\n if (!emojisCat[emoji.category]) {\n emojisCat[emoji.category] = [];\n }\n emojisCat[emoji.category].push(emoji);\n });\n if (othersCat.length) {\n emojisCat['--others--'] = othersCat;\n }\n setCustomEmojis(emojisCat);\n setUIState('default');\n } catch (e) {\n setUIState('error');\n console.error(e);\n }\n })();\n }, []);\n\n return (\n <div id=\"custom-emojis-sheet\" class=\"sheet\">\n {!!onClose && (\n <button type=\"button\" class=\"sheet-close\" onClick={onClose}>\n <Icon icon=\"x\" />\n </button>\n )}\n <header>\n <b>Custom emojis</b>{' '}\n {uiState === 'loading' ? (\n <Loader />\n ) : (\n <small class=\"insignificant\"> • {instance}</small>\n )}\n </header>\n <main>\n <div class=\"custom-emojis-list\">\n {uiState === 'error' && (\n <div class=\"ui-state\">\n <p>Error loading custom emojis</p>\n </div>\n )}\n {uiState === 'default' &&\n Object.entries(customEmojis).map(\n ([category, emojis]) =>\n !!emojis?.length && (\n <>\n <div class=\"section-header\">\n {{\n '--recent--': 'Recently used',\n '--others--': 'Others',\n }[category] || category}\n </div>\n <section>\n {emojis.map((emoji) => (\n <button\n key={emoji}\n type=\"button\"\n class=\"plain4\"\n onClick={() => {\n onClose();\n requestAnimationFrame(() => {\n onSelect(`:${emoji.shortcode}:`);\n });\n let recentlyUsedCustomEmojis =\n store.account.get('recentlyUsedCustomEmojis') ||\n [];\n const recentlyUsedEmojiIndex =\n recentlyUsedCustomEmojis.findIndex(\n (e) => e.shortcode === emoji.shortcode,\n );\n if (recentlyUsedEmojiIndex !== -1) {\n // Move emoji to index 0\n recentlyUsedCustomEmojis.splice(\n recentlyUsedEmojiIndex,\n 1,\n );\n recentlyUsedCustomEmojis.unshift(emoji);\n } else {\n recentlyUsedCustomEmojis.unshift(emoji);\n // Remove unavailable ones\n recentlyUsedCustomEmojis =\n recentlyUsedCustomEmojis.filter((e) =>\n customEmojisList.current?.find?.(\n (emoji) => emoji.shortcode === e.shortcode,\n ),\n );\n // Limit to 10\n recentlyUsedCustomEmojis =\n recentlyUsedCustomEmojis.slice(0, 10);\n }\n\n // Store back\n store.account.set(\n 'recentlyUsedCustomEmojis',\n recentlyUsedCustomEmojis,\n );\n }}\n title={`:${emoji.shortcode}:`}\n >\n <picture>\n {!!emoji.staticUrl && (\n <source\n srcset={emoji.staticUrl}\n media=\"(prefers-reduced-motion: reduce)\"\n />\n )}\n <img\n class=\"shortcode-emoji\"\n src={emoji.url || emoji.staticUrl}\n alt={emoji.shortcode}\n width=\"16\"\n height=\"16\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </picture>\n </button>\n ))}\n </section>\n </>\n ),\n )}\n </div>\n </main>\n </div>\n );\n}\n\nexport default Compose;\n","import { useLayoutEffect } from 'preact/hooks';\nimport { matchPath } from 'react-router-dom';\nimport { subscribeKey } from 'valtio/utils';\n\nimport states from './states';\n\nconst { PHANPY_CLIENT_NAME: CLIENT_NAME } = import.meta.env;\n\nexport default function useTitle(title, path) {\n function setTitle() {\n const { currentLocation } = states;\n const hasPaths = Array.isArray(path);\n let paths = hasPaths ? path : [];\n // Workaround for matchPath not working for optional path segments\n // https://github.com/remix-run/react-router/discussions/9862\n if (!hasPaths && /:?\\w+\\?/.test(path)) {\n paths.push(path.replace(/(:\\w+)\\?/g, '$1'));\n paths.push(path.replace(/\\/?:\\w+\\?/g, ''));\n }\n let matched = false;\n if (paths.length) {\n matched = paths.some((p) => matchPath(p, currentLocation));\n } else if (path) {\n matched = matchPath(path, currentLocation);\n }\n console.debug('setTitle', { title, path, currentLocation, paths, matched });\n if (matched) {\n document.title = title ? `${title} / ${CLIENT_NAME}` : CLIENT_NAME;\n }\n }\n\n useLayoutEffect(() => {\n const unsub = subscribeKey(states, 'currentLocation', setTitle);\n setTitle();\n return unsub;\n }, [title, path]);\n}\n"],"file":"assets/useTitle-zvzl1Hla.js"}