Merge commit '0d650780e26735621f08bbdd545b162871e4562c' into glitch-soc/merge-upstream

Conflicts:
- `.prettierignore`:
  Upstream added a file, glitch-soc had extra files.
  Took upstream's changes and moved glitch-soc's additions at the end.
This commit is contained in:
Claire
2025-06-26 18:04:37 +02:00
52 changed files with 616 additions and 351 deletions

View File

@@ -18,7 +18,7 @@ import { useIdentity } from 'mastodon/identity_context';
import { useAppHistory } from './router';
const messages = defineMessages({
export const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
moveLeft: {

View File

@@ -1,53 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import SettingsIcon from '@/material-icons/400-20px/settings.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { requestBrowserPermission } from 'mastodon/actions/notifications';
import { changeSetting } from 'mastodon/actions/settings';
import { Button } from 'mastodon/components/button';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
});
class NotificationsPermissionBanner extends PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.dispatch(requestBrowserPermission());
};
handleClose = () => {
this.props.dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
};
render () {
const { intl } = this.props;
return (
<div className='notifications-permission-banner'>
<div className='notifications-permission-banner__close'>
<IconButton icon='times' iconComponent={CloseIcon} onClick={this.handleClose} title={intl.formatMessage(messages.close)} />
</div>
<h2><FormattedMessage id='notifications_permission_banner.title' defaultMessage='Never miss a thing' /></h2>
<p><FormattedMessage id='notifications_permission_banner.how_to_control' defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled." values={{ icon: <Icon id='sliders' icon={SettingsIcon} /> }} /></p>
<Button onClick={this.handleClick}><FormattedMessage id='notifications_permission_banner.enable' defaultMessage='Enable desktop notifications' /></Button>
</div>
);
}
}
export default connect()(injectIntl(NotificationsPermissionBanner));

View File

@@ -0,0 +1,74 @@
import { useCallback } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useAppDispatch } from '@/mastodon/store';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import UnfoldMoreIcon from '@/material-icons/400-24px/unfold_more.svg?react';
import { requestBrowserPermission } from 'mastodon/actions/notifications';
import { changeSetting } from 'mastodon/actions/settings';
import { Button } from 'mastodon/components/button';
import { messages as columnHeaderMessages } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
});
const NotificationsPermissionBanner: React.FC = () => {
const intl = useIntl();
const dispatch = useAppDispatch();
const handleClick = useCallback(() => {
dispatch(requestBrowserPermission());
}, [dispatch]);
const handleClose = useCallback(() => {
dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
}, [dispatch]);
return (
<div className='notifications-permission-banner'>
<div className='notifications-permission-banner__close'>
<IconButton
icon='times'
iconComponent={CloseIcon}
onClick={handleClose}
title={intl.formatMessage(messages.close)}
/>
</div>
<h2>
<FormattedMessage
id='notifications_permission_banner.title'
defaultMessage='Never miss a thing'
/>
</h2>
<p>
<FormattedMessage
id='notifications_permission_banner.how_to_control'
defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled."
values={{
icon: (
<Icon
id='sliders'
icon={UnfoldMoreIcon}
aria-label={intl.formatMessage(columnHeaderMessages.show)}
/>
),
}}
/>
</p>
<Button onClick={handleClick}>
<FormattedMessage
id='notifications_permission_banner.enable'
defaultMessage='Enable desktop notifications'
/>
</Button>
</div>
);
};
// eslint-disable-next-line import/no-default-export
export default NotificationsPermissionBanner;

View File

