this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Experiment showing X months/years later

+152 -38
+29
src/app.css
··· 898 898 } 899 899 } 900 900 901 + .timeline.contextual .time-gap { 902 + padding-block: calc(24px + var(--time-gap-range, 0) * 4px) !important; 903 + padding-inline-start: calc(var(--line-start) + 16px); 904 + padding-inline-end: 16px; 905 + color: var(--text-insignificant-color); 906 + font-style: italic; 907 + font-size: 0.9em; 908 + position: relative; 909 + 910 + &:before { 911 + content: none !important; 912 + } 913 + &:after { 914 + content: ''; 915 + position: absolute; 916 + inset-block: 0; 917 + inset-inline-start: var(--line-start); 918 + width: var(--line-width); 919 + background-image: repeating-linear-gradient( 920 + to bottom, 921 + transparent, 922 + transparent var(--line-width), 923 + var(--bg-color) var(--line-width), 924 + var(--bg-color) calc(var(--line-width) * 2) 925 + ); 926 + mask-image: linear-gradient(to bottom, transparent, black, transparent); 927 + } 928 + } 929 + 901 930 .timeline-deck.compact .status { 902 931 max-height: max(25vh, 160px); 903 932 overflow: hidden;
+40 -32
src/locales/en.po
··· 119 119 #: src/pages/list.jsx:171 120 120 #: src/pages/public.jsx:116 121 121 #: src/pages/scheduled-posts.jsx:89 122 - #: src/pages/status.jsx:1323 122 + #: src/pages/status.jsx:1401 123 123 #: src/pages/trending.jsx:474 124 124 msgid "More" 125 125 msgstr "" ··· 221 221 #: src/pages/catchup.jsx:71 222 222 #: src/pages/catchup.jsx:1448 223 223 #: src/pages/catchup.jsx:2061 224 - #: src/pages/status.jsx:1040 225 - #: src/pages/status.jsx:1670 224 + #: src/pages/status.jsx:1046 225 + #: src/pages/status.jsx:1748 226 226 msgid "Replies" 227 227 msgstr "" 228 228 ··· 490 490 #: src/pages/notifications.jsx:942 491 491 #: src/pages/scheduled-posts.jsx:259 492 492 #: src/pages/settings.jsx:90 493 - #: src/pages/status.jsx:1410 493 + #: src/pages/status.jsx:1488 494 494 msgid "Close" 495 495 msgstr "" 496 496 ··· 1101 1101 #: src/pages/list.jsx:321 1102 1102 #: src/pages/notifications.jsx:922 1103 1103 #: src/pages/search.jsx:562 1104 - #: src/pages/status.jsx:1443 1104 + #: src/pages/status.jsx:1521 1105 1105 msgid "Show more…" 1106 1106 msgstr "" 1107 1107 ··· 1506 1506 #: src/components/nav-menu.jsx:326 1507 1507 #: src/pages/login.jsx:32 1508 1508 #: src/pages/login.jsx:199 1509 - #: src/pages/status.jsx:940 1509 + #: src/pages/status.jsx:946 1510 1510 #: src/pages/welcome.jsx:65 1511 1511 msgid "Log in" 1512 1512 msgstr "" ··· 1728 1728 #: src/components/poll.jsx:208 1729 1729 #: src/components/poll.jsx:210 1730 1730 #: src/pages/scheduled-posts.jsx:100 1731 - #: src/pages/status.jsx:1312 1732 - #: src/pages/status.jsx:1335 1731 + #: src/pages/status.jsx:1390 1732 + #: src/pages/status.jsx:1413 1733 1733 msgid "Refresh" 1734 1734 msgstr "" 1735 1735 ··· 2613 2613 #: src/components/timeline.jsx:587 2614 2614 #: src/pages/home.jsx:228 2615 2615 #: src/pages/notifications.jsx:898 2616 - #: src/pages/status.jsx:1093 2617 - #: src/pages/status.jsx:1472 2616 + #: src/pages/status.jsx:1099 2617 + #: src/pages/status.jsx:1550 2618 2618 msgid "Try again" 2619 2619 msgstr "" 2620 2620 ··· 3929 3929 msgstr "" 3930 3930 3931 3931 #. js-lingui-explicit-id 3932 - #: src/pages/status.jsx:673 3933 - #: src/pages/status.jsx:1237 3932 + #: src/pages/status.jsx:679 3933 + #: src/pages/status.jsx:1315 3934 3934 msgid "post.title" 3935 3935 msgstr "Post" 3936 3936 3937 - #: src/pages/status.jsx:927 3937 + #: src/pages/status.jsx:933 3938 3938 msgid "You're not logged in. Interactions (reply, boost, etc) are not possible." 3939 3939 msgstr "" 3940 3940 3941 - #: src/pages/status.jsx:947 3941 + #: src/pages/status.jsx:953 3942 3942 msgid "This post is from another instance (<0>{instance}</0>). Interactions (reply, boost, etc) are not possible." 3943 3943 msgstr "" 3944 3944 3945 - #: src/pages/status.jsx:975 3945 + #: src/pages/status.jsx:981 3946 3946 msgid "Error: {e}" 3947 3947 msgstr "" 3948 3948 3949 - #: src/pages/status.jsx:982 3949 + #: src/pages/status.jsx:988 3950 3950 msgid "Switch to my instance to enable interactions" 3951 3951 msgstr "" 3952 3952 3953 - #: src/pages/status.jsx:1084 3953 + #: src/pages/status.jsx:1090 3954 3954 msgid "Unable to load replies." 3955 3955 msgstr "" 3956 3956 3957 - #: src/pages/status.jsx:1197 3957 + #: src/pages/status.jsx:1146 3958 + msgid "{months, plural, one {# month later} other {# months later}}" 3959 + msgstr "{months, plural, one {# month later} other {# months later}}" 3960 + 3961 + #: src/pages/status.jsx:1152 3962 + msgid "{years, plural, one {# year later} other {# years later}}" 3963 + msgstr "{years, plural, one {# year later} other {# years later}}" 3964 + 3965 + #: src/pages/status.jsx:1275 3958 3966 msgid "Back" 3959 3967 msgstr "" 3960 3968 3961 - #: src/pages/status.jsx:1228 3969 + #: src/pages/status.jsx:1306 3962 3970 msgid "Go to main post" 3963 3971 msgstr "" 3964 3972 3965 3973 #. placeholder {0}: ancestors.length 3966 - #: src/pages/status.jsx:1251 3974 + #: src/pages/status.jsx:1329 3967 3975 msgid "{0} posts above ‒ Go to top" 3968 3976 msgstr "" 3969 3977 3970 - #: src/pages/status.jsx:1299 3971 - #: src/pages/status.jsx:1362 3978 + #: src/pages/status.jsx:1377 3979 + #: src/pages/status.jsx:1440 3972 3980 msgid "Switch to Side Peek view" 3973 3981 msgstr "" 3974 3982 3975 - #: src/pages/status.jsx:1363 3983 + #: src/pages/status.jsx:1441 3976 3984 msgid "Switch to Full view" 3977 3985 msgstr "" 3978 3986 3979 - #: src/pages/status.jsx:1381 3987 + #: src/pages/status.jsx:1459 3980 3988 msgid "Show all sensitive content" 3981 3989 msgstr "" 3982 3990 3983 - #: src/pages/status.jsx:1386 3991 + #: src/pages/status.jsx:1464 3984 3992 msgid "Experimental" 3985 3993 msgstr "" 3986 3994 3987 - #: src/pages/status.jsx:1395 3995 + #: src/pages/status.jsx:1473 3988 3996 msgid "Unable to switch" 3989 3997 msgstr "" 3990 3998 3991 3999 #. placeholder {0}: punycode.toUnicode( postInstance, ) 3992 - #: src/pages/status.jsx:1402 4000 + #: src/pages/status.jsx:1480 3993 4001 msgid "Switch to post's instance ({0})" 3994 4002 msgstr "Switch to post's instance ({0})" 3995 4003 3996 - #: src/pages/status.jsx:1405 4004 + #: src/pages/status.jsx:1483 3997 4005 msgid "Switch to post's instance" 3998 4006 msgstr "" 3999 4007 4000 - #: src/pages/status.jsx:1463 4008 + #: src/pages/status.jsx:1541 4001 4009 msgid "Unable to load post" 4002 4010 msgstr "" 4003 4011 4004 4012 #. placeholder {0}: replies.length 4005 4013 #. placeholder {1}: shortenNumber(replies.length) 4006 - #: src/pages/status.jsx:1600 4014 + #: src/pages/status.jsx:1678 4007 4015 msgid "{0, plural, one {# reply} other {<0>{1}</0> replies}}" 4008 4016 msgstr "" 4009 4017 4010 4018 #. placeholder {0}: shortenNumber(totalComments) 4011 - #: src/pages/status.jsx:1618 4019 + #: src/pages/status.jsx:1696 4012 4020 msgid "{totalComments, plural, one {# comment} other {<0>{0}</0> comments}}" 4013 4021 msgstr "" 4014 4022 4015 - #: src/pages/status.jsx:1640 4023 + #: src/pages/status.jsx:1718 4016 4024 msgid "View post with its replies" 4017 4025 msgstr "" 4018 4026
+83 -6
src/pages/status.jsx
··· 1 1 import './status.css'; 2 2 3 + import { plural } from '@lingui/core/macro'; 3 4 import { Plural, Trans, useLingui } from '@lingui/react/macro'; 4 - import { Menu, MenuDivider, MenuHeader, MenuItem } from '@szhsin/react-menu'; 5 + import { MenuDivider, MenuHeader, MenuItem } from '@szhsin/react-menu'; 5 6 import debounce from 'just-debounce-it'; 6 7 import pRetry from 'p-retry'; 7 8 import { memo } from 'preact/compat'; ··· 75 76 import { ThreadCountContext } from '../utils/thread-count-context'; 76 77 77 78 function StatusPage(params) { 78 - const { t } = useLingui(); 79 79 const { id } = params; 80 80 const { masto, instance } = api({ instance: params.instance }); 81 81 const snapStates = useSnapshot(states); ··· 275 275 return new Date(b.created_at) - new Date(a.created_at); 276 276 } 277 277 278 + const MONTH_IN_MS = 1000 * 60 * 60 * 24 * 30; 279 + 278 280 function StatusThread({ id, closeLink = '/', instance: propInstance }) { 279 281 const { t } = useLingui(); 280 282 const [searchParams, setSearchParams] = useSearchParams(); ··· 497 499 weight: calcStatusWeight(s), 498 500 level: 1, 499 501 replies: expandReplies(s.__replies, 1), 502 + createdAt: s.createdAt, 500 503 })); 501 504 const allStatuses = [ 502 505 ...ancestors.map((s) => ({ ··· 507 510 account: s.account, 508 511 repliesCount: s.repliesCount, 509 512 weight: calcStatusWeight(s), 513 + createdAt: s.createdAt, 510 514 })), 511 515 { 512 516 id, 513 517 accountID: heroStatus.account.id, 514 518 weight: calcStatusWeight(heroStatus), 519 + createdAt: heroStatus.createdAt, 515 520 }, 516 521 ...mappedNestedDescendants, 517 522 ]; ··· 1134 1139 return ids.map((id) => statusKey(id, instance)); 1135 1140 }, [showMore, statuses, limit, instance]); 1136 1141 1137 - const statusesList = useMemo( 1138 - () => statuses.slice(0, limit).map(renderStatus), 1139 - [statuses, limit, renderStatus], 1140 - ); 1142 + // Helper function to format time differences between two dates 1143 + function formatTimeGap(months) { 1144 + if (months < 12) { 1145 + return plural(months, { 1146 + one: '# month later', 1147 + other: '# months later', 1148 + }); 1149 + } else { 1150 + const years = Math.floor(months / 12); 1151 + return plural(years, { 1152 + one: '# year later', 1153 + other: '# years later', 1154 + }); 1155 + } 1156 + } 1157 + 1158 + const statusesList = useMemo(() => { 1159 + const result = []; 1160 + const slicedStatuses = statuses.slice(0, limit); 1161 + 1162 + for (let i = 0; i < slicedStatuses.length; i++) { 1163 + const status = slicedStatuses[i]; 1164 + 1165 + // Add time gap indicator if needed 1166 + if (i > 0) { 1167 + const prevStatus = slicedStatuses[i - 1]; 1168 + 1169 + const { createdAt, descendant, thread, id } = status; 1170 + 1171 + if (prevStatus?.createdAt && createdAt) { 1172 + const currentDate = Date.parse(createdAt); 1173 + if (isFinite(currentDate) && currentDate > MONTH_IN_MS) { 1174 + const prevDate = Date.parse(prevStatus.createdAt); 1175 + 1176 + if (prevDate && isFinite(prevDate)) { 1177 + const { ancestor, id: prevID } = prevStatus; 1178 + const timeDiff = currentDate - prevDate; 1179 + const monthsDiff = ~~(timeDiff / MONTH_IN_MS); 1180 + 1181 + if (monthsDiff > 0) { 1182 + result.push( 1183 + <li 1184 + key={`time-gap-${id}-${prevID}`} 1185 + style={{ 1186 + '--time-gap-range': Math.min(12, monthsDiff), 1187 + }} 1188 + class={`time-gap ${ancestor ? 'ancestor' : ''} ${descendant ? 'descendant' : ''} ${ 1189 + thread ? 'thread' : '' 1190 + }`} 1191 + > 1192 + {formatTimeGap(monthsDiff)} 1193 + </li>, 1194 + ); 1195 + } else { 1196 + // NOTE: For testing purposes 1197 + // result.push( 1198 + // <li 1199 + // key={`time-gap-${id}`} 1200 + // class={`time-gap ${ancestor ? 'ancestor' : ''} ${descendant ? 'descendant' : ''} ${ 1201 + // thread ? 'thread' : '' 1202 + // }`} 1203 + // > 1204 + // One eternity later 1205 + // </li>, 1206 + // ); 1207 + } 1208 + } 1209 + } 1210 + } 1211 + } 1212 + 1213 + result.push(renderStatus(status, i)); 1214 + } 1215 + 1216 + return result; 1217 + }, [statuses, limit, renderStatus]); 1141 1218 1142 1219 // If there's spoiler in hero status, auto-expand it 1143 1220 useEffect(() => {