diff --git a/src/app.css b/src/app.css
index 8361df31..4adcee34 100644
--- a/src/app.css
+++ b/src/app.css
@@ -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;
}
diff --git a/src/app.jsx b/src/app.jsx
index 0f491e94..3cdbbdf0 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -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() {
{
+ onClose={({ destination } = {}) => {
states.showAccount = false;
if (destination) {
states.showAccounts = false;
diff --git a/src/components/menu-confirm.jsx b/src/components/menu-confirm.jsx
index 14e67746..40cd146b 100644
--- a/src/components/menu-confirm.jsx
+++ b/src/components/menu-confirm.jsx
@@ -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 (
{
+ if (e.pointerType === 'touch') {
+ menuRef.current?.openMenu?.();
+ }
+ },
+ onPointerLeave: (e) => {
+ if (e.pointerType === 'touch') {
+ menuRef.current?.openMenu?.();
+ }
+ },
+ }}
>
)}
- {status && (
+ {_statuses?.length > 1 && (
+
+ {_statuses.map((status) => (
+ -
+
+
+
+
+ ))}
+
+ )}
+ {status && (!_statuses?.length || _statuses?.length <= 1) && (
* {
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) {
diff --git a/src/components/status.jsx b/src/components/status.jsx
index 21c3877d..d47b7418 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -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}
>
diff --git a/src/pages/accounts.jsx b/src/pages/accounts.jsx
index c7910b5a..47d90b01 100644
--- a/src/pages/accounts.jsx
+++ b/src/pages/accounts.jsx
@@ -143,7 +143,7 @@ function Accounts({ onClose }) {
accounts.splice(i, 1);
store.local.setJSON('accounts', accounts);
// location.reload();
- location.href = '/';
+ location.href = location.pathname || '/';
}}
>
diff --git a/src/pages/notifications.css b/src/pages/notifications.css
index b986018a..9eb28951 100644
--- a/src/pages/notifications.css
+++ b/src/pages/notifications.css
@@ -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;
diff --git a/src/pages/status.jsx b/src/pages/status.jsx
index 8afa7050..44babe8e 100644
--- a/src/pages/status.jsx
+++ b/src/pages/status.jsx
@@ -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"
>
- {' '}
-
- {shortenNumber(ancestors.length)}
-
+ {ancestors
+ .filter(
+ (a, i, arr) =>
+ arr.findIndex((b) => b.accountID === a.accountID) === i,
+ )
+ .slice(0, 3)
+ .map((ancestor) => (
+
+ ))}
+ {/* {' '} */}
+ {ancestors.length > 3 && (
+ <>
+ {' '}
+
+ {shortenNumber(ancestors.length)}
+
+ >
+ )}
>
)}
diff --git a/src/utils/auth.js b/src/utils/auth.js
index 36898f7a..600b0c0c 100644
--- a/src/utils/auth.js
+++ b/src/utils/auth.js
@@ -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',
diff --git a/src/utils/group-notifications.jsx b/src/utils/group-notifications.jsx
index eff19181..cbc80b48 100644
--- a/src/utils/group-notifications.jsx
+++ b/src/utils/group-notifications.jsx
@@ -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;
diff --git a/src/utils/states.js b/src/utils/states.js
index 6b8e281a..dde99563 100644
--- a/src/utils/states.js
+++ b/src/utils/states.js
@@ -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);
diff --git a/vite.config.js b/vite.config.js
index 4eaec943..0cf8a335 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -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),