1
0
Fork 0

Compare commits

...

12 commits

Author SHA1 Message Date
Alexander Yakovlev 531df4cafa
Merge remote-tracking branch 'origin/main' 2024-05-18 15:00:05 +06:00
Lim Chee Aun 62f843b4dc Fix crash when media url doesn't have http prefix 2024-05-17 17:10:54 +08:00
Lim Chee Aun b0a53b7fa1 Handle hideCollections 2024-05-16 21:11:51 +08:00
Lim Chee Aun 9934daeb4d Handle filtered quote posts 2024-05-16 13:00:23 +08:00
Lim Chee Aun d4a0a080b5 Bump up max entries for icons 2024-05-15 19:38:28 +08:00
Lim Chee Aun bc4e3b0f72 Fix red too faint in dark mode 2024-05-14 23:39:48 +08:00
Lim Chee Aun ac760265da Fix post preview internals becoming clickable 2024-05-11 13:09:08 +08:00
Lim Chee Aun 98b0ccf032 Default to floor rounding mode 2024-05-10 12:11:57 +08:00
Lim Chee Aun 90f06c511a Test allow linking to post from generic accounts modal 2024-05-08 10:29:00 +08:00
Lim Chee Aun e7aad03279 Preliminary implementation of moderation_warning notifications 2024-05-08 10:28:34 +08:00
Lim Chee Aun 1c6b0aa0d7 Upgrade dependencies 2024-05-06 12:48:55 +08:00
Lim Chee Aun 3e1b9ff53d Apply filter context in compact status too 2024-05-02 23:29:01 +08:00
14 changed files with 1729 additions and 1472 deletions

3011
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@
"@szhsin/react-menu": "~4.1.0",
"@uidotdev/usehooks": "~2.4.1",
"compare-versions": "~6.1.0",
"dayjs": "~1.11.10",
"dayjs": "~1.11.11",
"dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2",
"fast-equals": "~5.0.1",
@ -28,18 +28,18 @@
"idb-keyval": "~6.2.1",
"just-debounce-it": "~3.2.0",
"lz-string": "~1.5.0",
"masto": "~6.7.2",
"masto": "~6.7.7",
"moize": "~6.1.6",
"p-retry": "~6.2.0",
"p-throttle": "~6.1.0",
"preact": "~10.20.2",
"preact": "~10.21.0",
"punycode": "~2.3.1",
"react-hotkeys-hook": "~4.5.0",
"react-intersection-observer": "~9.8.2",
"react-intersection-observer": "~9.10.2",
"react-quick-pinch-zoom": "~5.1.0",
"react-router-dom": "6.6.2",
"string-length": "6.0.0",
"swiped-events": "~1.1.9",
"swiped-events": "~1.2.0",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
"use-debounce": "~10.0.0",
@ -51,18 +51,18 @@
"@preact/preset-vite": "~2.8.2",
"@trivago/prettier-plugin-sort-imports": "~4.3.0",
"postcss": "~8.4.38",
"postcss-dark-theme-class": "~1.2.3",
"postcss-preset-env": "~9.5.8",
"postcss-dark-theme-class": "~1.3.0",
"postcss-preset-env": "~9.5.11",
"twitter-text": "~3.1.0",
"vite": "~5.2.10",
"vite": "~5.2.11",
"vite-plugin-generate-file": "~0.1.1",
"vite-plugin-html-config": "~1.0.11",
"vite-plugin-pwa": "~0.19.8",
"vite-plugin-pwa": "~0.20.0",
"vite-plugin-remove-console": "~2.2.0",
"workbox-cacheable-response": "~7.0.0",
"workbox-expiration": "~7.0.0",
"workbox-routing": "~7.0.0",
"workbox-strategies": "~7.0.0"
"workbox-cacheable-response": "~7.1.0",
"workbox-expiration": "~7.1.0",
"workbox-routing": "~7.1.0",
"workbox-strategies": "~7.1.0"
},
"postcss": {
"plugins": {

View file

@ -62,7 +62,7 @@ const iconsRoute = new Route(
cacheName: 'icons',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxEntries: 300,
maxAgeSeconds: 3 * 24 * 60 * 60, // 3 days
purgeOnQuotaError: true,
}),

View file

@ -1967,6 +1967,10 @@ body > .szh-menu-container {
.szh-menu
.szh-menu__item.danger:not(.szh-menu__item--disabled).szh-menu__item--hover {
background-color: var(--red-text-color);
@media (prefers-color-scheme: dark) {
background-color: var(--red-color);
}
}
.szh-menu
.szh-menu__item:not(.szh-menu__item--disabled):not(

View file

@ -186,6 +186,7 @@ function AccountInfo({
memorial,
moved,
roles,
hideCollections,
} = info || {};
let headerIsAvatar = false;
let { header, headerStatic } = info || {};
@ -677,6 +678,9 @@ function AccountInfo({
excludeRelationshipAttrs: isSelf
? ['followedBy']
: [],
blankCopy: hideCollections
? 'This user has chosen to not make this information available.'
: undefined,
};
}, 0);
}}
@ -712,6 +716,9 @@ function AccountInfo({
fetchAccounts: fetchFollowing,
instance,
excludeRelationshipAttrs: isSelf ? ['following'] : [],
blankCopy: hideCollections
? 'This user has chosen to not make this information available.'
: undefined,
};
}, 0);
}}

