Merge pull request #268 from cheeaun/main

Update from main
This commit is contained in:
Chee Aun 2023-10-20 00:44:53 +08:00 committed by GitHub
commit 0cf7d683ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 194 additions and 95 deletions

View file

@ -14,15 +14,17 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
ref: production ref: production
- run: git tag "`date +%Y.%m.%d`.`git rev-parse --short HEAD`" $(git rev-parse HEAD) # - run: git tag "`date +%Y.%m.%d`.`git rev-parse --short HEAD`" $(git rev-parse HEAD)
- run: git push --tags # - run: git push --tags
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- run: npm ci && npm run build - run: npm ci && npm run build
- run: cd dist && zip -r ../phanpy-dist.zip . && cd .. - run: cd dist && zip -r ../phanpy-dist.zip . && cd ..
- id: tag_name
run: echo ::set-output name=tag_name::$(date +%Y.%m.%d).$(git rev-parse --short HEAD)
- uses: softprops/action-gh-release@v1 - uses: softprops/action-gh-release@v1
with: with:
tag_name: ${{ github.ref_name }} tag_name: ${{ steps.tag_name.outputs.tag_name }}
generate_release_notes: true generate_release_notes: true
files: phanpy-dist.zip files: phanpy-dist.zip

View file

@ -179,12 +179,12 @@ self.addEventListener('notificationclick', (event) => {
console.log('NOTIFICATION CLICK navigate', url); console.log('NOTIFICATION CLICK navigate', url);
if (bestClient) { if (bestClient) {
console.log('NOTIFICATION CLICK postMessage', bestClient); console.log('NOTIFICATION CLICK postMessage', bestClient);
bestClient.focus();
bestClient.postMessage?.({ bestClient.postMessage?.({
type: 'notification', type: 'notification',
id: tag, id: tag,
accessToken: access_token, accessToken: access_token,
}); });
bestClient.focus();
} else { } else {
console.log('NOTIFICATION CLICK openWindow', url); console.log('NOTIFICATION CLICK openWindow', url);
await self.clients.openWindow(url); await self.clients.openWindow(url);

View file

@ -676,6 +676,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
position: relative; position: relative;
border-radius: 0; border-radius: 0;
padding-block: 16px !important; padding-block: 16px !important;
.avatars-bunch > .avatar:not(:first-child) {
margin-left: -4px;
}
} }
.timeline .show-more:hover { .timeline .show-more:hover {
filter: none !important; filter: none !important;
@ -2116,6 +2120,13 @@ ul.link-list li a .icon {
transparent transparent
); );
align-items: center; align-items: center;
transition: opacity 0.3s ease-out;
&.loading,
.loading > & {
pointer-events: none;
opacity: 0.5;
}
} }
.filter-bar.centered { .filter-bar.centered {
justify-content: center; justify-content: center;
@ -2133,14 +2144,19 @@ ul.link-list li a .icon {
text-decoration: none; text-decoration: none;
white-space: nowrap; white-space: nowrap;
border: 2px solid transparent; border: 2px solid transparent;
transition: all 0.3s ease-out; transition: border-color 0.3s ease-out;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.filter-bar > a:is(:hover, :focus) { .filter-bar > a:focus-visible {
border-color: var(--link-light-color); border-color: var(--link-light-color);
} }
@media (hover: hover) {
.filter-bar > a:hover {
border-color: var(--link-light-color);
}
}
.filter-bar > a > * { .filter-bar > a > * {
vertical-align: middle; vertical-align: middle;
} }

View file

@ -550,11 +550,13 @@ function AccountInfo({
tabIndex={0} tabIndex={0}
to={accountLink} to={accountLink}
onClick={() => { onClick={() => {
states.showAccount = false; // states.showAccount = false;
states.showGenericAccounts = { setTimeout(() => {
heading: 'Followers', states.showGenericAccounts = {
fetchAccounts: fetchFollowers, heading: 'Followers',
}; fetchAccounts: fetchFollowers,
};
}, 0);
}} }}
> >
{!!familiarFollowers.length && ( {!!familiarFollowers.length && (
@ -581,11 +583,13 @@ function AccountInfo({
tabIndex={0} tabIndex={0}
to={accountLink} to={accountLink}
onClick={() => { onClick={() => {
states.showAccount = false; // states.showAccount = false;
states.showGenericAccounts = { setTimeout(() => {
heading: 'Following', states.showGenericAccounts = {
fetchAccounts: fetchFollowing, heading: 'Following',
}; fetchAccounts: fetchFollowing,
};
}, 0);
}} }}
> >
<span title={followingCount}> <span title={followingCount}>
@ -597,13 +601,13 @@ function AccountInfo({
<LinkOrDiv <LinkOrDiv
class="insignificant" class="insignificant"
to={accountLink} to={accountLink}
onClick={ // onClick={
standalone // standalone
? undefined // ? undefined
: () => { // : () => {
hideAllModals(); // hideAllModals();
} // }
} // }
> >
<span title={statusesCount}> <span title={statusesCount}>
{shortenNumber(statusesCount)} {shortenNumber(statusesCount)}
@ -626,9 +630,9 @@ function AccountInfo({
<LinkOrDiv <LinkOrDiv
to={accountLink} to={accountLink}
class="account-metadata-box" class="account-metadata-box"
onClick={() => { // onClick={() => {
states.showAccount = false; // states.showAccount = false;
}} // }}
> >
<div class="shazam-container"> <div class="shazam-container">
<div class="shazam-container-inner"> <div class="shazam-container-inner">
@ -1511,7 +1515,7 @@ function PrivateNoteSheet({
</button> </button>
)} )}
<header> <header>
<b>Private note for @{account?.acct}</b> <b>Private note about @{account?.username || account?.acct}</b>
</header> </header>
<main> <main>
<form <form

View file

@ -2,6 +2,7 @@ import { useEffect } from 'preact/hooks';
import { api } from '../utils/api'; import { api } from '../utils/api';
import states from '../utils/states'; import states from '../utils/states';
import useLocationChange from '../utils/useLocationChange';
import AccountInfo from './account-info'; import AccountInfo from './account-info';
import Icon from './icon'; import Icon from './icon';
@ -16,17 +17,19 @@ function AccountSheet({ account, instance: propInstance, onClose }) {
} }
}, [account]); }, [account]);
useLocationChange(onClose);
return ( return (
<div <div
class="sheet" class="sheet"
onClick={(e) => { // onClick={(e) => {
const accountBlock = e.target.closest('.account-block'); // const accountBlock = e.target.closest('.account-block');
if (accountBlock) { // if (accountBlock) {
onClose({ // onClose({
destination: 'account-statuses', // destination: 'account-statuses',
}); // });
} // }
}} // }}
> >
{!!onClose && ( {!!onClose && (
<button type="button" class="sheet-close outer" onClick={onClose}> <button type="button" class="sheet-close outer" onClick={onClose}>

View file

@ -5,6 +5,7 @@ import { InView } from 'react-intersection-observer';
import { useSnapshot } from 'valtio'; import { useSnapshot } from 'valtio';
import states from '../utils/states'; import states from '../utils/states';
import useLocationChange from '../utils/useLocationChange';
import AccountBlock from './account-block'; import AccountBlock from './account-block';
import Icon from './icon'; import Icon from './icon';
@ -16,6 +17,8 @@ export default function GenericAccounts({ onClose = () => {} }) {
const [accounts, setAccounts] = useState([]); const [accounts, setAccounts] = useState([]);
const [showMore, setShowMore] = useState(false); const [showMore, setShowMore] = useState(false);
useLocationChange(onClose);
if (!snapStates.showGenericAccounts) { if (!snapStates.showGenericAccounts) {
return null; return null;
} }

View file

@ -2,14 +2,14 @@ import './loader.css';
function Loader({ abrupt, hidden, ...props }) { function Loader({ abrupt, hidden, ...props }) {
return ( return (
<div <span
{...props} {...props}
class={`loader-container ${abrupt ? 'abrupt' : ''} ${ class={`loader-container ${abrupt ? 'abrupt' : ''} ${
hidden ? 'hidden' : '' hidden ? 'hidden' : ''
}`} }`}
> >
<div class="loader" /> <span class="loader" />
</div> </span>
); );
} }

View file

@ -130,6 +130,7 @@ function Media({
enabled: pinchZoomEnabled, enabled: pinchZoomEnabled,
draggableUnZoomed: false, draggableUnZoomed: false,
inertiaFriction: 0.9, inertiaFriction: 0.9,
doubleTapZoomOutOnMaxScale: true,
containerProps: { containerProps: {
className: 'media-zoom', className: 'media-zoom',
style: { style: {

View file

@ -117,9 +117,10 @@ export default function Modals() {
instance={snapStates.showAccount?.instance} instance={snapStates.showAccount?.instance}
onClose={({ destination } = {}) => { onClose={({ destination } = {}) => {
states.showAccount = false; states.showAccount = false;
if (destination) { // states.showGenericAccounts = false;
states.showAccounts = false; // if (destination) {
} // states.showAccounts = false;
// }
}} }}
/> />
</Modal> </Modal>

View file

@ -1,3 +1,9 @@
.nav-menu section:last-child {
background-color: var(--bg-faded-color);
margin-bottom: -8px;
padding-bottom: 8px;
}
@media (min-width: 23em) { @media (min-width: 23em) {
.nav-menu { .nav-menu {
display: grid; display: grid;
@ -8,6 +14,7 @@
'left right'; 'left right';
padding: 0; padding: 0;
width: 22em; width: 22em;
max-width: calc(100vw - 16px);
} }
.nav-menu .top-menu { .nav-menu .top-menu {
grid-area: top; grid-area: top;
@ -27,7 +34,6 @@
} }
} }
.nav-menu section:last-child { .nav-menu section:last-child {
background-color: var(--bg-faded-color);
background-image: linear-gradient( background-image: linear-gradient(
to right, to right,
var(--divider-color) 1px, var(--divider-color) 1px,
@ -45,6 +51,15 @@
animation: phanpying 0.2s ease-in-out both; animation: phanpying 0.2s ease-in-out both;
border-top-right-radius: inherit; border-top-right-radius: inherit;
border-bottom-right-radius: inherit; border-bottom-right-radius: inherit;
margin-bottom: 0;
display: flex;
flex-direction: column;
.divider-grow {
flex-grow: 1;
height: auto;
background-color: transparent;
}
} }
.nav-menu section:last-child > .szh-menu__divider:first-child { .nav-menu section:last-child > .szh-menu__divider:first-child {
display: none; display: none;

View file

@ -249,6 +249,7 @@ function NavMenu(props) {
<Icon icon="block" size="l" /> <Icon icon="block" size="l" />
Blocked users&hellip; Blocked users&hellip;
</MenuItem> </MenuItem>
<MenuDivider className="divider-grow" />
<MenuItem <MenuItem
onClick={() => { onClick={() => {
states.showKeyboardShortcutsHelp = true; states.showKeyboardShortcutsHelp = true;
@ -263,7 +264,7 @@ function NavMenu(props) {
}} }}
> >
<Icon icon="shortcut" size="l" />{' '} <Icon icon="shortcut" size="l" />{' '}
<span>Shortcuts Settings&hellip;</span> <span>Shortcuts / Columns&hellip;</span>
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {

View file

@ -249,46 +249,45 @@ function ShortcutsSettings({ onClose }) {
</h2> </h2>
</header> </header>
<main> <main>
<p> <p>Specify a list of shortcuts that'll appear&nbsp;as:</p>
Specify a list of shortcuts that'll appear&nbsp;as: <div class="shortcuts-view-mode">
<div class="shortcuts-view-mode"> {[
{[ {
{ value: 'float-button',
value: 'float-button', label: 'Floating button',
label: 'Floating button', imgURL: floatingButtonUrl,
imgURL: floatingButtonUrl, },
}, {
{ value: 'tab-menu-bar',
value: 'tab-menu-bar', label: 'Tab/Menu bar',
label: 'Tab/Menu bar', imgURL: tabMenuBarUrl,
imgURL: tabMenuBarUrl, },
}, {
{ value: 'multi-column',
value: 'multi-column', label: 'Multi-column',
label: 'Multi-column', imgURL: multiColumnUrl,
imgURL: multiColumnUrl, },
}, ].map(({ value, label, imgURL }) => (
].map(({ value, label, imgURL }) => ( <label>
<label> <input
<input type="radio"
type="radio" name="shortcuts-view-mode"
name="shortcuts-view-mode" value={value}
value={value} checked={
checked={ snapStates.settings.shortcutsViewMode === value ||
snapStates.settings.shortcutsViewMode === value || (value === 'float-button' &&
(value === 'float-button' && !snapStates.settings.shortcutsViewMode)
!snapStates.settings.shortcutsViewMode) }
} onChange={(e) => {
onChange={(e) => { states.settings.shortcutsViewMode = e.target.value;
states.settings.shortcutsViewMode = e.target.value; }}
}} />{' '}
/>{' '} <img src={imgURL} alt="" width="80" height="58" />{' '}
<img src={imgURL} alt="" width="80" height="58" />{' '} <span>{label}</span>
<span>{label}</span> </label>
</label> ))}
))} </div>
</div> {/* <select
{/* <select
value={snapStates.settings.shortcutsViewMode || 'float-button'} value={snapStates.settings.shortcutsViewMode || 'float-button'}
onChange={(e) => { onChange={(e) => {
states.settings.shortcutsViewMode = e.target.value; states.settings.shortcutsViewMode = e.target.value;
@ -298,7 +297,6 @@ function ShortcutsSettings({ onClose }) {
<option value="multi-column">Multi-column</option> <option value="multi-column">Multi-column</option>
<option value="tab-menu-bar">Tab/Menu bar </option> <option value="tab-menu-bar">Tab/Menu bar </option>
</select> */} </select> */}
</p>
{/* <p> {/* <p>
<details> <details>
<summary class="insignificant"> <summary class="insignificant">

View file

@ -579,7 +579,11 @@ function Status({
try { try {
const done = await confirmBoostStatus(); const done = await confirmBoostStatus();
if (!isSizeLarge && done) { if (!isSizeLarge && done) {
showToast(reblogged ? 'Unboosted' : 'Boosted'); showToast(
reblogged
? `Unboosted @${username || acct}'s post`
: `Boosted @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
}} }}
@ -597,7 +601,11 @@ function Status({
try { try {
favouriteStatus(); favouriteStatus();
if (!isSizeLarge) { if (!isSizeLarge) {
showToast(favourited ? 'Unfavourited' : 'Favourited'); showToast(
favourited
? `Unfavourited @${username || acct}'s post`
: `Favourited @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
}} }}
@ -621,7 +629,11 @@ function Status({
try { try {
bookmarkStatus(); bookmarkStatus();
if (!isSizeLarge) { if (!isSizeLarge) {
showToast(bookmarked ? 'Unbookmarked' : 'Bookmarked'); showToast(
bookmarked
? `Unbookmarked @${username || acct}'s post`
: `Bookmarked @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
}} }}
@ -829,7 +841,11 @@ function Status({
try { try {
favouriteStatus(); favouriteStatus();
if (!isSizeLarge) { if (!isSizeLarge) {
showToast(favourited ? 'Unfavourited' : 'Favourited'); showToast(
favourited
? `Unfavourited @${username || acct}'s post`
: `Favourited @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
}, },
@ -843,7 +859,11 @@ function Status({
try { try {
bookmarkStatus(); bookmarkStatus();
if (!isSizeLarge) { if (!isSizeLarge) {
showToast(bookmarked ? 'Unbookmarked' : 'Bookmarked'); showToast(
bookmarked
? `Unbookmarked @${username || acct}'s post`
: `Bookmarked @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
}, },
@ -858,7 +878,11 @@ function Status({
try { try {
const done = await confirmBoostStatus(); const done = await confirmBoostStatus();
if (!isSizeLarge && done) { if (!isSizeLarge && done) {
showToast(reblogged ? 'Unboosted' : 'Boosted'); showToast(
reblogged
? `Unboosted @${username || acct}'s post`
: `Boosted @${username || acct}'s post`,
);
} }
} catch (e) {} } catch (e) {}
})(); })();

View file

@ -334,7 +334,13 @@ function Timeline({
</button> </button>
)} )}
</header> </header>
{!!timelineStart && <div class="timeline-start">{timelineStart}</div>} {!!timelineStart && (
<div
class={`timeline-start ${uiState === 'loading' ? 'loading' : ''}`}
>
{timelineStart}
</div>
)}
{!!items.length ? ( {!!items.length ? (
<> <>
<ul class="timeline"> <ul class="timeline">

View file

@ -258,7 +258,7 @@ function AccountStatuses() {
useItemID useItemID
boostsCarousel={snapStates.settings.boostsCarousel} boostsCarousel={snapStates.settings.boostsCarousel}
timelineStart={TimelineStart} timelineStart={TimelineStart}
refresh={excludeReplies + excludeBoosts + tagged + media} refresh={[excludeReplies, excludeBoosts, tagged, media].toString()}
headerEnd={ headerEnd={
<Menu2 <Menu2
portal portal

View file

@ -152,7 +152,7 @@ function Search(props) {
</header> </header>
<main> <main>
{!!q && ( {!!q && (
<div class="filter-bar"> <div class={`filter-bar ${uiState === 'loading' ? 'loading' : ''}`}>
{!!type && ( {!!type && (
<Link to={`/search${q ? `?q=${encodeURIComponent(q)}` : ''}`}> <Link to={`/search${q ? `?q=${encodeURIComponent(q)}` : ''}`}>
All All

View file

@ -1062,7 +1062,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
onClick={() => setLimit((l) => l + LIMIT)} onClick={() => setLimit((l) => l + LIMIT)}
style={{ marginBlockEnd: '6em' }} style={{ marginBlockEnd: '6em' }}
> >
<div class="ib"> <div class="ib avatars-bunch">
{/* show avatars for first 5 statuses */} {/* show avatars for first 5 statuses */}
{statuses.slice(limit, limit + 5).map((status) => ( {statuses.slice(limit, limit + 5).map((status) => (
<Avatar <Avatar

View file

@ -1,3 +1,5 @@
import mem from './mem';
const div = document.createElement('div'); const div = document.createElement('div');
function getHTMLText(html) { function getHTMLText(html) {
if (!html) return ''; if (!html) return '';
@ -10,4 +12,4 @@ function getHTMLText(html) {
return div.innerText.replace(/[\r\n]{3,}/g, '\n\n').trim(); return div.innerText.replace(/[\r\n]{3,}/g, '\n\n').trim();
} }
export default getHTMLText; export default mem(getHTMLText);

View file

@ -0,0 +1,23 @@
import { useEffect, useRef } from 'preact/hooks';
import { useLocation } from 'react-router-dom';
// Hook that runs a callback when the location changes
// Won't run on the first render
export default function useLocationChange(fn) {
if (!fn) return;
const location = useLocation();
const currentLocationRef = useRef(location.pathname);
useEffect(() => {
// console.log('location', {
// current: currentLocationRef.current,
// next: location.pathname,
// });
if (
currentLocationRef.current &&
location.pathname !== currentLocationRef.current
) {
fn?.();
}
}, [location.pathname, fn]);
}