Merge pull request #223 from cheeaun/main

Update from main
This commit is contained in:
Chee Aun 2023-09-01 13:00:48 +08:00 committed by GitHub
commit b8d92bceb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 183 additions and 23 deletions

View file

@ -1099,7 +1099,7 @@ button.carousel-dot {
font-weight: bold;
color: var(--text-color);
background-color: var(--bg-faded-blur-color);
border: var(--hairline-width) solid var(--outline-color);
border: 1px solid var(--outline-color);
box-shadow: 0 4px 32px var(--drop-shadow-color);
transition: all 0.2s ease-out;
}

View file

@ -58,7 +58,7 @@ import {
import { getAccessToken } from './utils/auth';
import openCompose from './utils/open-compose';
import showToast from './utils/show-toast';
import states, { getStatus, saveStatus } from './utils/states';
import states, { initStates, saveStatus } from './utils/states';
import store from './utils/store';
import { getCurrentAccount } from './utils/store-utils';
import useInterval from './utils/useInterval';
@ -111,7 +111,7 @@ function App() {
if (code) {
console.log({ code });
// Clear the code from the URL
window.history.replaceState({}, document.title, '/');
window.history.replaceState({}, document.title, location.pathname || '/');
const clientID = store.session.get('clientID');
const clientSecret = store.session.get('clientSecret');
@ -130,6 +130,7 @@ function App() {
initInstance(masto, instanceURL),
initAccount(masto, instanceURL, accessToken),
]);
initStates();
initPreferences(masto);
setIsLoggedIn(true);
@ -389,7 +390,7 @@ function App() {
<AccountSheet
account={snapStates.showAccount?.account || snapStates.showAccount}
instance={snapStates.showAccount?.instance}
onClose={({ destination }) => {
onClose={({ destination } = {}) => {
states.showAccount = false;
if (destination) {
states.showAccounts = false;

View file

@ -1,5 +1,6 @@
import { Menu, MenuItem, SubMenu } from '@szhsin/react-menu';
import { cloneElement } from 'preact';
import { useRef } from 'preact/hooks';
function MenuConfirm({
subMenu = false,
@ -20,8 +21,10 @@ function MenuConfirm({
return children;
}
const Parent = subMenu ? SubMenu : Menu;
const menuRef = useRef();
return (
<Parent
instanceRef={menuRef}
openTrigger="clickOnly"
direction="bottom"
overflow="auto"
@ -31,6 +34,19 @@ function MenuConfirm({
{...restProps}
menuButton={subMenu ? undefined : children}
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}>
{confirmLabel}

View file

@ -39,21 +39,25 @@ const contentText = {
mention: 'mentioned you in their post.',
status: 'published a post.',
reblog: 'boosted your post.',
'reblog+account': (count) => `boosted ${count} of your posts.`,
reblog_reply: 'boosted your reply.',
follow: 'followed you.',
follow_request: 'requested to follow you.',
favourite: 'favourited your post.',
'favourite+account': (count) => `favourited ${count} of your posts.`,
favourite_reply: 'favourited your reply.',
poll: 'A poll you have voted in or created has ended.',
'poll-self': 'A poll you have created has ended.',
'poll-voted': 'A poll you have voted in has ended.',
update: 'A post you interacted with has been edited.',
'favourite+reblog': 'boosted & favourited your post.',
'favourite+reblog+account': (count) =>
`boosted & favourited ${count} of your posts.`,
'favourite+reblog_reply': 'boosted & favourited your reply.',
};
function Notification({ notification, instance, reload }) {
const { id, status, account, _accounts } = notification;
const { id, status, account, _accounts, _statuses } = notification;
let { type } = notification;
// status = Attached when type of the notification is favourite, reblog, status, mention, poll, or update
@ -90,12 +94,19 @@ function Notification({ notification, instance, reload }) {
type === 'favourite' ||
type === 'favourite+reblog'
) {
text =
contentText[isReplyToOthers ? `${type}_reply` : type] ||
contentText[type];
if (_statuses?.length > 1) {
text = contentText[`${type}+account`];
} else if (isReplyToOthers) {
text = contentText[`${type}_reply`];
} else {
text = contentText[type];
}
} else {
text = contentText[type];
}
if (typeof text === 'function') {
text = text(_statuses?.length || _accounts?.length);
}
if (type === 'mention' && !status) {
// Could be deleted
@ -206,7 +217,23 @@ function Notification({ notification, instance, reload }) {
))}
</p>
)}
{status && (
{_statuses?.length > 1 && (
<ul class="notification-group-statuses">
{_statuses.map((status) => (
<li key={status.id}>
<Link
class={`status-link status-type-${type}`}
to={
instance ? `/${instance}/s/${status.id}` : `/s/${status.id}`
}
>
<Status status={status} size="s" />
</Link>
</li>
))}
</ul>
)}
{status && (!_statuses?.length || _statuses?.length <= 1) && (
<Link
class={`status-link status-type-${type}`}
to={

View file

@ -121,11 +121,14 @@
.status-card > * {
pointer-events: none;
}
.status-card :is(.content, .poll, .media-container) {
.status-card:not(.status-carousel .status)
:is(.content, .poll, .media-container) {
max-height: 160px !important;
overflow: hidden;
}
.status.small .status-card :is(.content, .poll, .media-container) {
.status.small:not(.status-carousel .status)
.status-card
:is(.content, .poll, .media-container) {
max-height: 80px !important;
}
.status-card :is(.content, .poll) {

View file

@ -1170,9 +1170,7 @@ function Status({
(option) =>
`- ${option.title}${
option.votesCount >= 0
? ` (${option.votesCount} vote${
option.votesCount !== 1 ? 's' : ''
})`
? ` (${option.votesCount})`
: ''
}`,
)
@ -1436,6 +1434,7 @@ function Card({ card, instance }) {
url,
type,
embedUrl,
language,
} = card;
/* type
@ -1499,6 +1498,7 @@ function Card({ card, instance }) {
target={cardStatusURL ? null : '_blank'}
rel="nofollow noopener noreferrer"
class={`card link ${blurhashImage ? '' : size}`}
lang={language}
>
<div class="card-image">
<img
@ -1567,6 +1567,7 @@ function Card({ card, instance }) {
target={cardStatusURL ? null : '_blank'}
rel="nofollow noopener noreferrer"
class={`card link no-image`}
lang={language}
>
<div class="meta-container">
<p class="meta domain">

View file

@ -143,7 +143,7 @@ function Accounts({ onClose }) {
accounts.splice(i, 1);
store.local.setJSON('accounts', accounts);
// location.reload();
location.href = '/';
location.href = location.pathname || '/';
}}
>
<Icon icon="exit" />

View file

@ -92,6 +92,7 @@
filter: saturate(0.25);
}
.notification .status-link:not(.status-type-mention) > .status {
font-size: calc(var(--text-size) * 0.9);
max-height: 160px;
overflow: hidden;
/* fade out mask gradient bottom */
@ -134,6 +135,38 @@
margin-bottom: 8px;
}
.notification-group-statuses {
margin: 0;
padding: 0;
list-style: none;
}
.notification-group-statuses > li {
margin: 0;
padding: 0;
list-style: none;
position: relative;
counter-increment: index;
}
.notification-group-statuses > li:before {
content: counter(index);
position: absolute;
left: 0;
font-size: 10px;
padding: 8px;
font-weight: bold;
}
.notification-group-statuses > li + li {
margin-top: -1px;
}
.notification-group-statuses > li:not(:last-child) .status-link {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.notification-group-statuses > li:not(:first-child) .status-link {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
#mentions-option {
float: right;
margin-top: 0.5em;

View file

@ -336,6 +336,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
ancestor: true,
isThread: ancestorsIsThread,
accountID: s.account.id,
account: s.account,
repliesCount: s.repliesCount,
weight: calcStatusWeight(s),
})),
@ -705,6 +706,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
block: 'start',
});
}}
title="Go to main post"
>
<Icon
icon={heroPointer === 'down' ? 'arrow-down' : 'arrow-up'}
@ -727,12 +729,31 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
});
}}
hidden={!ancestors.length || nearReachStart}
title={`${ancestors.length} posts above Go to top`}
>
<Icon icon="arrow-up" />
<Icon icon="comment" />{' '}
{ancestors
.filter(
(a, i, arr) =>
arr.findIndex((b) => b.accountID === a.accountID) === i,
)
.slice(0, 3)
.map((ancestor) => (
<Avatar
key={ancestor.account.id}
url={ancestor.account.avatar}
alt={ancestor.account.displayName}
/>
))}
{/* <Icon icon="comment" />{' '} */}
{ancestors.length > 3 && (
<>
{' '}
<span class="insignificant">
{shortenNumber(ancestors.length)}
</span>
</>
)}
</button>
</>
)}

View file

@ -4,8 +4,8 @@ const { VITE_CLIENT_NAME: CLIENT_NAME, VITE_WEBSITE: WEBSITE } = import.meta
export async function registerApplication({ instanceURL }) {
const registrationParams = new URLSearchParams({
client_name: CLIENT_NAME,
redirect_uris: location.origin,
scopes: 'read write follow',
redirect_uris: location.origin + location.pathname,
website: WEBSITE,
});
const registrationResponse = await fetch(
@ -27,7 +27,7 @@ export async function getAuthorizationURL({ instanceURL, client_id }) {
const authorizationParams = new URLSearchParams({
client_id,
scope: 'read write follow',
redirect_uri: location.origin,
redirect_uri: location.origin + location.pathname,
// redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
response_type: 'code',
});
@ -44,7 +44,7 @@ export async function getAccessToken({
const params = new URLSearchParams({
client_id,
client_secret,
redirect_uri: location.origin,
redirect_uri: location.origin + location.pathname,
grant_type: 'authorization_code',
code,
scope: 'read write follow',

View file

@ -37,7 +37,40 @@ function groupNotifications(notifications) {
cleanNotifications[j++] = n;
}
}
return cleanNotifications;
// 2nd pass to group "favourite+reblog"-type notifications by account if _accounts.length <= 1
// This means one acount has favourited and reblogged the multiple statuses
// The grouped notification
// - type: "favourite+reblog+account"
// - _statuses: [status, status, ...]
const notificationsMap2 = {};
const cleanNotifications2 = [];
for (let i = 0, j = 0; i < cleanNotifications.length; i++) {
const notification = cleanNotifications[i];
const { account, _accounts, type, createdAt } = notification;
const date = new Date(createdAt).toLocaleDateString();
if (type === 'favourite+reblog' && account && _accounts.length === 1) {
const key = `${account?.id}-${type}-${date}`;
const mappedNotification = notificationsMap2[key];
if (mappedNotification) {
mappedNotification._statuses.push(notification.status);
} else {
let n = (notificationsMap2[key] = {
...notification,
type,
_statuses: [notification.status],
});
cleanNotifications2[j++] = n;
}
} else {
cleanNotifications2[j++] = notification;
}
}
console.log({ notifications, cleanNotifications, cleanNotifications2 });
// return cleanNotifications;
return cleanNotifications2;
}
export default groupNotifications;

View file

@ -60,6 +60,30 @@ const states = proxy({
export default states;
export function initStates() {
// init all account based states
// all keys that uses store.account.get() should be initialized here
states.notificationsLast = store.account.get('notificationsLast') || null;
states.shortcuts = store.account.get('shortcuts') ?? [];
states.settings.autoRefresh =
store.account.get('settings-autoRefresh') ?? false;
states.settings.shortcutsViewMode =
store.account.get('settings-shortcutsViewMode') ?? null;
states.settings.shortcutsColumnsMode =
store.account.get('settings-shortcutsColumnsMode') ?? false;
states.settings.boostsCarousel =
store.account.get('settings-boostsCarousel') ?? true;
states.settings.contentTranslation =
store.account.get('settings-contentTranslation') ?? true;
states.settings.contentTranslationTargetLanguage =
store.account.get('settings-contentTranslationTargetLanguage') || null;
states.settings.contentTranslationHideLanguages =
store.account.get('settings-contentTranslationHideLanguages') || [];
states.settings.contentTranslationAutoInline =
store.account.get('settings-contentTranslationAutoInline') ?? false;
states.settings.cloakMode = store.account.get('settings-cloakMode') ?? false;
}
subscribeKey(states, 'notificationsLast', (v) => {
console.log('CHANGE', v);
store.account.set('notificationsLast', states.notificationsLast);

View file

@ -25,6 +25,7 @@ const rollbarCode = fs.readFileSync(
// https://vitejs.dev/config/
export default defineConfig({
base: './',
mode: NODE_ENV,
define: {
__BUILD_TIME__: JSON.stringify(now),