View file

@ -17,6 +17,21 @@
);
filter: saturate(0.5);
}
&:is(a) {
pointer-events: auto;
display: block;
text-decoration: none;
color: inherit;
&:hover {
border-color: var(--outline-hover-color);
}
> * {
pointer-events: none;
}
}
}
.accounts-list {

View file

@ -11,6 +11,7 @@ import useLocationChange from '../utils/useLocationChange';
import AccountBlock from './account-block';
import Icon from './icon';
import Link from './link';
import Loader from './loader';
import Status from './status';
@ -19,6 +20,7 @@ export default function GenericAccounts({
excludeRelationshipAttrs = [],
postID,
onClose = () => {},
blankCopy = 'Nothing to show',
}) {
const { masto, instance: currentInstance } = api();
const isCurrentInstance = instance ? instance === currentInstance : true;
@ -143,9 +145,12 @@ export default function GenericAccounts({
</header>
<main>
{post && (
<div class="post-preview">
<Link
to={`/${instance || currentInstance}/s/${post.id}`}
class="post-preview"
>
<Status status={post} size="s" readOnly />
</div>
</Link>
)}
{accounts.length > 0 ? (
<>
@ -217,7 +222,7 @@ export default function GenericAccounts({
) : uiState === 'error' ? (
<p class="ui-state">Error loading accounts</p>
) : (
<p class="ui-state insignificant">Nothing to show</p>
<p class="ui-state insignificant">{blankCopy}</p>
)}
</main>
</div>

View file

@ -166,7 +166,7 @@ function Media({
[to],
);
const remoteMediaURLObj = remoteMediaURL ? new URL(remoteMediaURL) : null;
const remoteMediaURLObj = remoteMediaURL ? getURLObj(remoteMediaURL) : null;
const isVideoMaybe =
type === 'unknown' &&
remoteMediaURLObj &&
@ -618,4 +618,13 @@ function Media({
}
}
function getURLObj(url) {
try {
// Fake base URL if url doesn't have https:// prefix
return new URL(url, location.origin);
} catch (e) {
return null;
}
}
export default Media;

View file

@ -28,6 +28,7 @@ const NOTIFICATION_ICONS = {
'admin.signup': 'account-edit',
'admin.report': 'account-warning',
severed_relationships: 'heart-break',
moderation_warning: 'alert',
emoji_reaction: 'emoji2',
'pleroma:emoji_reaction': 'emoji2',
};
@ -45,6 +46,8 @@ poll = A poll you have voted in or created has ended
update = A status you interacted with has been edited
admin.sign_up = Someone signed up (optionally sent to admins)
admin.report = A new report has been filed
severed_relationships = Severed relationships
moderation_warning = Moderation warning
*/
function emojiText(emoji, emoji_url) {
@ -91,6 +94,7 @@ const contentText = {
Lost connections with <i>{name}</i>.
</>
),
moderation_warning: <b>Moderation warning</b>,
emoji_reaction: emojiText,
'pleroma:emoji_reaction': emojiText,
};
@ -117,6 +121,17 @@ const SEVERED_RELATIONSHIPS_TEXT = {
),
};
const MODERATION_WARNING_TEXT = {
none: 'Your account has received a moderation warning.',
disable: 'Your account has been disabled.',
mark_statuses_as_sensitive:
'Some of your posts have been marked as sensitive.',
delete_statuses: 'Some of your posts have been deleted.',
sensitive: 'Your posts will be marked as sensitive from now on.',
silence: 'Your account has been limited.',
suspend: 'Your account has been suspended.',
};
const AVATARS_LIMIT = 50;
function Notification({
@ -125,8 +140,16 @@ function Notification({
isStatic,
disableContextMenu,
}) {
const { id, status, account, report, event, _accounts, _statuses } =
notification;
const {
id,
status,
account,
report,
event,
moderation_warning,
_accounts,
_statuses,
} = notification;
let { type } = notification;
// status = Attached when type of the notification is favourite, reblog, status, mention, poll, or update
@ -314,6 +337,20 @@ function Notification({
.
</div>
)}
{type === 'moderation_warning' && !!moderation_warning && (
<div>
{MODERATION_WARNING_TEXT[moderation_warning.action]}
<br />
<a
href={`/disputes/strikes/${moderation_warning.id}`}
target="_blank"
rel="noopener noreferrer"
>
Learn more <Icon icon="external" size="s" />
</a>
.
</div>
)}
</>
)}
{_accounts?.length > 1 && (

View file

@ -569,8 +569,15 @@
font-weight: bold;
vertical-align: middle;
display: inline-block;
&.horizontal {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
}
}
.status-filtered-badge.badge-meta {
.status-filtered-badge:not(.horizontal).badge-meta {
display: inline-flex;
flex-direction: column;
position: relative;
@ -584,10 +591,10 @@
border-color: var(--text-color);
background: var(--bg-color);
}
.status-filtered-badge.badge-meta > span:first-child {
.status-filtered-badge:not(.horizontal).badge-meta > span:first-child {
white-space: nowrap;
}
.status-filtered-badge.badge-meta > span + span {
.status-filtered-badge:not(.horizontal).badge-meta > span + span {
display: block;
font-size: 9px;
font-weight: normal;
@ -601,6 +608,10 @@
left: 0;
text-align: center;
}
.status-filtered-badge.horizontal.badge-meta > span + span {
font-weight: normal;
text-transform: none;
}
.status.large > .container > .content-container {
margin-left: calc(-50px - 16px);

View file

@ -303,6 +303,7 @@ function Status({
onMouseEnter: debugHover,
}}
showFollowedTags
quoted={quoted}
/>
);
}
@ -3176,6 +3177,7 @@ function FilteredStatus({
instance,
containerProps = {},
showFollowedTags,
quoted,
}) {
const snapStates = useSnapshot(states);
const {
@ -3220,7 +3222,9 @@ function FilteredStatus({
return (
<div
class={
isReblog
quoted
? ''
: isReblog
? group
? 'status-group'
: 'status-reblog'
@ -3236,7 +3240,11 @@ function FilteredStatus({
}}
{...bindLongPressPeek()}
>
<article data-state-post-id={ssKey} class="status filtered" tabindex="-1">
<article
data-state-post-id={ssKey}
class={`status filtered ${quoted ? 'status-card' : ''}`}
tabindex="-1"
>
<b
class="status-filtered-badge clickable badge-meta"
title={filterTitleStr}

View file

@ -646,7 +646,11 @@ const TimelineItem = memo(
>
<Link class="status-link timeline-item" to={url}>
{showCompact ? (
<TimelineStatusCompact status={item} instance={instance} />
<TimelineStatusCompact
status={item}
instance={instance}
filterContext={filterContext}
/>
) : useItemID ? (
<Status
statusID={statusID}
@ -820,11 +824,12 @@ function StatusCarousel({ title, class: className, children }) {
);
}
function TimelineStatusCompact({ status, instance }) {
function TimelineStatusCompact({ status, instance, filterContext }) {
const snapStates = useSnapshot(states);
const { id, visibility, language } = status;
const statusPeekText = statusPeek(status);
const sKey = statusKey(id, instance);
const filterInfo = isFiltered(status.filtered, filterContext);
return (
<article
class={`status compact-thread ${
@ -850,13 +855,24 @@ function TimelineStatusCompact({ status, instance }) {
lang={language}
dir="auto"
>
{statusPeekText}
{status.sensitive && status.spoilerText && (
{!!filterInfo ? (
<b
class="status-filtered-badge badge-meta horizontal"
title={filterInfo?.titlesStr || ''}
>
<span>Filtered</span>: <span>{filterInfo?.titlesStr || ''}</span>
</b>
) : (
<>
{' '}
<span class="spoiler-badge">
<Icon icon="eye-close" size="s" />
</span>
{statusPeekText}
{status.sensitive && status.spoilerText && (
<>
{' '}
<span class="spoiler-badge">
<Icon icon="eye-close" size="s" />
</span>
</>
)}
</>
)}
</div>

View file

@ -102,6 +102,17 @@ function Notifications({ columnMode }) {
// },
// });
// TEST: Slot in a fake notification to test 'moderation_warning'
// notifications.unshift({
// id: '123123',
// type: 'moderation_warning',
// createdAt: new Date().toISOString(),
// moderation_warning: {
// id: '1231234',
// action: 'mark_statuses_as_sensitive',
// },
// });
// console.log({ notifications });
const groupedNotifications = groupNotifications(notifications);

View file

@ -1,5 +1,6 @@
const { locale } = Intl.NumberFormat().resolvedOptions();
const shortenNumber = Intl.NumberFormat(locale, {
notation: 'compact',
roundingMode: 'floor',
}).format;
export default shortenNumber;