From 015e798394a98bb826446ceb55bfadacc5888215 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 11 Oct 2022 10:51:33 +0200 Subject: Move some modules from flavours/glitch/utils/ back to flavours/glitch/features/ui/util/ --- .../flavours/glitch/features/ui/util/fullscreen.js | 46 +++++++++++++++ .../glitch/features/ui/util/get_rect_from_entry.js | 21 +++++++ .../ui/util/intersection_observer_wrapper.js | 57 ++++++++++++++++++ .../glitch/features/ui/util/optional_motion.js | 5 ++ .../features/ui/util/react_router_helpers.js | 69 ++++++++++++++++++++++ .../glitch/features/ui/util/reduced_motion.js | 44 ++++++++++++++ .../glitch/features/ui/util/schedule_idle_task.js | 29 +++++++++ 7 files changed, 271 insertions(+) create mode 100644 app/javascript/flavours/glitch/features/ui/util/fullscreen.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/optional_motion.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/reduced_motion.js create mode 100644 app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js (limited to 'app/javascript/flavours/glitch/features/ui/util') diff --git a/app/javascript/flavours/glitch/features/ui/util/fullscreen.js b/app/javascript/flavours/glitch/features/ui/util/fullscreen.js new file mode 100644 index 000000000..cf5d0cf98 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/fullscreen.js @@ -0,0 +1,46 @@ +// APIs for normalizing fullscreen operations. Note that Edge uses +// the WebKit-prefixed APIs currently (as of Edge 16). + +export const isFullscreen = () => document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement; + +export const exitFullscreen = () => { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } +}; + +export const requestFullscreen = el => { + if (el.requestFullscreen) { + el.requestFullscreen(); + } else if (el.webkitRequestFullscreen) { + el.webkitRequestFullscreen(); + } else if (el.mozRequestFullScreen) { + el.mozRequestFullScreen(); + } +}; + +export const attachFullscreenListener = (listener) => { + if ('onfullscreenchange' in document) { + document.addEventListener('fullscreenchange', listener); + } else if ('onwebkitfullscreenchange' in document) { + document.addEventListener('webkitfullscreenchange', listener); + } else if ('onmozfullscreenchange' in document) { + document.addEventListener('mozfullscreenchange', listener); + } +}; + +export const detachFullscreenListener = (listener) => { + if ('onfullscreenchange' in document) { + document.removeEventListener('fullscreenchange', listener); + } else if ('onwebkitfullscreenchange' in document) { + document.removeEventListener('webkitfullscreenchange', listener); + } else if ('onmozfullscreenchange' in document) { + document.removeEventListener('mozfullscreenchange', listener); + } +}; diff --git a/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js b/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js new file mode 100644 index 000000000..c266cd7dc --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/get_rect_from_entry.js @@ -0,0 +1,21 @@ + +// Get the bounding client rect from an IntersectionObserver entry. +// This is to work around a bug in Chrome: https://crbug.com/737228 + +let hasBoundingRectBug; + +function getRectFromEntry(entry) { + if (typeof hasBoundingRectBug !== 'boolean') { + const boundingRect = entry.target.getBoundingClientRect(); + const observerRect = entry.boundingClientRect; + hasBoundingRectBug = boundingRect.height !== observerRect.height || + boundingRect.top !== observerRect.top || + boundingRect.width !== observerRect.width || + boundingRect.bottom !== observerRect.bottom || + boundingRect.left !== observerRect.left || + boundingRect.right !== observerRect.right; + } + return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect; +} + +export default getRectFromEntry; diff --git a/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js b/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js new file mode 100644 index 000000000..2b24c6583 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/intersection_observer_wrapper.js @@ -0,0 +1,57 @@ +// Wrapper for IntersectionObserver in order to make working with it +// a bit easier. We also follow this performance advice: +// "If you need to observe multiple elements, it is both possible and +// advised to observe multiple elements using the same IntersectionObserver +// instance by calling observe() multiple times." +// https://developers.google.com/web/updates/2016/04/intersectionobserver + +class IntersectionObserverWrapper { + + callbacks = {}; + observerBacklog = []; + observer = null; + + connect (options) { + const onIntersection = (entries) => { + entries.forEach(entry => { + const id = entry.target.getAttribute('data-id'); + if (this.callbacks[id]) { + this.callbacks[id](entry); + } + }); + }; + + this.observer = new IntersectionObserver(onIntersection, options); + this.observerBacklog.forEach(([ id, node, callback ]) => { + this.observe(id, node, callback); + }); + this.observerBacklog = null; + } + + observe (id, node, callback) { + if (!this.observer) { + this.observerBacklog.push([ id, node, callback ]); + } else { + this.callbacks[id] = callback; + this.observer.observe(node); + } + } + + unobserve (id, node) { + if (this.observer) { + delete this.callbacks[id]; + this.observer.unobserve(node); + } + } + + disconnect () { + if (this.observer) { + this.callbacks = {}; + this.observer.disconnect(); + this.observer = null; + } + } + +} + +export default IntersectionObserverWrapper; diff --git a/app/javascript/flavours/glitch/features/ui/util/optional_motion.js b/app/javascript/flavours/glitch/features/ui/util/optional_motion.js new file mode 100644 index 000000000..a7fbe6310 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/optional_motion.js @@ -0,0 +1,5 @@ +import { reduceMotion } from 'flavours/glitch/initial_state'; +import ReducedMotion from './reduced_motion'; +import Motion from 'react-motion/lib/Motion'; + +export default reduceMotion ? ReducedMotion : Motion; diff --git a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js new file mode 100644 index 000000000..e36c512f3 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js @@ -0,0 +1,69 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Switch, Route } from 'react-router-dom'; + +import ColumnLoading from 'flavours/glitch/features/ui/components/column_loading'; +import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error'; +import BundleContainer from 'flavours/glitch/features/ui/containers/bundle_container'; + +// Small wrapper to pass multiColumn to the route components +export class WrappedSwitch extends React.PureComponent { + + render () { + const { multiColumn, children } = this.props; + + return ( + + {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))} + + ); + } + +} + +WrappedSwitch.propTypes = { + multiColumn: PropTypes.bool, + children: PropTypes.node, +}; + +// Small Wraper to extract the params from the route and pass +// them to the rendered component, together with the content to +// be rendered inside (the children) +export class WrappedRoute extends React.Component { + + static propTypes = { + component: PropTypes.func.isRequired, + content: PropTypes.node, + multiColumn: PropTypes.bool, + componentParams: PropTypes.object, + } + + static defaultProps = { + componentParams: {}, + }; + + renderComponent = ({ match }) => { + const { component, content, multiColumn, componentParams } = this.props; + + return ( + + {Component => {content}} + + ); + } + + renderLoading = () => { + return ; + } + + renderError = (props) => { + return ; + } + + render () { + const { component: Component, content, ...rest } = this.props; + + return ; + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js new file mode 100644 index 000000000..95519042b --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js @@ -0,0 +1,44 @@ +// Like react-motion's Motion, but reduces all animations to cross-fades +// for the benefit of users with motion sickness. +import React from 'react'; +import Motion from 'react-motion/lib/Motion'; +import PropTypes from 'prop-types'; + +const stylesToKeep = ['opacity', 'backgroundOpacity']; + +const extractValue = (value) => { + // This is either an object with a "val" property or it's a number + return (typeof value === 'object' && value && 'val' in value) ? value.val : value; +}; + +class ReducedMotion extends React.Component { + + static propTypes = { + defaultStyle: PropTypes.object, + style: PropTypes.object, + children: PropTypes.func, + } + + render() { + + const { style, defaultStyle, children } = this.props; + + Object.keys(style).forEach(key => { + if (stylesToKeep.includes(key)) { + return; + } + // If it's setting an x or height or scale or some other value, we need + // to preserve the end-state value without actually animating it + style[key] = defaultStyle[key] = extractValue(style[key]); + }); + + return ( + + {children} + + ); + } + +} + +export default ReducedMotion; diff --git a/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js b/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js new file mode 100644 index 000000000..b04d4a8ee --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/schedule_idle_task.js @@ -0,0 +1,29 @@ +// Wrapper to call requestIdleCallback() to schedule low-priority work. +// See https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API +// for a good breakdown of the concepts behind this. + +import Queue from 'tiny-queue'; + +const taskQueue = new Queue(); +let runningRequestIdleCallback = false; + +function runTasks(deadline) { + while (taskQueue.length && deadline.timeRemaining() > 0) { + taskQueue.shift()(); + } + if (taskQueue.length) { + requestIdleCallback(runTasks); + } else { + runningRequestIdleCallback = false; + } +} + +function scheduleIdleTask(task) { + taskQueue.push(task); + if (!runningRequestIdleCallback) { + runningRequestIdleCallback = true; + requestIdleCallback(runTasks); + } +} + +export default scheduleIdleTask; -- cgit From 7097a459be125f0c18bc4c2c77a00ba92c408bf9 Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 11 Oct 2022 11:23:59 +0200 Subject: Move flavours/glitch/utils/ready to flavours/glitch/ready --- .../glitch/components/media_attachments.js | 2 +- .../flavours/glitch/components/status.js | 2 +- .../flavours/glitch/features/emoji_picker/index.js | 2 +- .../glitch/features/ui/components/columns_area.js | 2 +- .../features/ui/components/focal_point_modal.js | 2 +- .../glitch/features/ui/components/modal_root.js | 2 +- .../flavours/glitch/features/ui/index.js | 2 +- .../glitch/features/ui/util/async-components.js | 183 +++++++++++++++++++++ app/javascript/flavours/glitch/packs/about.js | 2 +- app/javascript/flavours/glitch/packs/admin.js | 2 +- app/javascript/flavours/glitch/packs/error.js | 2 +- app/javascript/flavours/glitch/packs/public.js | 2 +- app/javascript/flavours/glitch/packs/settings.js | 2 +- app/javascript/flavours/glitch/packs/share.js | 2 +- app/javascript/flavours/glitch/ready.js | 32 ++++ .../flavours/glitch/utils/async-components.js | 183 --------------------- app/javascript/flavours/glitch/utils/config.js | 2 +- app/javascript/flavours/glitch/utils/main.js | 2 +- app/javascript/flavours/glitch/utils/ready.js | 32 ---- 19 files changed, 230 insertions(+), 230 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/ui/util/async-components.js create mode 100644 app/javascript/flavours/glitch/ready.js delete mode 100644 app/javascript/flavours/glitch/utils/async-components.js delete mode 100644 app/javascript/flavours/glitch/utils/ready.js (limited to 'app/javascript/flavours/glitch/features/ui/util') diff --git a/app/javascript/flavours/glitch/components/media_attachments.js b/app/javascript/flavours/glitch/components/media_attachments.js index dec262435..a517fcf30 100644 --- a/app/javascript/flavours/glitch/components/media_attachments.js +++ b/app/javascript/flavours/glitch/components/media_attachments.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { MediaGallery, Video, Audio } from 'flavours/glitch/utils/async-components'; +import { MediaGallery, Video, Audio } from 'flavours/glitch/features/ui/util/async-components'; import Bundle from 'flavours/glitch/features/ui/components/bundle'; import noop from 'lodash/noop'; diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 9748f2746..6cdc4b971 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -10,7 +10,7 @@ import AttachmentList from './attachment_list'; import Card from '../features/status/components/card'; import { injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { MediaGallery, Video, Audio } from 'flavours/glitch/utils/async-components'; +import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; import { HotKeys } from 'react-hotkeys'; import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container'; import classNames from 'classnames'; diff --git a/app/javascript/flavours/glitch/features/emoji_picker/index.js b/app/javascript/flavours/glitch/features/emoji_picker/index.js index bf58441f3..97032e107 100644 --- a/app/javascript/flavours/glitch/features/emoji_picker/index.js +++ b/app/javascript/flavours/glitch/features/emoji_picker/index.js @@ -6,7 +6,7 @@ import { useEmoji } from 'flavours/glitch/actions/emojis'; import React from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { EmojiPicker as EmojiPickerAsync } from 'flavours/glitch/utils/async-components'; +import { EmojiPicker as EmojiPickerAsync } from '../ui/util/async-components'; import Overlay from 'react-overlays/lib/Overlay'; import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js index 0196f691a..b38fe4b17 100644 --- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js +++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js @@ -26,7 +26,7 @@ import { BookmarkedStatuses, ListTimeline, Directory, -} from 'flavours/glitch/utils/async-components'; +} from '../../ui/util/async-components'; import Icon from 'flavours/glitch/components/icon'; import ComposePanel from './compose_panel'; import NavigationPanel from './navigation_panel'; diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js index c31a4822d..de330b3a1 100644 --- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js @@ -15,7 +15,7 @@ import Textarea from 'react-textarea-autosize'; import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress'; import CharacterCounter from 'flavours/glitch/features/compose/components/character_counter'; import { length } from 'stringz'; -import { Tesseract as fetchTesseract } from 'flavours/glitch/utils/async-components'; +import { Tesseract as fetchTesseract } from 'flavours/glitch/features/ui/util/async-components'; import GIFV from 'flavours/glitch/components/gifv'; import { me } from 'flavours/glitch/initial_state'; // eslint-disable-next-line import/no-extraneous-dependencies diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index dc612aea2..cedfabe03 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -29,7 +29,7 @@ import { PinnedAccountsEditor, CompareHistoryModal, FilterModal, -} from 'flavours/glitch/utils/async-components'; +} from 'flavours/glitch/features/ui/util/async-components'; const MODAL_COMPONENTS = { 'MEDIA': () => Promise.resolve({ default: MediaModal }), diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index f77e89591..3c679d394 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -52,7 +52,7 @@ import { Directory, Explore, FollowRecommendations, -} from 'flavours/glitch/utils/async-components'; +} from './util/async-components'; import { HotKeys } from 'react-hotkeys'; import { me, title } from 'flavours/glitch/initial_state'; import { closeOnboarding, INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding'; diff --git a/app/javascript/flavours/glitch/features/ui/util/async-components.js b/app/javascript/flavours/glitch/features/ui/util/async-components.js new file mode 100644 index 000000000..325efe74c --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/util/async-components.js @@ -0,0 +1,183 @@ +export function EmojiPicker () { + return import(/* webpackChunkName: "flavours/glitch/async/emoji_picker" */'flavours/glitch/utils/emoji/emoji_picker'); +} + +export function Compose () { + return import(/* webpackChunkName: "flavours/glitch/async/compose" */'flavours/glitch/features/compose'); +} + +export function Notifications () { + return import(/* webpackChunkName: "flavours/glitch/async/notifications" */'flavours/glitch/features/notifications'); +} + +export function HomeTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/home_timeline" */'flavours/glitch/features/home_timeline'); +} + +export function PublicTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/public_timeline" */'flavours/glitch/features/public_timeline'); +} + +export function CommunityTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/community_timeline" */'flavours/glitch/features/community_timeline'); +} + +export function HashtagTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/hashtag_timeline" */'flavours/glitch/features/hashtag_timeline'); +} + +export function ListTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/list_timeline" */'flavours/glitch/features/list_timeline'); +} + +export function Lists () { + return import(/* webpackChunkName: "flavours/glitch/async/lists" */'flavours/glitch/features/lists'); +} + +export function ListEditor () { + return import(/* webpackChunkName: "flavours/glitch/async/list_editor" */'flavours/glitch/features/list_editor'); +} + +export function PinnedAccountsEditor () { + return import(/* webpackChunkName: "flavours/glitch/async/pinned_accounts_editor" */'flavours/glitch/features/pinned_accounts_editor'); +} + +export function DirectTimeline() { + return import(/* webpackChunkName: "flavours/glitch/async/direct_timeline" */'flavours/glitch/features/direct_timeline'); +} + +export function Status () { + return import(/* webpackChunkName: "flavours/glitch/async/status" */'flavours/glitch/features/status'); +} + +export function GettingStarted () { + return import(/* webpackChunkName: "flavours/glitch/async/getting_started" */'flavours/glitch/features/getting_started'); +} + +export function KeyboardShortcuts () { + return import(/* webpackChunkName: "flavours/glitch/async/keyboard_shortcuts" */'flavours/glitch/features/keyboard_shortcuts'); +} + +export function PinnedStatuses () { + return import(/* webpackChunkName: "flavours/glitch/async/pinned_statuses" */'flavours/glitch/features/pinned_statuses'); +} + +export function AccountTimeline () { + return import(/* webpackChunkName: "flavours/glitch/async/account_timeline" */'flavours/glitch/features/account_timeline'); +} + +export function AccountGallery () { + return import(/* webpackChunkName: "flavours/glitch/async/account_gallery" */'flavours/glitch/features/account_gallery'); +} + +export function Followers () { + return import(/* webpackChunkName: "flavours/glitch/async/followers" */'flavours/glitch/features/followers'); +} + +export function Following () { + return import(/* webpackChunkName: "flavours/glitch/async/following" */'flavours/glitch/features/following'); +} + +export function Reblogs () { + return import(/* webpackChunkName: "flavours/glitch/async/reblogs" */'flavours/glitch/features/reblogs'); +} + +export function Favourites () { + return import(/* webpackChunkName: "flavours/glitch/async/favourites" */'flavours/glitch/features/favourites'); +} + +export function FollowRequests () { + return import(/* webpackChunkName: "flavours/glitch/async/follow_requests" */'flavours/glitch/features/follow_requests'); +} + +export function GenericNotFound () { + return import(/* webpackChunkName: "flavours/glitch/async/generic_not_found" */'flavours/glitch/features/generic_not_found'); +} + +export function FavouritedStatuses () { + return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'flavours/glitch/features/favourited_statuses'); +} + +export function BookmarkedStatuses () { + return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'flavours/glitch/features/bookmarked_statuses'); +} + +export function Blocks () { + return import(/* webpackChunkName: "flavours/glitch/async/blocks" */'flavours/glitch/features/blocks'); +} + +export function DomainBlocks () { + return import(/* webpackChunkName: "flavours/glitch/async/domain_blocks" */'flavours/glitch/features/domain_blocks'); +} + +export function Mutes () { + return import(/* webpackChunkName: "flavours/glitch/async/mutes" */'flavours/glitch/features/mutes'); +} + +export function OnboardingModal () { + return import(/* webpackChunkName: "flavours/glitch/async/onboarding_modal" */'flavours/glitch/features/ui/components/onboarding_modal'); +} + +export function MuteModal () { + return import(/* webpackChunkName: "flavours/glitch/async/mute_modal" */'flavours/glitch/features/ui/components/mute_modal'); +} + +export function BlockModal () { + return import(/* webpackChunkName: "flavours/glitch/async/block_modal" */'flavours/glitch/features/ui/components/block_modal'); +} + +export function ReportModal () { + return import(/* webpackChunkName: "flavours/glitch/async/report_modal" */'flavours/glitch/features/ui/components/report_modal'); +} + +export function SettingsModal () { + return import(/* webpackChunkName: "flavours/glitch/async/settings_modal" */'flavours/glitch/features/local_settings'); +} + +export function MediaGallery () { + return import(/* webpackChunkName: "flavours/glitch/async/media_gallery" */'flavours/glitch/components/media_gallery'); +} + +export function Video () { + return import(/* webpackChunkName: "flavours/glitch/async/video" */'flavours/glitch/features/video'); +} + +export function Audio () { + return import(/* webpackChunkName: "features/glitch/async/audio" */'flavours/glitch/features/audio'); +} + +export function EmbedModal () { + return import(/* webpackChunkName: "flavours/glitch/async/embed_modal" */'flavours/glitch/features/ui/components/embed_modal'); +} + +export function GettingStartedMisc () { + return import(/* webpackChunkName: "flavours/glitch/async/getting_started_misc" */'flavours/glitch/features/getting_started_misc'); +} + +export function ListAdder () { + return import(/* webpackChunkName: "features/glitch/async/list_adder" */'flavours/glitch/features/list_adder'); +} + +export function Tesseract () { + return import(/*webpackChunkName: "tesseract" */'tesseract.js'); +} + +export function Directory () { + return import(/* webpackChunkName: "features/glitch/async/directory" */'flavours/glitch/features/directory'); +} + +export function FollowRecommendations () { + return import(/* webpackChunkName: "features/glitch/async/follow_recommendations" */'flavours/glitch/features/follow_recommendations'); +} + +export function CompareHistoryModal () { + return import(/*webpackChunkName: "flavours/glitch/async/compare_history_modal" */'flavours/glitch/features/ui/components/compare_history_modal'); +} + +export function FilterModal () { + return import(/*webpackChunkName: "flavours/glitch/async/filter_modal" */'flavours/glitch/features/ui/components/filter_modal'); +} + +export function Explore () { + return import(/* webpackChunkName: "flavours/glitch/async/explore" */'flavours/glitch/features/explore'); +} diff --git a/app/javascript/flavours/glitch/packs/about.js b/app/javascript/flavours/glitch/packs/about.js index 87c8220ff..50e789946 100644 --- a/app/javascript/flavours/glitch/packs/about.js +++ b/app/javascript/flavours/glitch/packs/about.js @@ -14,7 +14,7 @@ function loaded() { } function main() { - const ready = require('flavours/glitch/utils/ready').default; + const ready = require('flavours/glitch/ready').default; ready(loaded); } diff --git a/app/javascript/flavours/glitch/packs/admin.js b/app/javascript/flavours/glitch/packs/admin.js index a866da742..56cdfc30a 100644 --- a/app/javascript/flavours/glitch/packs/admin.js +++ b/app/javascript/flavours/glitch/packs/admin.js @@ -1,5 +1,5 @@ import 'packs/public-path'; -import ready from 'flavours/glitch/utils/ready'; +import ready from 'flavours/glitch/ready'; ready(() => { const React = require('react'); diff --git a/app/javascript/flavours/glitch/packs/error.js b/app/javascript/flavours/glitch/packs/error.js index 424029cae..f13e32149 100644 --- a/app/javascript/flavours/glitch/packs/error.js +++ b/app/javascript/flavours/glitch/packs/error.js @@ -1,5 +1,5 @@ import 'packs/public-path'; -import ready from 'flavours/glitch/utils/ready'; +import ready from 'flavours/glitch/ready'; ready(() => { const image = document.querySelector('img'); diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js index bf1c5816b..d920acc00 100644 --- a/app/javascript/flavours/glitch/packs/public.js +++ b/app/javascript/flavours/glitch/packs/public.js @@ -1,6 +1,6 @@ import 'packs/public-path'; import loadPolyfills from 'flavours/glitch/utils/load_polyfills'; -import ready from 'flavours/glitch/utils/ready'; +import ready from 'flavours/glitch/ready'; import loadKeyboardExtensions from 'flavours/glitch/utils/load_keyboard_extensions'; function main() { diff --git a/app/javascript/flavours/glitch/packs/settings.js b/app/javascript/flavours/glitch/packs/settings.js index 944e74d0a..80f53bc78 100644 --- a/app/javascript/flavours/glitch/packs/settings.js +++ b/app/javascript/flavours/glitch/packs/settings.js @@ -1,6 +1,6 @@ import 'packs/public-path'; import loadPolyfills from 'flavours/glitch/utils/load_polyfills'; -import ready from 'flavours/glitch/utils/ready'; +import ready from 'flavours/glitch/ready'; import loadKeyboardExtensions from 'flavours/glitch/utils/load_keyboard_extensions'; import 'cocoon-js-vanilla'; diff --git a/app/javascript/flavours/glitch/packs/share.js b/app/javascript/flavours/glitch/packs/share.js index 08fc699bb..49c8545e7 100644 --- a/app/javascript/flavours/glitch/packs/share.js +++ b/app/javascript/flavours/glitch/packs/share.js @@ -14,7 +14,7 @@ function loaded() { } function main() { - const ready = require('flavours/glitch/utils/ready').default; + const ready = require('flavours/glitch/ready').default; ready(loaded); } diff --git a/app/javascript/flavours/glitch/ready.js b/app/javascript/flavours/glitch/ready.js new file mode 100644 index 000000000..e769cc756 --- /dev/null +++ b/app/javascript/flavours/glitch/ready.js @@ -0,0 +1,32 @@ +// @ts-check + +/** + * @param {(() => void) | (() => Promise)} callback + * @returns {Promise} + */ +export default function ready(callback) { + return new Promise((resolve, reject) => { + function loaded() { + let result; + try { + result = callback(); + } catch (err) { + reject(err); + + return; + } + + if (typeof result?.then === 'function') { + result.then(resolve).catch(reject); + } else { + resolve(); + } + } + + if (['interactive', 'complete'].includes(document.readyState)) { + loaded(); + } else { + document.addEventListener('DOMContentLoaded', loaded); + } + }); +} diff --git a/app/javascript/flavours/glitch/utils/async-components.js b/app/javascript/flavours/glitch/utils/async-components.js deleted file mode 100644 index 325efe74c..000000000 --- a/app/javascript/flavours/glitch/utils/async-components.js +++ /dev/null @@ -1,183 +0,0 @@ -export function EmojiPicker () { - return import(/* webpackChunkName: "flavours/glitch/async/emoji_picker" */'flavours/glitch/utils/emoji/emoji_picker'); -} - -export function Compose () { - return import(/* webpackChunkName: "flavours/glitch/async/compose" */'flavours/glitch/features/compose'); -} - -export function Notifications () { - return import(/* webpackChunkName: "flavours/glitch/async/notifications" */'flavours/glitch/features/notifications'); -} - -export function HomeTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/home_timeline" */'flavours/glitch/features/home_timeline'); -} - -export function PublicTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/public_timeline" */'flavours/glitch/features/public_timeline'); -} - -export function CommunityTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/community_timeline" */'flavours/glitch/features/community_timeline'); -} - -export function HashtagTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/hashtag_timeline" */'flavours/glitch/features/hashtag_timeline'); -} - -export function ListTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/list_timeline" */'flavours/glitch/features/list_timeline'); -} - -export function Lists () { - return import(/* webpackChunkName: "flavours/glitch/async/lists" */'flavours/glitch/features/lists'); -} - -export function ListEditor () { - return import(/* webpackChunkName: "flavours/glitch/async/list_editor" */'flavours/glitch/features/list_editor'); -} - -export function PinnedAccountsEditor () { - return import(/* webpackChunkName: "flavours/glitch/async/pinned_accounts_editor" */'flavours/glitch/features/pinned_accounts_editor'); -} - -export function DirectTimeline() { - return import(/* webpackChunkName: "flavours/glitch/async/direct_timeline" */'flavours/glitch/features/direct_timeline'); -} - -export function Status () { - return import(/* webpackChunkName: "flavours/glitch/async/status" */'flavours/glitch/features/status'); -} - -export function GettingStarted () { - return import(/* webpackChunkName: "flavours/glitch/async/getting_started" */'flavours/glitch/features/getting_started'); -} - -export function KeyboardShortcuts () { - return import(/* webpackChunkName: "flavours/glitch/async/keyboard_shortcuts" */'flavours/glitch/features/keyboard_shortcuts'); -} - -export function PinnedStatuses () { - return import(/* webpackChunkName: "flavours/glitch/async/pinned_statuses" */'flavours/glitch/features/pinned_statuses'); -} - -export function AccountTimeline () { - return import(/* webpackChunkName: "flavours/glitch/async/account_timeline" */'flavours/glitch/features/account_timeline'); -} - -export function AccountGallery () { - return import(/* webpackChunkName: "flavours/glitch/async/account_gallery" */'flavours/glitch/features/account_gallery'); -} - -export function Followers () { - return import(/* webpackChunkName: "flavours/glitch/async/followers" */'flavours/glitch/features/followers'); -} - -export function Following () { - return import(/* webpackChunkName: "flavours/glitch/async/following" */'flavours/glitch/features/following'); -} - -export function Reblogs () { - return import(/* webpackChunkName: "flavours/glitch/async/reblogs" */'flavours/glitch/features/reblogs'); -} - -export function Favourites () { - return import(/* webpackChunkName: "flavours/glitch/async/favourites" */'flavours/glitch/features/favourites'); -} - -export function FollowRequests () { - return import(/* webpackChunkName: "flavours/glitch/async/follow_requests" */'flavours/glitch/features/follow_requests'); -} - -export function GenericNotFound () { - return import(/* webpackChunkName: "flavours/glitch/async/generic_not_found" */'flavours/glitch/features/generic_not_found'); -} - -export function FavouritedStatuses () { - return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'flavours/glitch/features/favourited_statuses'); -} - -export function BookmarkedStatuses () { - return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'flavours/glitch/features/bookmarked_statuses'); -} - -export function Blocks () { - return import(/* webpackChunkName: "flavours/glitch/async/blocks" */'flavours/glitch/features/blocks'); -} - -export function DomainBlocks () { - return import(/* webpackChunkName: "flavours/glitch/async/domain_blocks" */'flavours/glitch/features/domain_blocks'); -} - -export function Mutes () { - return import(/* webpackChunkName: "flavours/glitch/async/mutes" */'flavours/glitch/features/mutes'); -} - -export function OnboardingModal () { - return import(/* webpackChunkName: "flavours/glitch/async/onboarding_modal" */'flavours/glitch/features/ui/components/onboarding_modal'); -} - -export function MuteModal () { - return import(/* webpackChunkName: "flavours/glitch/async/mute_modal" */'flavours/glitch/features/ui/components/mute_modal'); -} - -export function BlockModal () { - return import(/* webpackChunkName: "flavours/glitch/async/block_modal" */'flavours/glitch/features/ui/components/block_modal'); -} - -export function ReportModal () { - return import(/* webpackChunkName: "flavours/glitch/async/report_modal" */'flavours/glitch/features/ui/components/report_modal'); -} - -export function SettingsModal () { - return import(/* webpackChunkName: "flavours/glitch/async/settings_modal" */'flavours/glitch/features/local_settings'); -} - -export function MediaGallery () { - return import(/* webpackChunkName: "flavours/glitch/async/media_gallery" */'flavours/glitch/components/media_gallery'); -} - -export function Video () { - return import(/* webpackChunkName: "flavours/glitch/async/video" */'flavours/glitch/features/video'); -} - -export function Audio () { - return import(/* webpackChunkName: "features/glitch/async/audio" */'flavours/glitch/features/audio'); -} - -export function EmbedModal () { - return import(/* webpackChunkName: "flavours/glitch/async/embed_modal" */'flavours/glitch/features/ui/components/embed_modal'); -} - -export function GettingStartedMisc () { - return import(/* webpackChunkName: "flavours/glitch/async/getting_started_misc" */'flavours/glitch/features/getting_started_misc'); -} - -export function ListAdder () { - return import(/* webpackChunkName: "features/glitch/async/list_adder" */'flavours/glitch/features/list_adder'); -} - -export function Tesseract () { - return import(/*webpackChunkName: "tesseract" */'tesseract.js'); -} - -export function Directory () { - return import(/* webpackChunkName: "features/glitch/async/directory" */'flavours/glitch/features/directory'); -} - -export function FollowRecommendations () { - return import(/* webpackChunkName: "features/glitch/async/follow_recommendations" */'flavours/glitch/features/follow_recommendations'); -} - -export function CompareHistoryModal () { - return import(/*webpackChunkName: "flavours/glitch/async/compare_history_modal" */'flavours/glitch/features/ui/components/compare_history_modal'); -} - -export function FilterModal () { - return import(/*webpackChunkName: "flavours/glitch/async/filter_modal" */'flavours/glitch/features/ui/components/filter_modal'); -} - -export function Explore () { - return import(/* webpackChunkName: "flavours/glitch/async/explore" */'flavours/glitch/features/explore'); -} diff --git a/app/javascript/flavours/glitch/utils/config.js b/app/javascript/flavours/glitch/utils/config.js index c3e2b73ae..932cd0cbf 100644 --- a/app/javascript/flavours/glitch/utils/config.js +++ b/app/javascript/flavours/glitch/utils/config.js @@ -1,4 +1,4 @@ -import ready from './ready'; +import ready from '../ready'; export let assetHost = ''; diff --git a/app/javascript/flavours/glitch/utils/main.js b/app/javascript/flavours/glitch/utils/main.js index 0601e7a76..39a46db00 100644 --- a/app/javascript/flavours/glitch/utils/main.js +++ b/app/javascript/flavours/glitch/utils/main.js @@ -2,7 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { setupBrowserNotifications } from 'flavours/glitch/actions/notifications'; import Mastodon, { store } from 'flavours/glitch/containers/mastodon'; -import ready from 'flavours/glitch/utils/ready'; +import ready from 'flavours/glitch/ready'; const perf = require('flavours/glitch/utils/performance'); diff --git a/app/javascript/flavours/glitch/utils/ready.js b/app/javascript/flavours/glitch/utils/ready.js deleted file mode 100644 index e769cc756..000000000 --- a/app/javascript/flavours/glitch/utils/ready.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-check - -/** - * @param {(() => void) | (() => Promise)} callback - * @returns {Promise} - */ -export default function ready(callback) { - return new Promise((resolve, reject) => { - function loaded() { - let result; - try { - result = callback(); - } catch (err) { - reject(err); - - return; - } - - if (typeof result?.then === 'function') { - result.then(resolve).catch(reject); - } else { - resolve(); - } - } - - if (['interactive', 'complete'].includes(document.readyState)) { - loaded(); - } else { - document.addEventListener('DOMContentLoaded', loaded); - } - }); -} -- cgit From b728b25c102d10ac9447fee27a2b667e58f78b9b Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 11 Oct 2022 12:36:24 +0200 Subject: Move flavours/glitch/utils/emoji back to flavours/glitch/features/emoji --- app/javascript/flavours/glitch/actions/compose.js | 2 +- .../flavours/glitch/actions/importer/normalizer.js | 2 +- .../glitch/components/autosuggest_emoji.js | 2 +- app/javascript/flavours/glitch/components/poll.js | 2 +- .../flavours/glitch/features/emoji/emoji.js | 116 +++++++++ .../glitch/features/emoji/emoji_compressed.js | 122 ++++++++++ .../flavours/glitch/features/emoji/emoji_map.json | 1 + .../glitch/features/emoji/emoji_mart_data_light.js | 41 ++++ .../features/emoji/emoji_mart_search_light.js | 185 +++++++++++++++ .../flavours/glitch/features/emoji/emoji_picker.js | 7 + .../features/emoji/emoji_unicode_mapping_light.js | 35 +++ .../flavours/glitch/features/emoji/emoji_utils.js | 258 +++++++++++++++++++++ .../glitch/features/emoji/unicode_to_filename.js | 26 +++ .../features/emoji/unicode_to_unified_name.js | 21 ++ .../flavours/glitch/features/emoji_picker/index.js | 2 +- .../getting_started/components/announcements.js | 2 +- .../ui/components/compare_history_modal.js | 2 +- .../glitch/features/ui/util/async-components.js | 2 +- app/javascript/flavours/glitch/packs/public.js | 2 +- .../flavours/glitch/reducers/custom_emojis.js | 4 +- .../glitch/utils/emoji/emoji_compressed.js | 122 ---------- .../flavours/glitch/utils/emoji/emoji_map.json | 1 - .../glitch/utils/emoji/emoji_mart_data_light.js | 41 ---- .../glitch/utils/emoji/emoji_mart_search_light.js | 185 --------------- .../flavours/glitch/utils/emoji/emoji_picker.js | 7 - .../utils/emoji/emoji_unicode_mapping_light.js | 35 --- .../flavours/glitch/utils/emoji/emoji_utils.js | 258 --------------------- .../flavours/glitch/utils/emoji/index.js | 116 --------- .../glitch/utils/emoji/unicode_to_filename.js | 26 --- .../glitch/utils/emoji/unicode_to_unified_name.js | 21 -- 30 files changed, 823 insertions(+), 823 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_compressed.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_map.json create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_mart_data_light.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_mart_search_light.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_picker.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_unicode_mapping_light.js create mode 100644 app/javascript/flavours/glitch/features/emoji/emoji_utils.js create mode 100644 app/javascript/flavours/glitch/features/emoji/unicode_to_filename.js create mode 100644 app/javascript/flavours/glitch/features/emoji/unicode_to_unified_name.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_compressed.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_map.json delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_mart_data_light.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_mart_search_light.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_picker.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_unicode_mapping_light.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/emoji_utils.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/index.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/unicode_to_filename.js delete mode 100644 app/javascript/flavours/glitch/utils/emoji/unicode_to_unified_name.js (limited to 'app/javascript/flavours/glitch/features/ui/util') diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js index 0ef505363..02aa4f144 100644 --- a/app/javascript/flavours/glitch/actions/compose.js +++ b/app/javascript/flavours/glitch/actions/compose.js @@ -1,7 +1,7 @@ import api from '../api'; import { CancelToken, isCancel } from 'axios'; import { throttle } from 'lodash'; -import { search as emojiSearch } from 'flavours/glitch/utils/emoji/emoji_mart_search_light'; +import { search as emojiSearch } from 'flavours/glitch/features/emoji/emoji_mart_search_light'; import { useEmoji } from './emojis'; import { tagHistory } from '../settings'; import { recoverHashtags } from 'flavours/glitch/utils/hashtag'; diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js index bf1e129a2..1c9f524e4 100644 --- a/app/javascript/flavours/glitch/actions/importer/normalizer.js +++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js @@ -1,5 +1,5 @@ import escapeTextContentForBrowser from 'escape-html'; -import emojify from 'flavours/glitch/utils/emoji'; +import emojify from 'flavours/glitch/features/emoji/emoji'; import { unescapeHTML } from 'flavours/glitch/utils/html'; import { autoHideCW } from 'flavours/glitch/utils/content_warning'; diff --git a/app/javascript/flavours/glitch/components/autosuggest_emoji.js b/app/javascript/flavours/glitch/components/autosuggest_emoji.js index dd756544b..83fafbd10 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_emoji.js +++ b/app/javascript/flavours/glitch/components/autosuggest_emoji.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import unicodeMapping from 'flavours/glitch/utils/emoji/emoji_unicode_mapping_light'; +import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light'; import { assetHost } from 'flavours/glitch/utils/config'; diff --git a/app/javascript/flavours/glitch/components/poll.js b/app/javascript/flavours/glitch/components/poll.js index 6dfa73f49..da65cd241 100644 --- a/app/javascript/flavours/glitch/components/poll.js +++ b/app/javascript/flavours/glitch/components/poll.js @@ -7,7 +7,7 @@ import classNames from 'classnames'; import Motion from 'flavours/glitch/features/ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import escapeTextContentForBrowser from 'escape-html'; -import emojify from 'flavours/glitch/utils/emoji'; +import emojify from 'flavours/glitch/features/emoji/emoji'; import RelativeTimestamp from './relative_timestamp'; import Icon from 'flavours/glitch/components/icon'; diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js new file mode 100644 index 000000000..c4e2c26f2 --- /dev/null +++ b/app/javascript/flavours/glitch/features/emoji/emoji.js @@ -0,0 +1,116 @@ +import { autoPlayGif, useSystemEmojiFont } from 'flavours/glitch/initial_state'; +import unicodeMapping from './emoji_unicode_mapping_light'; +import { assetHost } from 'flavours/glitch/utils/config'; +import Trie from 'substring-trie'; + +const trie = new Trie(Object.keys(unicodeMapping)); + +// Convert to file names from emojis. (For different variation selector emojis) +const emojiFilenames = (emojis) => { + return emojis.map(v => unicodeMapping[v].filename); +}; + +// Emoji requiring extra borders depending on theme +const darkEmoji = emojiFilenames(['๐ŸŽฑ', '๐Ÿœ', 'โšซ', '๐Ÿ–ค', 'โฌ›', 'โ—ผ๏ธ', 'โ—พ', 'โ—ผ๏ธ', 'โœ’๏ธ', 'โ–ช๏ธ', '๐Ÿ’ฃ', '๐ŸŽณ', '๐Ÿ“ท', '๐Ÿ“ธ', 'โ™ฃ๏ธ', '๐Ÿ•ถ๏ธ', 'โœด๏ธ', '๐Ÿ”Œ', '๐Ÿ’‚โ€โ™€๏ธ', '๐Ÿ“ฝ๏ธ', '๐Ÿณ', '๐Ÿฆ', '๐Ÿ’‚', '๐Ÿ”ช', '๐Ÿ•ณ๏ธ', '๐Ÿ•น๏ธ', '๐Ÿ•‹', '๐Ÿ–Š๏ธ', '๐Ÿ–‹๏ธ', '๐Ÿ’‚โ€โ™‚๏ธ', '๐ŸŽค', '๐ŸŽ“', '๐ŸŽฅ', '๐ŸŽผ', 'โ™ ๏ธ', '๐ŸŽฉ', '๐Ÿฆƒ', '๐Ÿ“ผ', '๐Ÿ“น', '๐ŸŽฎ', '๐Ÿƒ', '๐Ÿด', '๐Ÿž', '๐Ÿ•บ', '๐Ÿ“ฑ', '๐Ÿ“ฒ', '๐Ÿšฒ']); +const lightEmoji = emojiFilenames(['๐Ÿ‘ฝ', 'โšพ', '๐Ÿ”', 'โ˜๏ธ', '๐Ÿ’จ', '๐Ÿ•Š๏ธ', '๐Ÿ‘€', '๐Ÿฅ', '๐Ÿ‘ป', '๐Ÿ', 'โ•', 'โ”', 'โ›ธ๏ธ', '๐ŸŒฉ๏ธ', '๐Ÿ”Š', '๐Ÿ”‡', '๐Ÿ“ƒ', '๐ŸŒง๏ธ', '๐Ÿ', '๐Ÿš', '๐Ÿ™', '๐Ÿ“', '๐Ÿ‘', '๐Ÿ’€', 'โ˜ ๏ธ', '๐ŸŒจ๏ธ', '๐Ÿ”‰', '๐Ÿ”ˆ', '๐Ÿ’ฌ', '๐Ÿ’ญ', '๐Ÿ', '๐Ÿณ๏ธ', 'โšช', 'โฌœ', 'โ—ฝ', 'โ—ป๏ธ', 'โ–ซ๏ธ']); + +const emojiFilename = (filename) => { + const borderedEmoji = (document.body && document.body.classList.contains('skin-mastodon-light')) ? lightEmoji : darkEmoji; + return borderedEmoji.includes(filename) ? (filename + '_border') : filename; +}; + +const emojify = (str, customEmojis = {}) => { + const tagCharsWithoutEmojis = '<&'; + const tagCharsWithEmojis = Object.keys(customEmojis).length ? '<&:' : '<&'; + let rtn = '', tagChars = tagCharsWithEmojis, invisible = 0; + for (;;) { + let match, i = 0, tag; + while (i < str.length && (tag = tagChars.indexOf(str[i])) === -1 && (invisible || useSystemEmojiFont || !(match = trie.search(str.slice(i))))) { + i += str.codePointAt(i) < 65536 ? 1 : 2; + } + let rend, replacement = ''; + if (i === str.length) { + break; + } else if (str[i] === ':') { + if (!(() => { + rend = str.indexOf(':', i + 1) + 1; + if (!rend) return false; // no pair of ':' + const lt = str.indexOf('<', i + 1); + if (!(lt === -1 || lt >= rend)) return false; // tag appeared before closing ':' + const shortname = str.slice(i, rend); + // now got a replacee as ':shortname:' + // if you want additional emoji handler, add statements below which set replacement and return true. + if (shortname in customEmojis) { + const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url; + replacement = `${shortname}`; + return true; + } + return false; + })()) rend = ++i; + } else if (tag >= 0) { // <, & + rend = str.indexOf('>;'[tag], i + 1) + 1; + if (!rend) { + break; + } + if (tag === 0) { + if (invisible) { + if (str[i + 1] === '/') { // closing tag + if (!--invisible) { + tagChars = tagCharsWithEmojis; + } + } else if (str[rend - 2] !== '/') { // opening tag + invisible++; + } + } else { + if (str.startsWith('