Merge commit 'd7bb36a64a842dfac52fb1cfb0fcd6646981fb38' into glitch-soc/merge-4.6

This commit is contained in:
Claire committed 2026-07-19 11:29:44 +02:00
commit 1285172f2a
17 files changed
+132 -36

No files matched your search

@@ -99,7 +99,7 @@
margin: 12px 16px;
padding: 8px;
transition: border-color 0.2s ease-in-out;
width: calc(100% - 48px);
width: calc(100% - 32px);
&:hover,
&:focus {
@@ -72,7 +72,7 @@ const DirectTimeline = ({ columnId, multiColumn }) => {
scrollKey={`direct_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
bindToDocument={!multiColumn}
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='https://docs.joinmastodon.org/user/posting/#private' rel='noreferrer' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
alwaysPrepend
/>
@@ -15,16 +15,21 @@ import { IconButton } from 'mastodon/components/icon_button';
import { injectIntl } from '@/mastodon/components/intl';
import Option from 'mastodon/features/report/components/option';
import { languages as preloadedLanguages } from 'mastodon/initial_state';
import { selectTimelinesByAccount } from '@/mastodon/selectors/timelines';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
});
const getAccountLanguages = createSelector([
(state, accountId) => state.getIn(['timelines', `account:${accountId}`, 'items'], ImmutableList()),
state => state.get('statuses'),
], (statusIds, statuses) =>
ImmutableSet(statusIds.map(statusId => statuses.get(statusId)).filter(status => !status.get('reblog')).map(status => status.get('language'))));
const getAccountLanguages = createSelector(
[selectTimelinesByAccount, (state) => state.get('statuses')],
(timelines, statuses) => ImmutableSet(
timelines
.reduce((statusIds, timeline) => statusIds.concat(timeline.get('items')), ImmutableList())
.map(statusId => statuses.get(statusId))
.filter(status => !status.get('reblog'))
.map(status => status.get('language'))
));
const mapStateToProps = (state, { accountId }) => ({
acct: state.getIn(['accounts', accountId, 'acct']),
@@ -37,7 +42,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
onSubmit (languages) {
dispatch(followAccount(accountId, { languages }));
},
});
class SubscribedLanguagesModal extends ImmutablePureComponent {
@@ -52,13 +52,18 @@ export const ConfirmationModal: React.FC<
noCloseOnConfirm = false,
noFocusButton = false,
}) => {
const handleClick = useCallback(() => {
if (!noCloseOnConfirm) {
onClose();
}
const handleSubmit = useCallback(
(e: React.FormEvent) => {
e.preventDefault();
void onConfirm();
}, [onClose, onConfirm, noCloseOnConfirm]);
if (!noCloseOnConfirm) {
onClose();
}
void onConfirm();
},
[onClose, onConfirm, noCloseOnConfirm],
);
const handleSecondary = useCallback(() => {
onClose();
@@ -66,7 +71,7 @@ export const ConfirmationModal: React.FC<
}, [onClose, onSecondary]);
return (
<ModalShell onSubmit={handleClick}>
<ModalShell onSubmit={handleSubmit}>
<ModalShellBody className={className}>
{noFocusButton ? (
<NavigationFocusTarget as='h1' id={titleId}>
@@ -107,7 +112,6 @@ export const ConfirmationModal: React.FC<
{/* eslint-disable jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
<Button
type='submit'
onClick={handleClick}
loading={updating}
disabled={disabled}
autoFocus={!noFocusButton}
@@ -32,6 +32,12 @@ export const selectTimelineByParams = createAppSelector(
(key, timelines) => toTypedTimeline(timelines.get(key)),
);
export const selectTimelinesByAccount = createAppSelector(
[(state) => state.timelines, (_, accountId: string) => accountId],
(timelines, accountId) =>
timelines.filter((_, key) => key.startsWith(`account:${accountId}:`)),
);
export function toTypedTimeline(timeline?: ImmutableMap<string, unknown>) {
if (!timeline) {
return null;
+5 -3
View File
@@ -9,13 +9,15 @@ describe('relativeTimeParts', () => {
// Past
[-30 * SECOND, { value: -30, unit: 'second' }],
[-90 * SECOND, { value: -2, unit: 'minute' }],
[-60 * SECOND, { value: -1, unit: 'minute' }],
[-90 * SECOND, { value: -1, unit: 'minute' }],
[-30 * MINUTE, { value: -30, unit: 'minute' }],
[-90 * MINUTE, { value: -2, unit: 'hour' }],
[-60 * MINUTE, { value: -1, unit: 'hour' }],
[-90 * MINUTE, { value: -1, unit: 'hour' }],
[-5 * HOUR, { value: -5, unit: 'hour' }],
[-24 * HOUR, { value: -1, unit: 'day' }],
[-36 * HOUR, { value: -1, unit: 'day' }],
[-47 * HOUR, { value: -2, unit: 'day' }],
[-47 * HOUR, { value: -1, unit: 'day' }],
[-3 * DAY, { value: -3, unit: 'day' }],
// Future
+13 -5
View File
@@ -16,21 +16,29 @@ export function relativeTimeParts(
): { value: number; unit: TimeUnit; delta: number } {
const delta = ts - now;
const absDelta = Math.abs(delta);
const sign = Math.sign(delta);
if (absDelta < MINUTE) {
return { value: Math.floor(delta / SECOND), unit: 'second', delta };
return {
value: sign * Math.floor(absDelta / SECOND),
unit: 'second',
delta,
};
}
if (absDelta < HOUR) {
return { value: Math.floor(delta / MINUTE), unit: 'minute', delta };
return {
value: sign * Math.floor(absDelta / MINUTE),
unit: 'minute',
delta,
};
}
if (absDelta < DAY) {
return { value: Math.floor(delta / HOUR), unit: 'hour', delta };
return { value: sign * Math.floor(absDelta / HOUR), unit: 'hour', delta };
}
// Round instead of use floor as days are big enough that the value is usually off by a few hours.
return { value: Math.round(delta / DAY), unit: 'day', delta };
return { value: sign * Math.floor(absDelta / DAY), unit: 'day', delta };
}
export function isToday(ts: number, now = Date.now()): boolean {
@@ -7255,6 +7255,7 @@ a.status-card {
border-radius: 8px;
position: relative;
width: 100%;
max-width: 100%;
min-height: 64px;
display: grid;
grid-template-columns: 1fr 1fr;
@@ -8573,7 +8574,7 @@ noscript {
}
&__pagination {
padding: 15px;
padding: 0 12px 15px 0;
color: var(--color-text-secondary);
position: absolute;
bottom: 3px;
@@ -8598,7 +8599,10 @@ noscript {
align-items: center;
margin-top: 15px;
margin-inline-start: -2px;
width: calc(100% - (90px - 33px));
--pagination-width: 70px;
width: calc(100% - var(--pagination-width));
&__item {
flex-shrink: 0;