Handle hidden-filter first then quote states

pull/1214/head
Lim Chee Aun 2025-07-11 11:55:56 +08:00
rodzic 821060c238
commit 20ad87338a
4 zmienionych plików z 429 dodań i 316 usunięć

Wyświetl plik

@ -282,7 +282,9 @@
}
&.status-card-ghost {
border-style: dashed;
color: var(--text-insignificant-color);
border: var(--hairline-width) dashed var(--text-insignificant-color);
box-shadow: none;
}
}

Wyświetl plik

@ -378,7 +378,6 @@ function Status({
showActionsBar,
showReplyParent,
mediaFirst,
showFilteredHidden,
}) {
const { _, t, i18n } = useLingui();
const rtf = RTF(i18n.locale);
@ -500,16 +499,6 @@ function Status({
isFiltered(filtered, filterContext);
if (filterInfo?.action === 'hide') {
if (showFilteredHidden) {
return (
<div class="status-card-unfulfilled status-card-ghost">
<Icon icon="quote" />
<i>
<Trans>Post hidden by your filters</Trans>
</i>
</div>
);
}
return null;
}
@ -3886,6 +3875,22 @@ function FilteredStatus({
);
}
const handledUnfulfilledStates = [
'deleted',
'unauthorized',
'pending',
'rejected',
'revoked',
];
const unfulfilledText = {
filterHidden: msg`Post hidden by your filters`,
deleted: msg`Post removed by author.`,
unauthorized: msg`Youre not authorized to view this post.`,
pending: msg`Post pending author approval.`,
rejected: msg`Quoting not allowed by the author.`,
revoked: msg`Quoting not allowed by the author.`,
};
const QuoteStatuses = memo(({ id, instance, level = 0 }) => {
if (!id || !instance) return;
const { _ } = useLingui();
@ -3899,43 +3904,43 @@ const QuoteStatuses = memo(({ id, instance, level = 0 }) => {
if (!uniqueQuotes?.length) return;
if (level > 2) return;
const filterContext = useContext(FilterContext);
const currentAccount = getCurrentAccID();
return uniqueQuotes.map((q) => {
if (q.state === 'deleted')
let unfulfilledState;
const quoteStatus = snapStates.statuses[statusKey(q.id, q.instance)];
if (quoteStatus) {
const isSelf =
currentAccount && currentAccount === quoteStatus.account?.id;
const filterInfo =
!isSelf && isFiltered(quoteStatus.filtered, filterContext);
if (filterInfo?.action === 'hide') {
unfulfilledState = 'filterHidden';
}
}
if (!unfulfilledState) {
unfulfilledState = handledUnfulfilledStates.find(
(state) => q.state === state,
);
}
if (unfulfilledState) {
return (
<div class="status-card-unfulfilled">
<div
class={`status-card-unfulfilled ${
unfulfilledState === 'filterHidden' ? 'status-card-ghost' : ''
}`}
>
<Icon icon="quote" />
<i>
<Trans>Post removed by author.</Trans>
</i>
</div>
);
if (q.state === 'unauthorized')
return (
<div class="status-card-unfulfilled">
<Icon icon="quote" />
<i>
<Trans>Youre not authorized to view this post.</Trans>
</i>
</div>
);
if (q.state === 'pending')
return (
<div class="status-card-unfulfilled">
<Icon icon="quote" />
<i>
<Trans>Post pending author approval.</Trans>
</i>
</div>
);
if (q.state === 'rejected' || q.state === 'revoked')
return (
<div class="status-card-unfulfilled">
<Icon icon="quote" />
<i>
<Trans>Quoting not allowed by the author.</Trans>
</i>
<i>{_(unfulfilledText[unfulfilledState])}</i>
</div>
);
}
const Parent = q.native ? Fragment : LazyShazam;
return (
<Parent id={q.instance + q.id} key={q.instance + q.id}>
@ -3951,7 +3956,6 @@ const QuoteStatuses = memo(({ id, instance, level = 0 }) => {
size="s"
quoted={level + 1}
enableCommentHint
showFilteredHidden
/>
</Link>
</Parent>

283
src/locales/en.po wygenerowano
Wyświetl plik

@ -34,7 +34,7 @@ msgstr ""
#: src/components/account-block.jsx:170
#: src/components/account-info.jsx:715
#: src/components/status.jsx:601
#: src/components/status.jsx:590
msgid "Group"
msgstr ""
@ -111,11 +111,11 @@ msgstr ""
#: src/components/compose.jsx:2792
#: src/components/media-alt-modal.jsx:55
#: src/components/media-modal.jsx:363
#: src/components/status.jsx:1847
#: src/components/status.jsx:1864
#: src/components/status.jsx:1989
#: src/components/status.jsx:2610
#: src/components/status.jsx:2613
#: src/components/status.jsx:1836
#: src/components/status.jsx:1853
#: src/components/status.jsx:1978
#: src/components/status.jsx:2599
#: src/components/status.jsx:2602
#: src/pages/account-statuses.jsx:531
#: src/pages/accounts.jsx:118
#: src/pages/hashtag.jsx:203
@ -203,7 +203,7 @@ msgid "Original"
msgstr ""
#: src/components/account-info.jsx:946
#: src/components/status.jsx:2394
#: src/components/status.jsx:2383
#: src/pages/catchup.jsx:71
#: src/pages/catchup.jsx:1448
#: src/pages/catchup.jsx:2061
@ -329,30 +329,30 @@ msgid "Add/Remove from Lists"
msgstr ""
#: src/components/account-info.jsx:1490
#: src/components/status.jsx:1269
#: src/components/status.jsx:1258
msgid "Link copied"
msgstr ""
#: src/components/account-info.jsx:1493
#: src/components/status.jsx:1272
#: src/components/status.jsx:1261
msgid "Unable to copy link"
msgstr ""
#: src/components/account-info.jsx:1499
#: src/components/shortcuts-settings.jsx:1059
#: src/components/status.jsx:1278
#: src/components/status.jsx:3388
#: src/components/status.jsx:1267
#: src/components/status.jsx:3377
msgid "Copy"
msgstr ""
#: src/components/account-info.jsx:1514
#: src/components/shortcuts-settings.jsx:1077
#: src/components/status.jsx:1294
#: src/components/status.jsx:1283
msgid "Sharing doesn't seem to work."
msgstr ""
#: src/components/account-info.jsx:1520
#: src/components/status.jsx:1300
#: src/components/status.jsx:1289
msgid "Share…"
msgstr ""
@ -466,9 +466,9 @@ msgstr ""
#: src/components/shortcuts-settings.jsx:230
#: src/components/shortcuts-settings.jsx:583
#: src/components/shortcuts-settings.jsx:783
#: src/components/status.jsx:3112
#: src/components/status.jsx:3352
#: src/components/status.jsx:3861
#: src/components/status.jsx:3101
#: src/components/status.jsx:3341
#: src/components/status.jsx:3850
#: src/pages/accounts.jsx:45
#: src/pages/catchup.jsx:1584
#: src/pages/filters.jsx:225
@ -718,7 +718,7 @@ msgid "Attachment #{i} failed"
msgstr "Attachment #{i} failed"
#: src/components/compose.jsx:1221
#: src/components/status.jsx:2177
#: src/components/status.jsx:2166
#: src/components/timeline.jsx:1023
msgid "Content warning"
msgstr ""
@ -754,7 +754,7 @@ msgstr ""
#: src/components/compose.jsx:1288
#: src/components/status.jsx:100
#: src/components/status.jsx:2053
#: src/components/status.jsx:2042
msgid "Private mention"
msgstr ""
@ -791,10 +791,10 @@ msgstr "Schedule"
#: src/components/compose.jsx:1677
#: src/components/keyboard-shortcuts-help.jsx:155
#: src/components/status.jsx:1041
#: src/components/status.jsx:1827
#: src/components/status.jsx:1828
#: src/components/status.jsx:2514
#: src/components/status.jsx:1030
#: src/components/status.jsx:1816
#: src/components/status.jsx:1817
#: src/components/status.jsx:2503
msgid "Reply"
msgstr ""
@ -1016,7 +1016,7 @@ msgstr ""
#: src/components/drafts.jsx:128
#: src/components/list-add-edit.jsx:188
#: src/components/status.jsx:1444
#: src/components/status.jsx:1433
#: src/pages/filters.jsx:603
#: src/pages/scheduled-posts.jsx:369
msgid "Delete…"
@ -1225,10 +1225,10 @@ msgid "<0>l</0> or <1>f</1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:176
#: src/components/status.jsx:1049
#: src/components/status.jsx:2541
#: src/components/status.jsx:2564
#: src/components/status.jsx:2565
#: src/components/status.jsx:1038
#: src/components/status.jsx:2530
#: src/components/status.jsx:2553
#: src/components/status.jsx:2554
msgid "Boost"
msgstr ""
@ -1237,9 +1237,9 @@ msgid "<0>Shift</0> + <1>b</1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:184
#: src/components/status.jsx:1112
#: src/components/status.jsx:2589
#: src/components/status.jsx:2590
#: src/components/status.jsx:1101
#: src/components/status.jsx:2578
#: src/components/status.jsx:2579
msgid "Bookmark"
msgstr ""
@ -1304,14 +1304,14 @@ msgid "Media description"
msgstr ""
#: src/components/media-alt-modal.jsx:67
#: src/components/status.jsx:1155
#: src/components/status.jsx:1164
#: src/components/status.jsx:1144
#: src/components/status.jsx:1153
#: src/components/translation-block.jsx:239
msgid "Translate"
msgstr ""
#: src/components/media-alt-modal.jsx:78
#: src/components/status.jsx:1183
#: src/components/status.jsx:1172
msgid "Speak"
msgstr ""
@ -1348,9 +1348,9 @@ msgid "Filtered: {filterTitleStr}"
msgstr ""
#: src/components/media-post.jsx:133
#: src/components/status.jsx:3691
#: src/components/status.jsx:3787
#: src/components/status.jsx:3865
#: src/components/status.jsx:3680
#: src/components/status.jsx:3776
#: src/components/status.jsx:3854
#: src/components/timeline.jsx:1012
#: src/pages/catchup.jsx:75
#: src/pages/catchup.jsx:1880
@ -1662,8 +1662,8 @@ msgid "[Unknown notification type: {type}]"
msgstr ""
#: src/components/notification.jsx:451
#: src/components/status.jsx:1126
#: src/components/status.jsx:1136
#: src/components/status.jsx:1115
#: src/components/status.jsx:1125
msgid "Boosted/Liked by…"
msgstr ""
@ -1993,7 +1993,7 @@ msgid "Move down"
msgstr ""
#: src/components/shortcuts-settings.jsx:379
#: src/components/status.jsx:1406
#: src/components/status.jsx:1395
#: src/pages/list.jsx:195
msgid "Edit"
msgstr ""
@ -2192,345 +2192,346 @@ msgstr ""
msgid "Import/export settings from/to instance server (Very experimental)"
msgstr ""
#: src/components/status.jsx:508
msgid "Post hidden by your filters"
msgstr "Post hidden by your filters"
#: src/components/status.jsx:625
#: src/components/status.jsx:614
msgid "<0/> <1>boosted</1>"
msgstr ""
#: src/components/status.jsx:724
#: src/components/status.jsx:713
msgid "Sorry, your current logged-in instance can't interact with this post from another instance."
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:878
#: src/components/status.jsx:867
msgid "Unliked @{0}'s post"
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:879
#: src/components/status.jsx:868
msgid "Liked @{0}'s post"
msgstr "Liked @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:918
#: src/components/status.jsx:907
msgid "Unbookmarked @{0}'s post"
msgstr "Unbookmarked @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:919
#: src/components/status.jsx:908
msgid "Bookmarked @{0}'s post"
msgstr "Bookmarked @{0}'s post"
#: src/components/status.jsx:1018
#: src/components/status.jsx:1007
msgid "Some media have no descriptions."
msgstr ""
#. placeholder {0}: rtf.format(-statusMonthsAgo, 'month')
#: src/components/status.jsx:1025
#: src/components/status.jsx:1014
msgid "Old post (<0>{0}</0>)"
msgstr ""
#: src/components/status.jsx:1049
#: src/components/status.jsx:1089
#: src/components/status.jsx:2541
#: src/components/status.jsx:2564
#: src/components/status.jsx:1038
#: src/components/status.jsx:1078
#: src/components/status.jsx:2530
#: src/components/status.jsx:2553
msgid "Unboost"
msgstr ""
#: src/components/status.jsx:1065
#: src/components/status.jsx:2556
#: src/components/status.jsx:1054
#: src/components/status.jsx:2545
msgid "Quote"
msgstr ""
#. placeholder {0}: username || acct
#: src/components/status.jsx:1077
#: src/components/status.jsx:1543
#: src/components/status.jsx:1066
#: src/components/status.jsx:1532
msgid "Unboosted @{0}'s post"
msgstr "Unboosted @{0}'s post"
#. placeholder {0}: username || acct
#: src/components/status.jsx:1078
#: src/components/status.jsx:1544
#: src/components/status.jsx:1067
#: src/components/status.jsx:1533
msgid "Boosted @{0}'s post"
msgstr "Boosted @{0}'s post"
#: src/components/status.jsx:1090
#: src/components/status.jsx:1079
msgid "Boost…"
msgstr ""
#: src/components/status.jsx:1102
#: src/components/status.jsx:1837
#: src/components/status.jsx:2577
#: src/components/status.jsx:1091
#: src/components/status.jsx:1826
#: src/components/status.jsx:2566
msgid "Unlike"
msgstr ""
#: src/components/status.jsx:1103
#: src/components/status.jsx:1837
#: src/components/status.jsx:1838
#: src/components/status.jsx:2577
#: src/components/status.jsx:2578
#: src/components/status.jsx:1092
#: src/components/status.jsx:1826
#: src/components/status.jsx:1827
#: src/components/status.jsx:2566
#: src/components/status.jsx:2567
msgid "Like"
msgstr ""
#: src/components/status.jsx:1112
#: src/components/status.jsx:2589
#: src/components/status.jsx:1101
#: src/components/status.jsx:2578
msgid "Unbookmark"
msgstr ""
#: src/components/status.jsx:1195
#: src/components/status.jsx:1184
msgid "Post text copied"
msgstr "Post text copied"
#: src/components/status.jsx:1198
#: src/components/status.jsx:1187
msgid "Unable to copy post text"
msgstr "Unable to copy post text"
#: src/components/status.jsx:1204
#: src/components/status.jsx:1193
msgid "Copy post text"
msgstr "Copy post text"
#. placeholder {0}: username || acct
#: src/components/status.jsx:1222
#: src/components/status.jsx:1211
msgid "View post by <0>@{0}</0>"
msgstr ""
#: src/components/status.jsx:1243
#: src/components/status.jsx:1232
msgid "Show Edit History"
msgstr ""
#: src/components/status.jsx:1246
#: src/components/status.jsx:1235
msgid "Edited: {editedDateText}"
msgstr ""
#: src/components/status.jsx:1313
#: src/components/status.jsx:3357
#: src/components/status.jsx:1302
#: src/components/status.jsx:3346
msgid "Embed post"
msgstr ""
#: src/components/status.jsx:1327
#: src/components/status.jsx:1316
msgid "Conversation unmuted"
msgstr ""
#: src/components/status.jsx:1327
#: src/components/status.jsx:1316
msgid "Conversation muted"
msgstr ""
#: src/components/status.jsx:1333
#: src/components/status.jsx:1322
msgid "Unable to unmute conversation"
msgstr ""
#: src/components/status.jsx:1334
#: src/components/status.jsx:1323
msgid "Unable to mute conversation"
msgstr ""
#: src/components/status.jsx:1343
#: src/components/status.jsx:1332
msgid "Unmute conversation"
msgstr ""
#: src/components/status.jsx:1350
#: src/components/status.jsx:1339
msgid "Mute conversation"
msgstr ""
#: src/components/status.jsx:1366
#: src/components/status.jsx:1355
msgid "Post unpinned from profile"
msgstr ""
#: src/components/status.jsx:1367
#: src/components/status.jsx:1356
msgid "Post pinned to profile"
msgstr ""
#: src/components/status.jsx:1372
#: src/components/status.jsx:1361
msgid "Unable to unpin post"
msgstr ""
#: src/components/status.jsx:1372
#: src/components/status.jsx:1361
msgid "Unable to pin post"
msgstr ""
#: src/components/status.jsx:1381
#: src/components/status.jsx:1370
msgid "Unpin from profile"
msgstr ""
#: src/components/status.jsx:1388
#: src/components/status.jsx:1377
msgid "Pin to profile"
msgstr ""
#: src/components/status.jsx:1417
#: src/components/status.jsx:1406
msgid "Delete this post?"
msgstr ""
#: src/components/status.jsx:1433
#: src/components/status.jsx:1422
msgid "Post deleted"
msgstr ""
#: src/components/status.jsx:1436
#: src/components/status.jsx:1425
msgid "Unable to delete post"
msgstr ""
#: src/components/status.jsx:1464
#: src/components/status.jsx:1453
msgid "Report post…"
msgstr ""
#: src/components/status.jsx:1838
#: src/components/status.jsx:1874
#: src/components/status.jsx:2578
#: src/components/status.jsx:1827
#: src/components/status.jsx:1863
#: src/components/status.jsx:2567
msgid "Liked"
msgstr ""
#: src/components/status.jsx:1871
#: src/components/status.jsx:2565
#: src/components/status.jsx:1860
#: src/components/status.jsx:2554
msgid "Boosted"
msgstr ""
#: src/components/status.jsx:1881
#: src/components/status.jsx:2590
#: src/components/status.jsx:1870
#: src/components/status.jsx:2579
msgid "Bookmarked"
msgstr ""
#: src/components/status.jsx:1885
#: src/components/status.jsx:1874
msgid "Pinned"
msgstr ""
#: src/components/status.jsx:1931
#: src/components/status.jsx:2402
#: src/components/status.jsx:1920
#: src/components/status.jsx:2391
msgid "Deleted"
msgstr ""
#: src/components/status.jsx:1972
#: src/components/status.jsx:1961
msgid "{repliesCount, plural, one {# reply} other {# replies}}"
msgstr ""
#. placeholder {0}: snapStates.statusThreadNumber[sKey] ? ` ${snapStates.statusThreadNumber[sKey]}/X` : ''
#: src/components/status.jsx:2062
#: src/components/status.jsx:2051
msgid "Thread{0}"
msgstr ""
#: src/components/status.jsx:2140
#: src/components/status.jsx:2202
#: src/components/status.jsx:2298
#: src/components/status.jsx:2129
#: src/components/status.jsx:2191
#: src/components/status.jsx:2287
msgid "Show less"
msgstr ""
#: src/components/status.jsx:2140
#: src/components/status.jsx:2202
#: src/components/status.jsx:2129
#: src/components/status.jsx:2191
msgid "Show content"
msgstr ""
#. placeholder {0}: filterInfo.titlesStr
#. placeholder {0}: filterInfo?.titlesStr
#: src/components/status.jsx:2294
#: src/components/status.jsx:2283
#: src/pages/catchup.jsx:1879
msgid "Filtered: {0}"
msgstr "Filtered: {0}"
#: src/components/status.jsx:2298
#: src/components/status.jsx:2287
msgid "Show media"
msgstr ""
#: src/components/status.jsx:2438
#: src/components/status.jsx:2427
msgid "Edited"
msgstr ""
#: src/components/status.jsx:2515
#: src/components/status.jsx:2504
msgid "Comments"
msgstr ""
#. More from [Author]
#: src/components/status.jsx:2815
#: src/components/status.jsx:2804
msgid "More from <0/>"
msgstr "More from <0/>"
#: src/components/status.jsx:3117
#: src/components/status.jsx:3106
msgid "Edit History"
msgstr ""
#: src/components/status.jsx:3121
#: src/components/status.jsx:3110
msgid "Failed to load history"
msgstr ""
#: src/components/status.jsx:3126
#: src/components/status.jsx:3115
#: src/pages/annual-report.jsx:45
msgid "Loading…"
msgstr ""
#: src/components/status.jsx:3362
#: src/components/status.jsx:3351
msgid "HTML Code"
msgstr ""
#: src/components/status.jsx:3379
#: src/components/status.jsx:3368
msgid "HTML code copied"
msgstr ""
#: src/components/status.jsx:3382
#: src/components/status.jsx:3371
msgid "Unable to copy HTML code"
msgstr ""
#: src/components/status.jsx:3394
#: src/components/status.jsx:3383
msgid "Media attachments:"
msgstr ""
#: src/components/status.jsx:3416
#: src/components/status.jsx:3405
msgid "Account Emojis:"
msgstr ""
#: src/components/status.jsx:3447
#: src/components/status.jsx:3492
#: src/components/status.jsx:3436
#: src/components/status.jsx:3481
msgid "static URL"
msgstr ""
#: src/components/status.jsx:3461
#: src/components/status.jsx:3450
msgid "Emojis:"
msgstr ""
#: src/components/status.jsx:3506
#: src/components/status.jsx:3495
msgid "Notes:"
msgstr ""
#: src/components/status.jsx:3510
#: src/components/status.jsx:3499
msgid "This is static, unstyled and scriptless. You may need to apply your own styles and edit as needed."
msgstr ""
#: src/components/status.jsx:3516
#: src/components/status.jsx:3505
msgid "Polls are not interactive, becomes a list with vote counts."
msgstr ""
#: src/components/status.jsx:3521
#: src/components/status.jsx:3510
msgid "Media attachments can be images, videos, audios or any file types."
msgstr ""
#: src/components/status.jsx:3527
#: src/components/status.jsx:3516
msgid "Post could be edited or deleted later."
msgstr ""
#: src/components/status.jsx:3533
#: src/components/status.jsx:3522
msgid "Preview"
msgstr ""
#: src/components/status.jsx:3542
#: src/components/status.jsx:3531
msgid "Note: This preview is lightly styled."
msgstr ""
#. [Name] [Visibility icon] boosted
#: src/components/status.jsx:3795
#: src/components/status.jsx:3784
msgid "<0/> <1/> boosted"
msgstr ""
#: src/components/status.jsx:3908
#: src/components/status.jsx:3886
msgid "Post hidden by your filters"
msgstr "Post hidden by your filters"
#: src/components/status.jsx:3887
msgid "Post removed by author."
msgstr "Post removed by author."
#: src/components/status.jsx:3917
#: src/components/status.jsx:3888
msgid "Youre not authorized to view this post."
msgstr "Youre not authorized to view this post."
#: src/components/status.jsx:3926
#: src/components/status.jsx:3889
msgid "Post pending author approval."
msgstr "Post pending author approval."
#: src/components/status.jsx:3935
#: src/components/status.jsx:3890
#: src/components/status.jsx:3891
msgid "Quoting not allowed by the author."
msgstr "Quoting not allowed by the author."

Wyświetl plik

@ -311,6 +311,7 @@ const INITIAL_STATE = {
showQuotes: false,
quotesCount: '1',
quoteNestingLevel: '0',
quoteState: 'accepted', // State for all quote posts
size: 'medium',
filters: [false, false, false], // hide, blur, warn
quoteFilters: [false, false, false], // hide, blur, warn for quotes
@ -398,6 +399,7 @@ export default function Sandbox() {
showQuotes: toggleState.showQuotes,
quotesCount: toggleState.quotesCount,
quoteNestingLevel: toggleState.quoteNestingLevel,
quoteState: toggleState.quoteState,
size: toggleState.size,
filters: toggleState.filters,
quoteFilters: toggleState.quoteFilters,
@ -528,162 +530,173 @@ export default function Sandbox() {
id: quoteId,
instance: DEFAULT_INSTANCE,
url: `https://example.social/s/${quoteId}`, // Include URL to ensure uniqueness check works
state:
toggleState.quoteState === 'accepted'
? undefined
: toggleState.quoteState, // Only set state if not accepted
};
// First, delete any existing status with this ID to avoid duplicates
delete states.statuses[quoteId];
const quoteStatusKey = statusKey(quoteId, DEFAULT_INSTANCE);
delete states.statuses[quoteStatusKey];
// Create the actual status object that will be retrieved by QuoteStatuses
const quoteStatus = {
id: quoteId,
content: `<p>This is quote post ${i + 1}${i % 2 === 0 ? '' : ' with some extra text'}</p>`,
account: {
id: `quote-account-${i}`,
username: `quote${i}`,
name: `Quote User ${i}`,
avatar: '/logo-192.png',
acct: `quote${i}@example.social`,
url: `https://example.social/@quote${i}`,
},
visibility: 'public',
createdAt: new Date(Date.now() - i * 3600000).toISOString(), // Each post 1 hour older
emojis: [],
// First quote post should be plain (no media, no poll)
mediaAttachments:
i > 0 && i % 2 === 0
? [
{
// Only non-first posts can have media (every 3rd post after the 1st)
id: `quote-media-${i}`,
type: 'image',
url: `https://picsum.photos/seed/quote-${i}/600/400`,
previewUrl: `https://picsum.photos/seed/quote-${i}/300/200`,
meta: {
original: { width: 600, height: 400 },
small: { width: 300, height: 200 },
},
},
]
: [],
poll:
i > 0 && i % 3 === 0
? {
// Only non-first posts can have polls (every 4th post after the 1st)
id: `quote-poll-${i}`,
options: [
{
title: 'Option A',
votesCount: Math.floor(Math.random() * 50),
},
{
title: 'Option B',
votesCount: Math.floor(Math.random() * 50),
},
],
expiresAt: new Date(
Date.now() + 24 * 60 * 60 * 1000,
).toISOString(),
multiple: false,
votesCount: Math.floor(Math.random() * 100),
}
: null,
};
// Add filtering to quote posts if enabled
if (
toggleState.quoteFilters &&
toggleState.quoteFilters.some((f) => f)
) {
quoteStatus.filtered = toggleState.quoteFilters
.map((enabled, filterIndex) => {
if (!enabled) return null;
const filterTypes = ['hide', 'blur', 'warn'];
return {
filter: {
id: `quote-filter-${i}-${filterIndex}`,
title: `Quote ${filterTypes[filterIndex]} filter`,
context: ['home', 'public', 'thread', 'account'],
filterAction: filterTypes[filterIndex],
},
keywordMatches: [],
statusMatches: [],
};
})
.filter(Boolean);
}
// Assign the quote status to the states
states.statuses[quoteId] = quoteStatus;
// If nesting level > 0, add nested quotes to each quote post
if (nestingLevel > 0 && i % 2 === 0) {
// Add nested quotes to every other quote - use stable ID
const nestedQuoteId = `nested-quote-${i}-12345`;
// Add the nested quote post to states.statuses
states.statuses[nestedQuoteId] = {
id: nestedQuoteId,
content: `<p>This is a nested quote inside quote ${i + 1}</p>`,
// Create the actual status object for all quote states
// This allows filtering logic to run even for non-accepted states
{
// Create the actual status object that will be retrieved by QuoteStatuses
const quoteStatus = {
id: quoteId,
content: `<p>This is quote post ${i + 1}${i % 2 === 0 ? '' : ' with some extra text'}</p>`,
account: {
id: `nested-account-${i}`,
username: `nested${i}`,
name: `Nested User ${i}`,
id: `quote-account-${i}`,
username: `quote${i}`,
name: `Quote User ${i}`,
avatar: '/logo-192.png',
acct: `nested${i}@example.social`,
url: `https://example.social/@nested${i}`,
acct: `quote${i}@example.social`,
url: `https://example.social/@quote${i}`,
},
visibility: 'public',
createdAt: new Date(Date.now() - (i + 1) * 3600000).toISOString(),
createdAt: new Date(Date.now() - i * 3600000).toISOString(), // Each post 1 hour older
emojis: [],
mediaAttachments: [], // No media in nested quotes for simplicity
// First quote post should be plain (no media, no poll)
mediaAttachments:
i > 0 && i % 2 === 0
? [
{
// Only non-first posts can have media (every 3rd post after the 1st)
id: `quote-media-${i}`,
type: 'image',
url: `https://picsum.photos/seed/quote-${i}/600/400`,
previewUrl: `https://picsum.photos/seed/quote-${i}/300/200`,
meta: {
original: { width: 600, height: 400 },
small: { width: 300, height: 200 },
},
},
]
: [],
poll:
i > 0 && i % 3 === 0
? {
// Only non-first posts can have polls (every 4th post after the 1st)
id: `quote-poll-${i}`,
options: [
{
title: 'Option A',
votesCount: Math.floor(Math.random() * 50),
},
{
title: 'Option B',
votesCount: Math.floor(Math.random() * 50),
},
],
expiresAt: new Date(
Date.now() + 24 * 60 * 60 * 1000,
).toISOString(),
multiple: false,
votesCount: Math.floor(Math.random() * 100),
}
: null,
};
// Create reference object for nested quote - critical for proper rendering
const nestedQuoteRef = {
id: nestedQuoteId,
instance: DEFAULT_INSTANCE,
url: `https://example.social/s/${nestedQuoteId}`,
};
// Add filtering to quote posts if enabled
if (
toggleState.quoteFilters &&
toggleState.quoteFilters.some((f) => f)
) {
quoteStatus.filtered = toggleState.quoteFilters
.map((enabled, filterIndex) => {
if (!enabled) return null;
const filterTypes = ['hide', 'blur', 'warn'];
return {
filter: {
id: `quote-filter-${i}-${filterIndex}`,
title: `Quote ${filterTypes[filterIndex]} filter`,
context: ['home', 'public', 'thread', 'account'],
filterAction: filterTypes[filterIndex],
},
keywordMatches: [],
statusMatches: [],
};
})
.filter(Boolean);
}
// Add another level of nesting if specified
if (nestingLevel > 1 && i === 0) {
// Only add deepest nesting to first quote
const deepNestedId = `deep-nested-${i}-12345`;
// Assign the quote status to the states using the correct key format
states.statuses[quoteStatusKey] = quoteStatus;
states.statuses[deepNestedId] = {
id: deepNestedId,
content: `<p>This is a deeply nested quote (level 2)</p>`,
// If nesting level > 0, add nested quotes to each quote post
if (nestingLevel > 0 && i % 2 === 0) {
// Add nested quotes to every other quote - use stable ID
const nestedQuoteId = `nested-quote-${i}-12345`;
// Add the nested quote post to states.statuses
states.statuses[nestedQuoteId] = {
id: nestedQuoteId,
content: `<p>This is a nested quote inside quote ${i + 1}</p>`,
account: {
id: `deep-account-${i}`,
username: `deep${i}`,
name: `Deep User ${i}`,
id: `nested-account-${i}`,
username: `nested${i}`,
name: `Nested User ${i}`,
avatar: '/logo-192.png',
acct: `deep${i}@example.social`,
url: `https://example.social/@deep${i}`,
acct: `nested${i}@example.social`,
url: `https://example.social/@nested${i}`,
},
visibility: 'public',
createdAt: new Date(
Date.now() - (i + 2) * 3600000,
Date.now() - (i + 1) * 3600000,
).toISOString(),
emojis: [],
mediaAttachments: [], // No media in nested quotes for simplicity
};
// Create deep nested reference
const deepNestedRef = {
id: deepNestedId,
// Create reference object for nested quote - critical for proper rendering
const nestedQuoteRef = {
id: nestedQuoteId,
instance: DEFAULT_INSTANCE,
url: `https://example.social/s/${deepNestedId}`,
url: `https://example.social/s/${nestedQuoteId}`,
};
// Important: Use the proper key format for the nested quote
const nestedKey = statusKey(nestedQuoteId, DEFAULT_INSTANCE);
states.statusQuotes[nestedKey] = [deepNestedRef];
}
// Add another level of nesting if specified
if (nestingLevel > 1 && i === 0) {
// Only add deepest nesting to first quote
const deepNestedId = `deep-nested-${i}-12345`;
// Add nested quote to the quote's quotes using the proper key format
const quoteKey = statusKey(quoteId, DEFAULT_INSTANCE);
states.statusQuotes[quoteKey] = [nestedQuoteRef];
}
states.statuses[deepNestedId] = {
id: deepNestedId,
content: `<p>This is a deeply nested quote (level 2)</p>`,
account: {
id: `deep-account-${i}`,
username: `deep${i}`,
name: `Deep User ${i}`,
avatar: '/logo-192.png',
acct: `deep${i}@example.social`,
url: `https://example.social/@deep${i}`,
},
visibility: 'public',
createdAt: new Date(
Date.now() - (i + 2) * 3600000,
).toISOString(),
emojis: [],
};
// Create deep nested reference
const deepNestedRef = {
id: deepNestedId,
instance: DEFAULT_INSTANCE,
url: `https://example.social/s/${deepNestedId}`,
};
// Important: Use the proper key format for the nested quote
const nestedKey = statusKey(nestedQuoteId, DEFAULT_INSTANCE);
states.statusQuotes[nestedKey] = [deepNestedRef];
}
// Add nested quote to the quote's quotes using the proper key format
const quoteKey = statusKey(quoteId, DEFAULT_INSTANCE);
states.statusQuotes[quoteKey] = [nestedQuoteRef];
}
} // Close the quote status creation block
return quoteRef;
});
@ -699,6 +712,7 @@ export default function Sandbox() {
toggleState.showQuotes,
toggleState.quotesCount,
toggleState.quoteNestingLevel,
toggleState.quoteState,
toggleState.quoteFilters,
]);
@ -1251,6 +1265,98 @@ export default function Sandbox() {
/>
</label>
</li>
<li>
<span>Quote state</span>
<ul>
<li>
<label>
<input
type="radio"
name="quoteState"
value="accepted"
checked={toggleState.quoteState === 'accepted'}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Accepted</span>
</label>
</li>
<li>
<label>
<input
type="radio"
name="quoteState"
value="deleted"
checked={toggleState.quoteState === 'deleted'}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Deleted</span>
</label>
</li>
<li>
<label>
<input
type="radio"
name="quoteState"
value="unauthorized"
checked={
toggleState.quoteState === 'unauthorized'
}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Unauthorized</span>
</label>
</li>
<li>
<label>
<input
type="radio"
name="quoteState"
value="pending"
checked={toggleState.quoteState === 'pending'}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Pending</span>
</label>
</li>
<li>
<label>
<input
type="radio"
name="quoteState"
value="rejected"
checked={toggleState.quoteState === 'rejected'}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Rejected</span>
</label>
</li>
<li>
<label>
<input
type="radio"
name="quoteState"
value="revoked"
checked={toggleState.quoteState === 'revoked'}
onChange={(e) => {
updateToggles({ quoteState: e.target.value });
}}
/>
<span>Revoked</span>
</label>
</li>
</ul>
</li>
<li>
<b>Quote Filters</b>
<ul>