@@ -1,71 +1,30 @@
import { useLayoutEffect, useEffect, useState } from 'react';
import { useLayoutEffect } from 'react';
import { createAppSelector, useAppSelector } from 'mastodon/store';
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
const getShouldLockBodyScroll = createAppSelector(
[
(state) => state.navigation.open,
(state) => state.modal.get('stack').size > 0,
],
(isMobileMenuOpen: boolean, isModalOpen: boolean) => {
return isMobileMenuOpen || isModalOpen;
},
(isMobileMenuOpen: boolean, isModalOpen: boolean) =>
isMobileMenuOpen || isModalOpen,
);
/**
* This component locks scrolling on the `body` element when
* This component locks scrolling on the body when
* `getShouldLockBodyScroll` returns true.
*
* The scrollbar width is taken into account and written to
* a CSS custom property `--root-scrollbar-width`
*/
export const BodyScrollLock: React.FC = () => {
const shouldLockBodyScroll = useAppSelector(getShouldLockBodyScroll);
useLayoutEffect(() => {
document.body.classList.toggle('with-modals--active', shouldLockBodyScroll);
document.documentElement.classList.toggle(
'has-modal',
shouldLockBodyScroll,
);
}, [shouldLockBodyScroll]);
const [scrollbarWidth, setScrollbarWidth] = useState(() =>
getScrollbarWidth(),
);
useEffect(() => {
const handleResize = () => {
setScrollbarWidth(getScrollbarWidth());
};
window.addEventListener('resize', handleResize, { passive: true });
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
// Inject style element to make scrollbar width available
// as CSS custom property
useLayoutEffect(() => {
const nonce = document
.querySelector('meta[name=style-nonce]')
?.getAttribute('content');
if (nonce) {
const styleEl = document.createElement('style');
styleEl.nonce = nonce;
styleEl.innerHTML = `
:root {
--root-scrollbar-width: ${scrollbarWidth}px;
}
`;
document.head.appendChild(styleEl);
return () => {
document.head.removeChild(styleEl);
};
}
return () => '';
}, [scrollbarWidth]);
return null;
};

View File

@@ -13,6 +13,7 @@ export const ConfirmationModal: React.FC<
title: React.ReactNode;
message: React.ReactNode;
confirm: React.ReactNode;
cancel?: React.ReactNode;
secondary?: React.ReactNode;
onSecondary?: () => void;
onConfirm: () => void;
@@ -22,6 +23,7 @@ export const ConfirmationModal: React.FC<
title,
message,
confirm,
cancel,
onClose,
onConfirm,
secondary,
@@ -57,10 +59,12 @@ export const ConfirmationModal: React.FC<
<div className='safety-action-modal__bottom'>
<div className='safety-action-modal__actions'>
<button onClick={handleCancel} className='link-button'>
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
{cancel ?? (
<FormattedMessage
id='confirmation_modal.cancel'
defaultMessage='Cancel'
/>
)}
</button>
{secondary && (

View File

@@ -0,0 +1,104 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { replyCompose } from 'mastodon/actions/compose';
import { editStatus } from 'mastodon/actions/statuses';
import type { Status } from 'mastodon/models/status';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const editMessages = defineMessages({
title: {
id: 'confirmations.discard_draft.edit.title',
defaultMessage: 'Discard changes to your post?',
},
message: {
id: 'confirmations.discard_draft.edit.message',
defaultMessage:
'Continuing will discard any changes you have made to the post you are currently editing.',
},
cancel: {
id: 'confirmations.discard_draft.edit.cancel',
defaultMessage: 'Resume editing',
},
});
const postMessages = defineMessages({
title: {
id: 'confirmations.discard_draft.post.title',
defaultMessage: 'Discard your draft post?',
},
message: {
id: 'confirmations.discard_draft.post.message',
defaultMessage:
'Continuing will discard the post you are currently composing.',
},
cancel: {
id: 'confirmations.discard_draft.post.cancel',
defaultMessage: 'Resume draft',
},
});
const messages = defineMessages({
confirm: {
id: 'confirmations.discard_draft.confirm',
defaultMessage: 'Discard and continue',
},
});
const DiscardDraftConfirmationModal: React.FC<
{
onConfirm: () => void;
} & BaseConfirmationModalProps
> = ({ onConfirm, onClose }) => {
const intl = useIntl();
const isEditing = useAppSelector((state) => !!state.compose.get('id'));
const contextualMessages = isEditing ? editMessages : postMessages;
return (
<ConfirmationModal
title={intl.formatMessage(contextualMessages.title)}
message={intl.formatMessage(contextualMessages.message)}
cancel={intl.formatMessage(contextualMessages.cancel)}
confirm={intl.formatMessage(messages.confirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};
export const ConfirmReplyModal: React.FC<
{
status: Status;
} & BaseConfirmationModalProps
> = ({ status, onClose }) => {
const dispatch = useAppDispatch();
const onConfirm = useCallback(() => {
dispatch(replyCompose(status));
}, [dispatch, status]);
return (
<DiscardDraftConfirmationModal onConfirm={onConfirm} onClose={onClose} />
);
};
export const ConfirmEditStatusModal: React.FC<
{
statusId: string;
} & BaseConfirmationModalProps
> = ({ statusId, onClose }) => {
const dispatch = useAppDispatch();
const onConfirm = useCallback(() => {
dispatch(editStatus(statusId));
}, [dispatch, statusId]);
return (
<DiscardDraftConfirmationModal onConfirm={onConfirm} onClose={onClose} />
);
};

View File

@@ -1,45 +0,0 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { editStatus } from 'mastodon/actions/statuses';
import { useAppDispatch } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
editTitle: {
id: 'confirmations.edit.title',
defaultMessage: 'Overwrite post?',
},
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
editMessage: {
id: 'confirmations.edit.message',
defaultMessage:
'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?',
},
});
export const ConfirmEditStatusModal: React.FC<
{
statusId: string;
} & BaseConfirmationModalProps
> = ({ statusId, onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const onConfirm = useCallback(() => {
dispatch(editStatus(statusId));
}, [dispatch, statusId]);
return (
<ConfirmationModal
title={intl.formatMessage(messages.editTitle)}
message={intl.formatMessage(messages.editMessage)}
confirm={intl.formatMessage(messages.editConfirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};

View File

@@ -1,8 +1,10 @@
export { ConfirmationModal } from './confirmation_modal';
export { ConfirmDeleteStatusModal } from './delete_status';
export { ConfirmDeleteListModal } from './delete_list';
export { ConfirmReplyModal } from './reply';
export { ConfirmEditStatusModal } from './edit_status';
export {
ConfirmReplyModal,
ConfirmEditStatusModal,
} from './discard_draft_confirmation';
export { ConfirmUnfollowModal } from './unfollow';
export { ConfirmClearNotificationsModal } from './clear_notifications';
export { ConfirmLogOutModal } from './log_out';

View File

@@ -1,46 +0,0 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { replyCompose } from 'mastodon/actions/compose';
import type { Status } from 'mastodon/models/status';
import { useAppDispatch } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
replyTitle: {
id: 'confirmations.reply.title',
defaultMessage: 'Overwrite post?',
},
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
replyMessage: {
id: 'confirmations.reply.message',
defaultMessage:
'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?',
},
});
export const ConfirmReplyModal: React.FC<
{
status: Status;
} & BaseConfirmationModalProps
> = ({ status, onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const onConfirm = useCallback(() => {
dispatch(replyCompose(status));
}, [dispatch, status]);
return (
<ConfirmationModal
title={intl.formatMessage(messages.replyTitle)}
message={intl.formatMessage(messages.replyMessage)}
confirm={intl.formatMessage(messages.replyConfirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};

View File

@@ -24,7 +24,7 @@
"account.blocking": "Blokering",
"account.cancel_follow_request": "Annullér anmodning om at følge",
"account.copy": "Kopiér link til profil",
"account.direct": "Privat omtale @{name}",
"account.direct": "Nævn @{name} privat",
"account.disable_notifications": "Advisér mig ikke længere, når @{name} poster",
"account.domain_blocking": "Blokerer domæne",
"account.edit_profile": "Redigér profil",
@@ -121,7 +121,7 @@
"annual_report.summary.highlighted_post.by_replies": "mest besvarede indlæg",
"annual_report.summary.highlighted_post.possessive": "{name}s",
"annual_report.summary.most_used_app.most_used_app": "mest benyttede app",
"annual_report.summary.most_used_hashtag.most_used_hashtag": "mest benyttede etiket",
"annual_report.summary.most_used_hashtag.most_used_hashtag": "mest benyttede hashtag",
"annual_report.summary.most_used_hashtag.none": "Intet",
"annual_report.summary.new_posts.new_posts": "nye indlæg",
"annual_report.summary.percentile.text": "<topLabel>Det betyder, at man er i top</topLabel><percentage></percentage><bottomLabel>af {domain}-brugere.</bottomLabel>",
@@ -194,10 +194,10 @@
"compose.saved.body": "Indlæg gemt.",
"compose_form.direct_message_warning_learn_more": "Få mere at vide",
"compose_form.encryption_warning": "Indlæg på Mastodon er ikke ende-til-ende-krypteret. Del derfor ikke sensitiv information via Mastodon.",
"compose_form.hashtag_warning": "Da indlægget ikke er offentligt, vises det ikke under nogen etiket, da kun offentlige indlæg er søgbare via etiketter.",
"compose_form.hashtag_warning": "Da indlægget ikke er offentligt, vises det ikke under noget hashtag, da kun offentlige indlæg er søgbare via hashtags.",
"compose_form.lock_disclaimer": "Din konto er ikke {locked}. Enhver kan følge dig og se indlæg kun beregnet for følgere.",
"compose_form.lock_disclaimer.lock": "låst",
"compose_form.placeholder": "Hvad tænker du på?",
"compose_form.placeholder": "Hvad har du på hjertet?",
"compose_form.poll.duration": "Afstemningens varighed",
"compose_form.poll.multiple": "Multivalg",
"compose_form.poll.option_placeholder": "Valgmulighed {number}",
@@ -205,7 +205,7 @@
"compose_form.poll.switch_to_multiple": "Ændr afstemning til flervalgstype",
"compose_form.poll.switch_to_single": "Ændr afstemning til enkeltvalgstype",
"compose_form.poll.type": "Stil",
"compose_form.publish": "Indsend",
"compose_form.publish": "Slå op",
"compose_form.reply": "Svar",
"compose_form.save_changes": "Opdatér",
"compose_form.spoiler.marked": "Fjern emnefelt",
@@ -305,8 +305,8 @@
"emoji_button.search_results": "Søgeresultater",
"emoji_button.symbols": "Symboler",
"emoji_button.travel": "Rejser og steder",
"empty_column.account_featured.me": "Intet fremhævet endnu. Vidste du, at man kan fremhæve sine mest brugte hashtags og endda en vens konti på sin profil?",
"empty_column.account_featured.other": "{acct} har ikke fremhævet noget endnu. Vidste du, at man kan fremhæve sine mest brugte hashtags og endda en vens konti på sin profil?",
"empty_column.account_featured.me": "Intet fremhævet endnu. Vidste du, at man kan fremhæve sine mest brugte hashtags og endda din vens konti på din profil?",
"empty_column.account_featured.other": "{acct} har ikke fremhævet noget endnu. Vidste du, at du kan fremhæve dine mest brugte hashtags og endda din vens konti på din profil?",
"empty_column.account_featured_other.unknown": "Denne konto har ikke fremhævet noget endnu.",
"empty_column.account_hides_collections": "Brugeren har valgt ikke at gøre denne information tilgængelig",
"empty_column.account_suspended": "Konto suspenderet",
@@ -321,8 +321,8 @@
"empty_column.favourited_statuses": "Du har endnu ingen favoritindlæg. Når du føjer et opslag til favoritter, vil det dukke op her.",
"empty_column.favourites": "Ingen har endnu føjet dette indlæg til favoritter. Når nogen gør det, vil det dukke op her.",
"empty_column.follow_requests": "Du har endnu ingen følgeanmodninger. Når du modtager én, vil den dukke op her.",
"empty_column.followed_tags": "Ingen etiketter følges endnu. Når det sker, vil de fremgå her.",
"empty_column.hashtag": "Der er intet med denne etiket endnu.",
"empty_column.followed_tags": "Ingen hashtags følges endnu. Når det sker, vil de fremgå her.",
"empty_column.hashtag": "Der er intet med dette hashtag endnu.",
"empty_column.home": "Din hjemmetidslinje er tom! Følg nogle personer, for at fylde den op.",
"empty_column.list": "Der er ikke noget på denne liste endnu. Når medlemmer af listen udgiver nye indlæg vil de fremgå her.",
"empty_column.mutes": "Du har endnu ikke skjult nogle brugere.",
@@ -336,9 +336,10 @@
"errors.unexpected_crash.copy_stacktrace": "Kopiér stacktrace til udklipsholderen",
"errors.unexpected_crash.report_issue": "Anmeld problem",
"explore.suggested_follows": "Personer",
"explore.title": "Trender",
"explore.trending_links": "Nyheder",
"explore.trending_statuses": "Indlæg",
"explore.trending_tags": "Etiketter",
"explore.trending_tags": "Hashtags",
"featured_carousel.header": "{count, plural, one {Fastgjort indlæg} other {Fastgjorte indlæg}}",
"featured_carousel.next": "Næste",
"featured_carousel.post": "Indlæg",
@@ -384,7 +385,7 @@
"follow_suggestions.similar_to_recently_followed_longer": "Svarende til profiler, som for nylig er fulgt",
"follow_suggestions.view_all": "Vis alle",
"follow_suggestions.who_to_follow": "Hvem, som skal følges",
"followed_tags": "Etiketter, som følges",
"followed_tags": "Hashtag, som følges",
"footer.about": "Om",
"footer.directory": "Profiloversigt",
"footer.get_app": "Hent appen",
@@ -402,7 +403,7 @@
"hashtag.column_header.tag_mode.any": "eller {additional}",
"hashtag.column_header.tag_mode.none": "uden {additional}",
"hashtag.column_settings.select.no_options_message": "Ingen forslag fundet",
"hashtag.column_settings.select.placeholder": "Angiv etiketter…",
"hashtag.column_settings.select.placeholder": "Angiv hashtags…",
"hashtag.column_settings.tag_mode.all": "Alle disse",
"hashtag.column_settings.tag_mode.any": "Nogle af disse",
"hashtag.column_settings.tag_mode.none": "Ingen af disse",
@@ -411,10 +412,10 @@
"hashtag.counter_by_uses": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}} i dag",
"hashtag.feature": "Fremhæv på profil",
"hashtag.follow": "Følg etiket",
"hashtag.follow": "Følg hashtag",
"hashtag.mute": "Tavsgør #{hashtag}",
"hashtag.unfeature": "Fremhæv ikke på profil",
"hashtag.unfollow": "Stop med at følge etiket",
"hashtag.unfollow": "Følg ikke længere hashtag",
"hashtags.and_other": "…og {count, plural, one {}other {# flere}}",
"hints.profiles.followers_may_be_missing": "Der kan mangle følgere for denne profil.",
"hints.profiles.follows_may_be_missing": "Fulgte kan mangle for denne profil.",
@@ -442,7 +443,7 @@
"ignore_notifications_modal.new_accounts_title": "Ignorér notifikationer fra nye konti?",
"ignore_notifications_modal.not_followers_title": "Ignorér notifikationer fra folk, som ikke er følgere?",
"ignore_notifications_modal.not_following_title": "Ignorér notifikationer fra folk, man ikke følger?",
"ignore_notifications_modal.private_mentions_title": "Ignorér notifikationer fra uopfordrede Private omtaler?",
"ignore_notifications_modal.private_mentions_title": "Ignorér notifikationer fra uopfordrede private omtaler?",
"info_button.label": "Hjælp",
"info_button.what_is_alt_text": "<h1>Hvad er alt-tekst?</h1> <p>Alt-tekst leverer billedbeskrivelser til folk med synsnedsættelser, lav båndbredde-forbindelser eller med ønske om ekstra kontekst.</p> <p>Tilgængelighed og forståelse kan forbedres for alle ved at skrive klar, kortfattet og objektiv alt-tekst.</p> <ul> <li>Fang vigtige elementer</li> <li>Opsummér tekst i billeder</li> <li>Brug almindelig sætningsstruktur</li> <li>Undgå overflødig information</li> <li>Fokusér på tendenser og centrale resultater i kompleks grafik (såsom diagrammer eller kort)</li> </ul>",
"interaction_modal.action.favourite": "For at fortsætte, skal du føje til favoritter fra din konto.",
@@ -558,7 +559,7 @@
"navigation_bar.favourites": "Favoritter",
"navigation_bar.filters": "Skjulte ord",
"navigation_bar.follow_requests": "Følgeanmodninger",
"navigation_bar.followed_tags": "Etiketter, som følges",
"navigation_bar.followed_tags": "Hashtag, som følges",
"navigation_bar.follows_and_followers": "Følges og følgere",
"navigation_bar.import_export": "Import og eksport",
"navigation_bar.lists": "Lister",
@@ -574,7 +575,9 @@
"navigation_bar.search": "Søg",
"navigation_bar.search_trends": "Søg/Populære",
"navigation_panel.collapse_followed_tags": "Sammenfold menuen Fulgte hashtags",
"navigation_panel.collapse_lists": "Luk listemenu",
"navigation_panel.expand_followed_tags": "Udfold menuen Fulgte hashtags",
"navigation_panel.expand_lists": "Udvid listemenu",
"not_signed_in_indicator.not_signed_in": "Log ind for at tilgå denne ressource.",
"notification.admin.report": "{name} anmeldte {target}",
"notification.admin.report_account": "{name} anmeldte {count, plural, one {et indlæg} other {# indlæg}} fra {target} angående {category}",
@@ -703,7 +706,7 @@
"onboarding.profile.display_name": "Vist navn",
"onboarding.profile.display_name_hint": "Dit fulde navn eller dit sjove navn…",
"onboarding.profile.note": "Bio",
"onboarding.profile.note_hint": "Man kan @omtale andre personer eller #etiketter…",
"onboarding.profile.note_hint": "Du kan @omtale andre personer eller #hashtags…",
"onboarding.profile.save_and_continue": "Gem og fortsæt",
"onboarding.profile.title": "Profilopsætning",
"onboarding.profile.upload_avatar": "Upload profilbillede",
@@ -728,9 +731,9 @@
"privacy.private.short": "Følgere",
"privacy.public.long": "Alle på og udenfor Mastodon",
"privacy.public.short": "Offentlig",
"privacy.unlisted.additional": "Dette er præcis som offentlig adfærd, dog vises indlægget ikke i tidslinjer, under etiketter, udforsk eller Mastodon-søgning, selv hvis du ellers har sagt at dine opslag godt må være søgbare.",
"privacy.unlisted.additional": "Dette er præcis som offentlig adfærd, dog vises indlægget ikke i live feeds/hashtags, udforsk eller Mastodon-søgning, selv hvis valget gælder hele kontoen.",
"privacy.unlisted.long": "Færre algoritmiske fanfarer",
"privacy.unlisted.short": "Stille offentligt",
"privacy.unlisted.short": "Offentlig (stille)",
"privacy_policy.last_updated": "Senest opdateret {date}",
"privacy_policy.title": "Privatlivspolitik",
"recommended": "Anbefalet",
@@ -801,14 +804,15 @@
"report_notification.categories.violation": "Regelovertrædelse",
"report_notification.categories.violation_sentence": "regelovertrædelse",
"report_notification.open": "Åbn anmeldelse",
"search.clear": "Ryd søgning",
"search.no_recent_searches": "Ingen seneste søgninger",
"search.placeholder": "Søg",
"search.quick_action.account_search": "Profiler matchende {x}",
"search.quick_action.go_to_account": "Gå til profilen {x}",
"search.quick_action.go_to_hashtag": "Gå til etiketten {x}",
"search.quick_action.go_to_hashtag": "Gå til hashtagget {x}",
"search.quick_action.open_url": "Åbn URL i Mastodon",
"search.quick_action.status_search": "Indlæg matchende {x}",
"search.search_or_paste": "Søg efter eller angiv URL",
"search.search_or_paste": "Søg eller indsæt URL",
"search_popout.full_text_search_disabled_message": "Utilgængelig på {domain}.",
"search_popout.full_text_search_logged_out_message": "Kun tilgængelig, når logget ind.",
"search_popout.language_code": "ISO-sprogkode",
@@ -819,9 +823,9 @@
"search_popout.user": "bruger",
"search_results.accounts": "Profiler",
"search_results.all": "Alle",
"search_results.hashtags": "Etiketter",
"search_results.hashtags": "Hashtags",
"search_results.no_results": "Ingen resultater.",
"search_results.no_search_yet": "Prøv at søge efter indlæg, profiler eller etiketter.",
"search_results.no_search_yet": "Prøv at søge efter indlæg, profiler eller hashtags.",
"search_results.see_all": "Vis alle",
"search_results.statuses": "Indlæg",
"search_results.title": "Søg efter \"{q}\"",
@@ -846,7 +850,7 @@
"status.copy": "Kopiér link til indlæg",
"status.delete": "Slet",
"status.detailed_status": "Detaljeret samtalevisning",
"status.direct": "Privat omtale @{name}",
"status.direct": "Nævn @{name} privat",
"status.direct_indicator": "Privat omtale",
"status.edit": "Redigér",
"status.edited": "Senest redigeret {date}",
@@ -903,7 +907,10 @@
"subscribed_languages.save": "Gem ændringer",
"subscribed_languages.target": "Skift abonnementssprog for {target}",
"tabs_bar.home": "Hjem",
"tabs_bar.menu": "Menu",
"tabs_bar.notifications": "Notifikationer",
"tabs_bar.publish": "Nyt indlæg",
"tabs_bar.search": "Søg",
"terms_of_service.effective_as_of": "Gældende pr. {date}",
"terms_of_service.title": "Tjenestevilkår",
"terms_of_service.upcoming_changes_on": "Kommende ændringer pr. {date}",

View File

@@ -219,11 +219,15 @@
"confirmations.delete_list.confirm": "Delete",
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
"confirmations.delete_list.title": "Delete list?",
"confirmations.discard_draft.confirm": "Discard and continue",
"confirmations.discard_draft.edit.cancel": "Resume editing",
"confirmations.discard_draft.edit.message": "Continuing will discard any changes you have made to the post you are currently editing.",
"confirmations.discard_draft.edit.title": "Discard changes to your post?",
"confirmations.discard_draft.post.cancel": "Resume draft",
"confirmations.discard_draft.post.message": "Continuing will discard the post you are currently composing.",
"confirmations.discard_draft.post.title": "Discard your draft post?",
"confirmations.discard_edit_media.confirm": "Discard",
"confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
"confirmations.edit.confirm": "Edit",
"confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.edit.title": "Overwrite post?",
"confirmations.follow_to_list.confirm": "Follow and add to list",
"confirmations.follow_to_list.message": "You need to be following {name} to add them to a list.",
"confirmations.follow_to_list.title": "Follow user?",
@@ -241,9 +245,6 @@
"confirmations.remove_from_followers.confirm": "Remove follower",
"confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?",
"confirmations.remove_from_followers.title": "Remove follower?",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.reply.title": "Overwrite post?",
"confirmations.unfollow.confirm": "Unfollow",
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
"confirmations.unfollow.title": "Unfollow user?",

View File

@@ -569,6 +569,7 @@
"navigation_bar.mutes": "کاربران خموشانده",
"navigation_bar.opened_in_classic_interface": "فرسته‌ها، حساب‌ها و دیگر صفحه‌های خاص به طور پیش‌گزیده در میانای وب کلاسیک گشوده می‌شوند.",
"navigation_bar.preferences": "ترجیحات",
"navigation_bar.privacy_and_reach": "محرمانگی و دسترسی",
"navigation_bar.search": "جست‌وجو",
"navigation_bar.search_trends": "جستجو \\ پرطرفدار",
"not_signed_in_indicator.not_signed_in": "برای دسترسی به این منبع باید وارد شوید.",

View File

@@ -804,6 +804,7 @@
"report_notification.categories.violation": "Sääntörikkomus",
"report_notification.categories.violation_sentence": "sääntörikkomus",
"report_notification.open": "Avaa raportti",
"search.clear": "Tyhjennä haku",
"search.no_recent_searches": "Ei viimeaikaisia hakuja",
"search.placeholder": "Hae",
"search.quick_action.account_search": "Profiilit haulla {x}",

View File

@@ -563,6 +563,8 @@
"navigation_bar.follows_and_followers": "Ag leanúint agus do do leanúint",
"navigation_bar.import_export": "Allmhairiú agus onnmhairiú",
"navigation_bar.lists": "Liostaí",
"navigation_bar.live_feed_local": "Fotha beo (áitiúil)",
"navigation_bar.live_feed_public": "Fotha beo (poiblí)",
"navigation_bar.logout": "Logáil Amach",
"navigation_bar.moderation": "Measarthacht",
"navigation_bar.more": "Tuilleadh",
@@ -802,6 +804,7 @@
"report_notification.categories.violation": "Sárú rialach",
"report_notification.categories.violation_sentence": "sárú riail",
"report_notification.open": "Oscail tuairisc",
"search.clear": "Glan an cuardach",
"search.no_recent_searches": "Níl aon chuardach le déanaí",
"search.placeholder": "Cuardaigh",
"search.quick_action.account_search": "Próifílí a mheaitseálann {x}",

View File

@@ -573,7 +573,10 @@
"navigation_bar.preferences": "Preferências",
"navigation_bar.privacy_and_reach": "Privacidade e alcance",
"navigation_bar.search": "Pesquisar",
"navigation_bar.search_trends": "Pesquisar / Em destaque",
"navigation_panel.collapse_followed_tags": "Recolher o menu de etiquetas seguidas",
"navigation_panel.collapse_lists": "Recolher lista de menu",
"navigation_panel.expand_followed_tags": "Expandir o menu de etiquetas seguidas",
"navigation_panel.expand_lists": "Expandir lista de menu",
"not_signed_in_indicator.not_signed_in": "Tens de iniciar a sessão para utilizares esta funcionalidade.",
"notification.admin.report": "{name} denunciou {target}",

View File

@@ -804,6 +804,7 @@
"report_notification.categories.violation": "Kural ihlali",
"report_notification.categories.violation_sentence": "kural ihlali",
"report_notification.open": "Bildirim aç",
"search.clear": "Aramayı temizle",
"search.no_recent_searches": "Son arama yok",
"search.placeholder": "Ara",
"search.quick_action.account_search": "Eşleşen profiller {x}",

View File

@@ -1,19 +0,0 @@
import { isMobile } from '../is_mobile';
export const getScrollbarWidth = () => {
if (isMobile(window.innerWidth)) {
return 0;
}
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.overflow = 'scroll';
document.body.appendChild(outer);
const inner = document.createElement('div');
outer.appendChild(inner);
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
outer.remove();
return scrollbarWidth;
};

View File

@@ -1,6 +1,20 @@
@use 'variables' as *;
@use 'functions' as *;
html.has-modal {
&,
body {
touch-action: none;
overscroll-behavior: none;
-webkit-overflow-scrolling: auto;
scrollbar-gutter: stable;
}
body {
overflow: hidden !important;
}
}
body {
font-family: $font-sans-serif, sans-serif;
background: var(--background-color);
@@ -64,21 +78,6 @@ body {
height: 100%;
padding-bottom: env(safe-area-inset-bottom);
}
&.with-modals--active {
overflow-y: hidden;
overscroll-behavior: none;
margin-right: var(--root-scrollbar-width, 0);
}
}
&.with-modals {
overflow-x: hidden;
overflow-y: scroll;
&--active {
overflow-y: hidden;
}
}
&.player {

View File

@@ -2896,10 +2896,6 @@ a.account__display-name {
border-top: 1px solid var(--background-border-color);
box-sizing: border-box;
.with-modals--active & {
padding-right: var(--root-scrollbar-width);
}
.layout-multiple-columns & {
display: none;
}
@@ -3170,7 +3166,7 @@ a.account__display-name {
.navigation-panel {
margin: 0;
border-inline-start: 1px solid var(--background-border-color);
height: 100vh;
height: 100dvh;
}
.navigation-panel__banner,
@@ -3228,6 +3224,7 @@ a.account__display-name {
.navigation-panel {
width: 284px;
overflow-y: auto;
scrollbar-width: thin;
&__menu {
flex-shrink: 0;