Porównaj commity

...

19 Commity

Autor SHA1 Wiadomość Data
Alyx ba5e172440
Merge fa196a2c94 into 015ed5e7eb 2024-04-05 12:11:17 +02:00
Lim Chee Aun 015ed5e7eb Further expand usage of SubMenu2 2024-04-04 17:03:30 +08:00
Lim Chee Aun 2ad9706304 Further utilize lazy shazam 2024-04-04 14:34:28 +08:00
Lim Chee Aun 30382d088b Possible fix for menus again 2024-04-04 14:34:04 +08:00
Lim Chee Aun 80196f83ca Revert "Test if this fixes submenu not opening"
This reverts commit 49fa48bd28.
2024-04-04 14:29:46 +08:00
Lim Chee Aun 419ad34250 Revert "Test another fix for submenus not opening"
This reverts commit a7cc0785f9.
2024-04-04 14:29:35 +08:00
Lim Chee Aun ed0d714cf2 Just a little spacing fix 2024-04-03 22:51:29 +08:00
Lim Chee Aun 708976a9e9 Anything Intl always need to extract out
and memoized
2024-04-03 19:48:18 +08:00
Lim Chee Aun d77ba19308 Handle another kind of emojiReaction response
Can't everyone just standardize the responses?
2024-04-03 17:58:37 +08:00
Lim Chee Aun b10e22a9a2 Better fallbacks 2024-04-03 17:57:15 +08:00
Lim Chee Aun 36d8b62e1e Height adjustments when switching between poll form and results 2024-04-03 16:14:59 +08:00
Lim Chee Aun 989e788d8e Slight delay is needed 2024-04-03 16:06:37 +08:00
Lim Chee Aun ebd9f05f69 Preload IntlSegmenter polyfill if needed 2024-04-03 14:33:53 +08:00
Lim Chee Aun 5246af4ae9 Undo lazy component experiment
Doesn't make much difference
2024-04-03 14:33:19 +08:00
Lim Chee Aun e6ba72f4c8 'Remove follower' menu item 2024-04-03 11:54:46 +08:00
Lim Chee Aun 960dff8b9e Make lazy shazam ignore top sticky header 2024-04-03 11:53:03 +08:00
Lim Chee Aun e3c25d25ee Add menus to view profile image and header 2024-04-03 09:29:23 +08:00
Lim Chee Aun 090320150a Select text too when pressing / 2024-04-03 09:28:59 +08:00
Alyx fa196a2c94
Add Dockerfile 2024-03-18 09:52:29 +01:00
16 zmienionych plików z 192 dodań i 86 usunięć

23
Dockerfile 100644
Wyświetl plik

@ -0,0 +1,23 @@
FROM busybox:1 AS build
ARG PHANPY_RELEASE_VERSION
WORKDIR /root/phanpy_release
RUN wget "https://github.com/cheeaun/phanpy/releases/download/${PHANPY_RELEASE_VERSION}/phanpy-dist.tar.gz" && \
tar -xvf "phanpy-dist.tar.gz" -C /root/phanpy_release && \
rm "phanpy-dist.tar.gz"
# ---
FROM busybox:1
# Create a non-root user to own the files and run our server
RUN adduser -D static
USER static
WORKDIR /home/static
# Copy the static website
# Use the .dockerignore file to control what ends up inside the image!
COPY --chown=static:static --from=build /root/phanpy_release /home/static
# Run BusyBox httpd
CMD ["httpd", "-f", "-v", "-p", "8080"]

Wyświetl plik

@ -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 />} />

Wyświetl plik

@ -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'),
}; };

Wyświetl plik

@ -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

Wyświetl plik

@ -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"

Wyświetl plik

@ -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>;

Wyświetl plik

@ -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);
}
} }
}, []); }, []);

Wyświetl plik

@ -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}

Wyświetl plik

@ -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&hellip; Blocked users&hellip;
</MenuItem>{' '} </MenuItem>{' '}
</SubMenu> </SubMenu2>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
onClick={() => { onClick={() => {

Wyświetl plik

@ -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>
); );
} }

Wyświetl plik

@ -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;

Wyświetl plik

@ -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>
); );

Wyświetl plik

@ -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?.();
}
},
}}
/>
);
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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,

Wyświetl plik

@ -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);