kopia lustrzana https://github.com/cheeaun/phanpy
Porównaj commity
17 Commity
7100937e79
...
015ed5e7eb
Autor | SHA1 | Data |
---|---|---|
Lim Chee Aun | 015ed5e7eb | |
Lim Chee Aun | 2ad9706304 | |
Lim Chee Aun | 30382d088b | |
Lim Chee Aun | 80196f83ca | |
Lim Chee Aun | 419ad34250 | |
Lim Chee Aun | ed0d714cf2 | |
Lim Chee Aun | 708976a9e9 | |
Lim Chee Aun | d77ba19308 | |
Lim Chee Aun | b10e22a9a2 | |
Lim Chee Aun | 36d8b62e1e | |
Lim Chee Aun | 989e788d8e | |
Lim Chee Aun | ebd9f05f69 | |
Lim Chee Aun | 5246af4ae9 | |
Lim Chee Aun | e6ba72f4c8 | |
Lim Chee Aun | 960dff8b9e | |
Lim Chee Aun | e3c25d25ee | |
Lim Chee Aun | 090320150a |
21
src/app.jsx
21
src/app.jsx
|
@ -1,7 +1,6 @@
|
||||||
import './app.css';
|
import './app.css';
|
||||||
|
|
||||||
import debounce from 'just-debounce-it';
|
import debounce from 'just-debounce-it';
|
||||||
import { lazy, Suspense } from 'preact/compat';
|
|
||||||
import {
|
import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
|
@ -18,14 +17,14 @@ import ComposeButton from './components/compose-button';
|
||||||
import { ICONS } from './components/ICONS';
|
import { ICONS } from './components/ICONS';
|
||||||
import KeyboardShortcutsHelp from './components/keyboard-shortcuts-help';
|
import KeyboardShortcutsHelp from './components/keyboard-shortcuts-help';
|
||||||
import Loader from './components/loader';
|
import Loader from './components/loader';
|
||||||
// import Modals from './components/modals';
|
import Modals from './components/modals';
|
||||||
import NotificationService from './components/notification-service';
|
import NotificationService from './components/notification-service';
|
||||||
import SearchCommand from './components/search-command';
|
import SearchCommand from './components/search-command';
|
||||||
import Shortcuts from './components/shortcuts';
|
import Shortcuts from './components/shortcuts';
|
||||||
import NotFound from './pages/404';
|
import NotFound from './pages/404';
|
||||||
import AccountStatuses from './pages/account-statuses';
|
import AccountStatuses from './pages/account-statuses';
|
||||||
import Bookmarks from './pages/bookmarks';
|
import Bookmarks from './pages/bookmarks';
|
||||||
// import Catchup from './pages/catchup';
|
import Catchup from './pages/catchup';
|
||||||
import Favourites from './pages/favourites';
|
import Favourites from './pages/favourites';
|
||||||
import Filters from './pages/filters';
|
import Filters from './pages/filters';
|
||||||
import FollowedHashtags from './pages/followed-hashtags';
|
import FollowedHashtags from './pages/followed-hashtags';
|
||||||
|
@ -57,9 +56,6 @@ import store from './utils/store';
|
||||||
import { getCurrentAccount } from './utils/store-utils';
|
import { getCurrentAccount } from './utils/store-utils';
|
||||||
import './utils/toast-alert';
|
import './utils/toast-alert';
|
||||||
|
|
||||||
const Catchup = lazy(() => import('./pages/catchup'));
|
|
||||||
const Modals = lazy(() => import('./components/modals'));
|
|
||||||
|
|
||||||
window.__STATES__ = states;
|
window.__STATES__ = states;
|
||||||
window.__STATES_STATS__ = () => {
|
window.__STATES_STATS__ = () => {
|
||||||
const keys = [
|
const keys = [
|
||||||
|
@ -387,9 +383,7 @@ function App() {
|
||||||
)}
|
)}
|
||||||
{isLoggedIn && <ComposeButton />}
|
{isLoggedIn && <ComposeButton />}
|
||||||
{isLoggedIn && <Shortcuts />}
|
{isLoggedIn && <Shortcuts />}
|
||||||
<Suspense>
|
<Modals />
|
||||||
<Modals />
|
|
||||||
</Suspense>
|
|
||||||
{isLoggedIn && <NotificationService />}
|
{isLoggedIn && <NotificationService />}
|
||||||
<BackgroundService isLoggedIn={isLoggedIn} />
|
<BackgroundService isLoggedIn={isLoggedIn} />
|
||||||
{uiState !== 'loading' && <SearchCommand onClose={focusDeck} />}
|
{uiState !== 'loading' && <SearchCommand onClose={focusDeck} />}
|
||||||
|
@ -466,14 +460,7 @@ function SecondaryRoutes({ isLoggedIn }) {
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/fh" element={<FollowedHashtags />} />
|
<Route path="/fh" element={<FollowedHashtags />} />
|
||||||
<Route path="/ft" element={<Filters />} />
|
<Route path="/ft" element={<Filters />} />
|
||||||
<Route
|
<Route path="/catchup" element={<Catchup />} />
|
||||||
path="/catchup"
|
|
||||||
element={
|
|
||||||
<Suspense>
|
|
||||||
<Catchup />
|
|
||||||
</Suspense>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
|
<Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
|
||||||
|
|
|
@ -107,4 +107,5 @@ export const ICONS = {
|
||||||
quote: () => import('@iconify-icons/mingcute/quote-left-line'),
|
quote: () => import('@iconify-icons/mingcute/quote-left-line'),
|
||||||
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
|
settings: () => import('@iconify-icons/mingcute/settings-6-line'),
|
||||||
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
|
'heart-break': () => import('@iconify-icons/mingcute/heart-crack-line'),
|
||||||
|
'user-x': () => import('@iconify-icons/mingcute/user-x-line'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './account-info.css';
|
import './account-info.css';
|
||||||
|
|
||||||
import { Menu, MenuDivider, MenuItem, SubMenu } from '@szhsin/react-menu';
|
import { MenuDivider, MenuItem } from '@szhsin/react-menu';
|
||||||
import {
|
import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
@ -33,7 +33,9 @@ import ListAddEdit from './list-add-edit';
|
||||||
import Loader from './loader';
|
import Loader from './loader';
|
||||||
import Menu2 from './menu2';
|
import Menu2 from './menu2';
|
||||||
import MenuConfirm from './menu-confirm';
|
import MenuConfirm from './menu-confirm';
|
||||||
|
import MenuLink from './menu-link';
|
||||||
import Modal from './modal';
|
import Modal from './modal';
|
||||||
|
import SubMenu2 from './submenu2';
|
||||||
import TranslationBlock from './translation-block';
|
import TranslationBlock from './translation-block';
|
||||||
|
|
||||||
const MUTE_DURATIONS = [
|
const MUTE_DURATIONS = [
|
||||||
|
@ -582,6 +584,15 @@ function AccountInfo({
|
||||||
<Icon icon="external" />
|
<Icon icon="external" />
|
||||||
<span>Go to original profile page</span>
|
<span>Go to original profile page</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuLink href={info.avatar} target="_blank">
|
||||||
|
<Icon icon="user" />
|
||||||
|
<span>View profile image</span>
|
||||||
|
</MenuLink>
|
||||||
|
<MenuLink href={info.header} target="_blank">
|
||||||
|
<Icon icon="media" />
|
||||||
|
<span>View profile header</span>
|
||||||
|
</MenuLink>
|
||||||
</Menu2>
|
</Menu2>
|
||||||
) : (
|
) : (
|
||||||
<AccountBlock
|
<AccountBlock
|
||||||
|
@ -660,6 +671,7 @@ function AccountInfo({
|
||||||
// states.showAccount = false;
|
// states.showAccount = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
states.showGenericAccounts = {
|
states.showGenericAccounts = {
|
||||||
|
id: 'followers',
|
||||||
heading: 'Followers',
|
heading: 'Followers',
|
||||||
fetchAccounts: fetchFollowers,
|
fetchAccounts: fetchFollowers,
|
||||||
instance,
|
instance,
|
||||||
|
@ -1273,7 +1285,7 @@ function RelatedActions({
|
||||||
<span>Unmute @{username}</span>
|
<span>Unmute @{username}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
) : (
|
||||||
<SubMenu
|
<SubMenu2
|
||||||
menuClassName="menu-blur"
|
menuClassName="menu-blur"
|
||||||
openTrigger="clickOnly"
|
openTrigger="clickOnly"
|
||||||
direction="bottom"
|
direction="bottom"
|
||||||
|
@ -1327,7 +1339,44 @@ function RelatedActions({
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</SubMenu>
|
</SubMenu2>
|
||||||
|
)}
|
||||||
|
{followedBy && (
|
||||||
|
<MenuConfirm
|
||||||
|
subMenu
|
||||||
|
menuItemClassName="danger"
|
||||||
|
confirmLabel={
|
||||||
|
<>
|
||||||
|
<Icon icon="user-x" />
|
||||||
|
<span>Remove @{username} from followers?</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
setRelationshipUIState('loading');
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const newRelationship = await currentMasto.v1.accounts
|
||||||
|
.$select(currentInfo?.id || id)
|
||||||
|
.removeFromFollowers();
|
||||||
|
console.log(
|
||||||
|
'removing from followers',
|
||||||
|
newRelationship,
|
||||||
|
);
|
||||||
|
setRelationship(newRelationship);
|
||||||
|
setRelationshipUIState('default');
|
||||||
|
showToast(`@${username} removed from followers`);
|
||||||
|
states.reloadGenericAccounts.id = 'followers';
|
||||||
|
states.reloadGenericAccounts.counter++;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setRelationshipUIState('error');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="user-x" />
|
||||||
|
<span>Remove follower…</span>
|
||||||
|
</MenuConfirm>
|
||||||
)}
|
)}
|
||||||
<MenuConfirm
|
<MenuConfirm
|
||||||
subMenu
|
subMenu
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
export default function CustomEmoji({ staticUrl, alt, url }) {
|
export default function CustomEmoji({ staticUrl, alt, url }) {
|
||||||
return (
|
return (
|
||||||
<picture>
|
<picture>
|
||||||
<source srcset={staticUrl} media="(prefers-reduced-motion: reduce)" />
|
{staticUrl && (
|
||||||
|
<source srcset={staticUrl} media="(prefers-reduced-motion: reduce)" />
|
||||||
|
)}
|
||||||
<img
|
<img
|
||||||
key={alt}
|
key={alt || url}
|
||||||
src={url}
|
src={url}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
class="shortcode-emoji emoji"
|
class="shortcode-emoji emoji"
|
||||||
|
|
|
@ -6,6 +6,15 @@ import Loader from './loader';
|
||||||
|
|
||||||
const supportsIntlSegmenter = !shouldPolyfill();
|
const supportsIntlSegmenter = !shouldPolyfill();
|
||||||
|
|
||||||
|
// Preload IntlSegmenter
|
||||||
|
setTimeout(() => {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (!supportsIntlSegmenter) {
|
||||||
|
import('@formatjs/intl-segmenter/polyfill-force').catch(() => {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
export default function IntlSegmenterSuspense({ children }) {
|
export default function IntlSegmenterSuspense({ children }) {
|
||||||
if (supportsIntlSegmenter) {
|
if (supportsIntlSegmenter) {
|
||||||
return <Suspense fallback={<Loader />}>{children}</Suspense>;
|
return <Suspense fallback={<Loader />}>{children}</Suspense>;
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
/*
|
/*
|
||||||
Rendered but hidden. Only show when visible
|
Rendered but hidden. Only show when visible
|
||||||
*/
|
*/
|
||||||
import { useLayoutEffect, useRef, useState } from 'preact/hooks';
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
|
|
||||||
|
// The sticky header, usually at the top
|
||||||
|
const TOP = 48;
|
||||||
|
|
||||||
export default function LazyShazam({ children }) {
|
export default function LazyShazam({ children }) {
|
||||||
const containerRef = useRef();
|
const containerRef = useRef();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
@ -11,6 +14,7 @@ export default function LazyShazam({ children }) {
|
||||||
|
|
||||||
const { ref } = useInView({
|
const { ref } = useInView({
|
||||||
root: null,
|
root: null,
|
||||||
|
rootMargin: `-${TOP}px 0px 0px 0px`,
|
||||||
trackVisibility: true,
|
trackVisibility: true,
|
||||||
delay: 1000,
|
delay: 1000,
|
||||||
onChange: (inView) => {
|
onChange: (inView) => {
|
||||||
|
@ -22,11 +26,15 @@ export default function LazyShazam({ children }) {
|
||||||
skip: visibleStart || visible,
|
skip: visibleStart || visible,
|
||||||
});
|
});
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current) return;
|
if (!containerRef.current) return;
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
if (rect.bottom > 0) {
|
if (rect.bottom > TOP) {
|
||||||
setVisibleStart(true);
|
if (rect.top < window.innerHeight) {
|
||||||
|
setVisible(true);
|
||||||
|
} else {
|
||||||
|
setVisibleStart(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { MenuItem, SubMenu } from '@szhsin/react-menu';
|
import { MenuItem } from '@szhsin/react-menu';
|
||||||
import { cloneElement } from 'preact';
|
import { cloneElement } from 'preact';
|
||||||
import { useRef } from 'preact/hooks';
|
|
||||||
|
|
||||||
import Menu2 from './menu2';
|
import Menu2 from './menu2';
|
||||||
|
import SubMenu2 from './submenu2';
|
||||||
|
|
||||||
function MenuConfirm({
|
function MenuConfirm({
|
||||||
subMenu = false,
|
subMenu = false,
|
||||||
|
@ -23,11 +23,9 @@ function MenuConfirm({
|
||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
const Parent = subMenu ? SubMenu : Menu2;
|
const Parent = subMenu ? SubMenu2 : Menu2;
|
||||||
const menuRef = useRef();
|
|
||||||
return (
|
return (
|
||||||
<Parent
|
<Parent
|
||||||
instanceRef={menuRef}
|
|
||||||
openTrigger="clickOnly"
|
openTrigger="clickOnly"
|
||||||
direction="bottom"
|
direction="bottom"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
|
@ -37,19 +35,6 @@ function MenuConfirm({
|
||||||
{...restProps}
|
{...restProps}
|
||||||
menuButton={subMenu ? undefined : children}
|
menuButton={subMenu ? undefined : children}
|
||||||
label={subMenu ? children : undefined}
|
label={subMenu ? children : undefined}
|
||||||
// Test fix for bug; submenus not opening on Android
|
|
||||||
itemProps={{
|
|
||||||
onPointerMove: (e) => {
|
|
||||||
if (e.pointerType === 'touch') {
|
|
||||||
menuRef.current?.openMenu?.();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPointerLeave: (e) => {
|
|
||||||
if (e.pointerType === 'touch') {
|
|
||||||
menuRef.current?.openMenu?.();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MenuItem className={menuItemClassName} onClick={onClick}>
|
<MenuItem className={menuItemClassName} onClick={onClick}>
|
||||||
{confirmLabel}
|
{confirmLabel}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import './nav-menu.css';
|
import './nav-menu.css';
|
||||||
|
|
||||||
import {
|
import { ControlledMenu, MenuDivider, MenuItem } from '@szhsin/react-menu';
|
||||||
ControlledMenu,
|
|
||||||
MenuDivider,
|
|
||||||
MenuItem,
|
|
||||||
SubMenu,
|
|
||||||
} from '@szhsin/react-menu';
|
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
||||||
import { useLongPress } from 'use-long-press';
|
import { useLongPress } from 'use-long-press';
|
||||||
|
@ -20,8 +15,7 @@ import store from '../utils/store';
|
||||||
import Avatar from './avatar';
|
import Avatar from './avatar';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import MenuLink from './menu-link';
|
import MenuLink from './menu-link';
|
||||||
|
import SubMenu2 from './submenu2';
|
||||||
const supportsTouch = 'ontouchstart' in window;
|
|
||||||
|
|
||||||
function NavMenu(props) {
|
function NavMenu(props) {
|
||||||
const snapStates = useSnapshot(states);
|
const snapStates = useSnapshot(states);
|
||||||
|
@ -150,7 +144,7 @@ function NavMenu(props) {
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
// viewScroll="close"
|
viewScroll="close"
|
||||||
position="anchor"
|
position="anchor"
|
||||||
align="center"
|
align="center"
|
||||||
boundingBoxPadding={boundingBoxPadding}
|
boundingBoxPadding={boundingBoxPadding}
|
||||||
|
@ -211,8 +205,7 @@ function NavMenu(props) {
|
||||||
</MenuLink>
|
</MenuLink>
|
||||||
)}
|
)}
|
||||||
{lists?.length > 0 ? (
|
{lists?.length > 0 ? (
|
||||||
<SubMenu
|
<SubMenu2
|
||||||
openTrigger={supportsTouch ? 'clickOnly' : undefined}
|
|
||||||
menuClassName="nav-submenu"
|
menuClassName="nav-submenu"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
gap={-8}
|
gap={-8}
|
||||||
|
@ -237,7 +230,7 @@ function NavMenu(props) {
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</SubMenu>
|
</SubMenu2>
|
||||||
) : (
|
) : (
|
||||||
<MenuLink to="/l">
|
<MenuLink to="/l">
|
||||||
<Icon icon="list" size="l" />
|
<Icon icon="list" size="l" />
|
||||||
|
@ -247,8 +240,7 @@ function NavMenu(props) {
|
||||||
<MenuLink to="/b">
|
<MenuLink to="/b">
|
||||||
<Icon icon="bookmark" size="l" /> <span>Bookmarks</span>
|
<Icon icon="bookmark" size="l" /> <span>Bookmarks</span>
|
||||||
</MenuLink>
|
</MenuLink>
|
||||||
<SubMenu
|
<SubMenu2
|
||||||
openTrigger={supportsTouch ? 'clickOnly' : undefined}
|
|
||||||
menuClassName="nav-submenu"
|
menuClassName="nav-submenu"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
gap={-8}
|
gap={-8}
|
||||||
|
@ -297,7 +289,7 @@ function NavMenu(props) {
|
||||||
<Icon icon="block" size="l" />
|
<Icon icon="block" size="l" />
|
||||||
Blocked users…
|
Blocked users…
|
||||||
</MenuItem>{' '}
|
</MenuItem>{' '}
|
||||||
</SubMenu>
|
</SubMenu2>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import './shortcuts.css';
|
import './shortcuts.css';
|
||||||
|
|
||||||
import { MenuDivider, SubMenu } from '@szhsin/react-menu';
|
import { MenuDivider } from '@szhsin/react-menu';
|
||||||
import { memo } from 'preact/compat';
|
import { memo } from 'preact/compat';
|
||||||
import { useRef, useState } from 'preact/hooks';
|
import { useRef, useState } from 'preact/hooks';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -17,6 +17,7 @@ import Icon from './icon';
|
||||||
import Link from './link';
|
import Link from './link';
|
||||||
import Menu2 from './menu2';
|
import Menu2 from './menu2';
|
||||||
import MenuLink from './menu-link';
|
import MenuLink from './menu-link';
|
||||||
|
import SubMenu2 from './submenu2';
|
||||||
|
|
||||||
function Shortcuts() {
|
function Shortcuts() {
|
||||||
const { instance } = api();
|
const { instance } = api();
|
||||||
|
@ -182,7 +183,7 @@ function Shortcuts() {
|
||||||
{formattedShortcuts.map(({ id, path, title, subtitle, icon }, i) => {
|
{formattedShortcuts.map(({ id, path, title, subtitle, icon }, i) => {
|
||||||
if (id === 'lists') {
|
if (id === 'lists') {
|
||||||
return (
|
return (
|
||||||
<SubMenu
|
<SubMenu2
|
||||||
menuClassName="glass-menu"
|
menuClassName="glass-menu"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
gap={-8}
|
gap={-8}
|
||||||
|
@ -205,7 +206,7 @@ function Shortcuts() {
|
||||||
<span>{list.title}</span>
|
<span>{list.title}</span>
|
||||||
</MenuLink>
|
</MenuLink>
|
||||||
))}
|
))}
|
||||||
</SubMenu>
|
</SubMenu2>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1695,13 +1695,14 @@ a.card:is(:hover, :focus):visited {
|
||||||
}
|
}
|
||||||
.poll-label input:is([type='radio'], [type='checkbox']) {
|
.poll-label input:is([type='radio'], [type='checkbox']) {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 3px;
|
margin: 0 3px;
|
||||||
min-height: 1em;
|
min-height: 0.9em;
|
||||||
}
|
}
|
||||||
.poll-option-votes {
|
.poll-option-votes {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
.poll-option-leading .poll-option-votes {
|
.poll-option-leading .poll-option-votes {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -1943,7 +1943,24 @@ function Status({
|
||||||
{!!emojiReactions?.length && (
|
{!!emojiReactions?.length && (
|
||||||
<div class="emoji-reactions">
|
<div class="emoji-reactions">
|
||||||
{emojiReactions.map((emojiReaction) => {
|
{emojiReactions.map((emojiReaction) => {
|
||||||
const { name, count, me } = emojiReaction;
|
const { name, count, me, url, staticUrl } = emojiReaction;
|
||||||
|
if (url) {
|
||||||
|
// Some servers return url and staticUrl
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class={`emoji-reaction tag ${
|
||||||
|
me ? '' : 'insignificant'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<CustomEmoji
|
||||||
|
alt={name}
|
||||||
|
url={url}
|
||||||
|
staticUrl={staticUrl}
|
||||||
|
/>{' '}
|
||||||
|
{count}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
const isShortCode = /^:.+?:$/.test(name);
|
const isShortCode = /^:.+?:$/.test(name);
|
||||||
if (isShortCode) {
|
if (isShortCode) {
|
||||||
const emoji = emojis.find(
|
const emoji = emojis.find(
|
||||||
|
@ -1962,7 +1979,7 @@ function Status({
|
||||||
alt={name}
|
alt={name}
|
||||||
url={emoji.url}
|
url={emoji.url}
|
||||||
staticUrl={emoji.staticUrl}
|
staticUrl={emoji.staticUrl}
|
||||||
/>
|
/>{' '}
|
||||||
{count}
|
{count}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { SubMenu } from '@szhsin/react-menu';
|
||||||
|
import { useRef } from 'preact/hooks';
|
||||||
|
|
||||||
|
export default function SubMenu2(props) {
|
||||||
|
const menuRef = useRef();
|
||||||
|
return (
|
||||||
|
<SubMenu
|
||||||
|
{...props}
|
||||||
|
instanceRef={menuRef}
|
||||||
|
// Test fix for bug; submenus not opening on Android
|
||||||
|
itemProps={{
|
||||||
|
onPointerMove: (e) => {
|
||||||
|
if (e.pointerType === 'touch') {
|
||||||
|
menuRef.current?.openMenu?.();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPointerLeave: (e) => {
|
||||||
|
if (e.pointerType === 'touch') {
|
||||||
|
menuRef.current?.openMenu?.();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import localeCode2Text from '../utils/localeCode2Text';
|
||||||
import pmem from '../utils/pmem';
|
import pmem from '../utils/pmem';
|
||||||
|
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
|
import LazyShazam from './lazy-shazam';
|
||||||
import Loader from './loader';
|
import Loader from './loader';
|
||||||
|
|
||||||
const { PHANPY_LINGVA_INSTANCES } = import.meta.env;
|
const { PHANPY_LINGVA_INSTANCES } = import.meta.env;
|
||||||
|
@ -142,23 +143,21 @@ function TranslationBlock({
|
||||||
detectedLang !== targetLangText
|
detectedLang !== targetLangText
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<div class="shazam-container">
|
<LazyShazam>
|
||||||
<div class="shazam-container-inner">
|
<div class="status-translation-block-mini">
|
||||||
<div class="status-translation-block-mini">
|
<Icon
|
||||||
<Icon
|
icon="translate"
|
||||||
icon="translate"
|
alt={`Auto-translated from ${sourceLangText}`}
|
||||||
alt={`Auto-translated from ${sourceLangText}`}
|
/>
|
||||||
/>
|
<output
|
||||||
<output
|
lang={targetLang}
|
||||||
lang={targetLang}
|
dir="auto"
|
||||||
dir="auto"
|
title={pronunciationContent || ''}
|
||||||
title={pronunciationContent || ''}
|
>
|
||||||
>
|
{translatedContent}
|
||||||
{translatedContent}
|
</output>
|
||||||
</output>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</LazyShazam>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -177,6 +177,7 @@ function Search({ columnMode, ...props }) {
|
||||||
['/', 'Slash'],
|
['/', 'Slash'],
|
||||||
(e) => {
|
(e) => {
|
||||||
searchFormRef.current?.focus?.();
|
searchFormRef.current?.focus?.();
|
||||||
|
searchFormRef.current?.select?.();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
export default function localeCode2Text(code) {
|
import mem from './mem';
|
||||||
|
|
||||||
|
const IntlDN = new Intl.DisplayNames(navigator.languages, {
|
||||||
|
type: 'language',
|
||||||
|
});
|
||||||
|
|
||||||
|
function _localeCode2Text(code) {
|
||||||
try {
|
try {
|
||||||
return new Intl.DisplayNames(navigator.languages, {
|
return IntlDN.of(code);
|
||||||
type: 'language',
|
|
||||||
}).of(code);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default mem(_localeCode2Text);
|
||||||
|
|
Ładowanie…
Reference in New Issue