Merge commit '081d38679f965ac20ff1a6c72e2fc1108c34ac30' into glitch-soc/merge-upstream

This commit is contained in:
Claire committed 2025-08-04 21:24:47 +02:00
commit 53185f5381
69 files changed
+1172 -529

No files matched your search

@@ -37,7 +37,7 @@ export interface BaseApiAccountJSON {
roles?: ApiAccountJSON[];
statuses_count: number;
uri: string;
url: string;
url?: string;
username: string;
moved?: ApiAccountJSON;
suspended?: boolean;
@@ -13,6 +13,7 @@ export const allNotificationTypes = [
'favourite',
'reblog',
'mention',
'quote',
'poll',
'status',
'update',
@@ -28,6 +29,7 @@ export type NotificationWithStatusType =
| 'reblog'
| 'status'
| 'mention'
| 'quote'
| 'poll'
| 'update';
@@ -15,17 +15,6 @@ export const SKIN_TONE_CODES = [
0x1f3ff, // Dark skin tone
] as const;
// TODO: Test and create fallback for browsers that do not handle the /v flag.
export const UNICODE_EMOJI_REGEX = /\p{RGI_Emoji}/v;
// See: https://www.unicode.org/reports/tr51/#valid-emoji-tag-sequences
export const UNICODE_FLAG_EMOJI_REGEX =
/\p{RGI_Emoji_Flag_Sequence}|\p{RGI_Emoji_Tag_Sequence}/v;
export const CUSTOM_EMOJI_REGEX = /:([a-z0-9_]+):/i;
export const ANY_EMOJI_REGEX = new RegExp(
`(${UNICODE_EMOJI_REGEX.source}|${CUSTOM_EMOJI_REGEX.source})`,
'gv',
);
// Emoji rendering modes. A mode is what we are using to render emojis, a style is what the user has selected.
export const EMOJI_MODE_NATIVE = 'native';
export const EMOJI_MODE_NATIVE_WITH_FLAGS = 'native-flags';
@@ -1,5 +1,7 @@
import type { ComponentPropsWithoutRef, ElementType } from 'react';
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
import { useEmojify } from './hooks';
import type { CustomEmojiMapArg } from './types';
@@ -12,7 +14,7 @@ type EmojiHTMLProps<Element extends ElementType = 'div'> = Omit<
as?: Element;
};
export const EmojiHTML = <Element extends ElementType>({
export const ModernEmojiHTML = <Element extends ElementType>({
extraEmojis,
htmlString,
as: asElement, // Rename for syntax highlighting
@@ -29,3 +31,18 @@ export const EmojiHTML = <Element extends ElementType>({
<Wrapper {...props} dangerouslySetInnerHTML={{ __html: emojifiedHtml }} />
);
};
export const EmojiHTML = <Element extends ElementType>(
props: EmojiHTMLProps<Element>,
) => {
if (isModernEmojiEnabled()) {
return <ModernEmojiHTML {...props} />;
}
const Wrapper = props.as ?? 'div';
return (
<Wrapper
{...props}
dangerouslySetInnerHTML={{ __html: props.htmlString }}
/>
);
};
@@ -8,7 +8,6 @@ import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
import { toSupportedLocale } from './locale';
import { determineEmojiMode } from './mode';
import { emojifyElement } from './render';
import type {
CustomEmojiMapArg,
EmojiAppState,
@@ -39,6 +38,7 @@ export function useEmojify(text: string, extraEmojis?: CustomEmojiMapArg) {
async (input: string) => {
const wrapper = document.createElement('div');
wrapper.innerHTML = input;
const { emojifyElement } = await import('./render');
const result = await emojifyElement(wrapper, appState, extra);
if (result) {
setEmojifiedText(result.innerHTML);
@@ -9,7 +9,6 @@ import {
EMOJI_TYPE_UNICODE,
EMOJI_TYPE_CUSTOM,
EMOJI_STATE_MISSING,
ANY_EMOJI_REGEX,
} from './constants';
import {
searchCustomEmojisByShortcodes,
@@ -32,7 +31,12 @@ import type {
LocaleOrCustom,
UnicodeEmojiToken,
} from './types';
import { emojiLogger, stringHasAnyEmoji, stringHasUnicodeFlags } from './utils';
import {
anyEmojiRegex,
emojiLogger,
stringHasAnyEmoji,
stringHasUnicodeFlags,
} from './utils';
const log = emojiLogger('render');
@@ -207,7 +211,7 @@ export function tokenizeText(text: string): TokenizedText {
const tokens = [];
let lastIndex = 0;
for (const match of text.matchAll(ANY_EMOJI_REGEX)) {
for (const match of text.matchAll(anyEmojiRegex())) {
if (match.index > lastIndex) {
tokens.push(text.slice(lastIndex, match.index));
}
@@ -1,23 +1,32 @@
import debug from 'debug';
import {
CUSTOM_EMOJI_REGEX,
UNICODE_EMOJI_REGEX,
UNICODE_FLAG_EMOJI_REGEX,
} from './constants';
import { emojiRegexPolyfill } from '@/mastodon/polyfills';
export function emojiLogger(segment: string) {
return debug(`emojis:${segment}`);
}
export function stringHasUnicodeEmoji(input: string): boolean {
return UNICODE_EMOJI_REGEX.test(input);
return new RegExp(EMOJI_REGEX, supportedFlags()).test(input);
}
export function stringHasUnicodeFlags(input: string): boolean {
return UNICODE_FLAG_EMOJI_REGEX.test(input);
if (supportsRegExpSets()) {
return new RegExp(
'\\p{RGI_Emoji_Flag_Sequence}|\\p{RGI_Emoji_Tag_Sequence}',
'v',
).test(input);
}
return new RegExp(
// First range is regional indicator symbols,
// Second is a black flag + 0-9|a-z tag chars + cancel tag.
// See: https://en.wikipedia.org/wiki/Regional_indicator_symbol
'(?:\uD83C[\uDDE6-\uDDFF]){2}|\uD83C\uDFF4(?:\uDB40[\uDC30-\uDC7A])+\uDB40\uDC7F',
).test(input);
}
// Constant as this is supported by all browsers.
const CUSTOM_EMOJI_REGEX = /:([a-z0-9_]+):/i;
export function stringHasCustomEmoji(input: string) {
return CUSTOM_EMOJI_REGEX.test(input);
}
@@ -25,3 +34,23 @@ export function stringHasCustomEmoji(input: string) {
export function stringHasAnyEmoji(input: string) {
return stringHasUnicodeEmoji(input) || stringHasCustomEmoji(input);
}
export function anyEmojiRegex() {
return new RegExp(
`${EMOJI_REGEX}|${CUSTOM_EMOJI_REGEX.source}`,
supportedFlags('gi'),
);
}
function supportsRegExpSets() {
return 'unicodeSets' in RegExp.prototype;
}
function supportedFlags(flags = '') {
if (supportsRegExpSets()) {
return `${flags}v`;
}
return flags;
}
const EMOJI_REGEX = emojiRegexPolyfill?.source ?? '\\p{RGI_Emoji}';
@@ -8,9 +8,9 @@ import { Link, withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import PersonIcon from '@/material-icons/400-24px/person-fill.svg?react';
@@ -42,6 +42,7 @@ const messages = defineMessages({
adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' },
relationshipsSevered: { id: 'notification.relationships_severance_event', defaultMessage: 'Lost connections with {name}' },
moderationWarning: { id: 'notification.moderation_warning', defaultMessage: 'You have received a moderation warning' },
quote: { id: 'notification.label.quote', defaultMessage: '{name} quoted your post'}
});
const notificationForScreenReader = (intl, message, timestamp) => {
@@ -251,6 +252,36 @@ class Notification extends ImmutablePureComponent {
);
}
renderQuote (notification, link) {
const { intl, unread } = this.props;
return (
<Hotkeys handlers={this.getHandlers()}>
<div className={classNames('notification notification-quote focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.quote, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
<div className='notification__message'>
<Icon id='quote' icon={FormatQuoteIcon} />
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.label.quote' defaultMessage='{name} quoted your post' values={{ name: link }} />
</span>
</div>
<StatusQuoteManager
id={notification.get('status')}
account={notification.get('account')}
muted
withDismiss
hidden={this.props.hidden}
getScrollPosition={this.props.getScrollPosition}
updateScrollBottom={this.props.updateScrollBottom}
cachedMediaWidth={this.props.cachedMediaWidth}
cacheMediaWidth={this.props.cacheMediaWidth}
/>
</div>
</Hotkeys>
);
}
renderStatus (notification, link) {
const { intl, unread, status } = this.props;
@@ -467,6 +498,8 @@ class Notification extends ImmutablePureComponent {
return this.renderFollowRequest(notification, account, link);
case 'mention':
return this.renderMention(notification);
case 'quote':
return this.renderQuote(notification);
case 'favourite':
return this.renderFavourite(notification, link);
case 'reblog':
@@ -15,6 +15,7 @@ import { NotificationFollowRequest } from './notification_follow_request';
import { NotificationMention } from './notification_mention';
import { NotificationModerationWarning } from './notification_moderation_warning';
import { NotificationPoll } from './notification_poll';
import { NotificationQuote } from './notification_quote';
import { NotificationReblog } from './notification_reblog';
import { NotificationSeveredRelationships } from './notification_severed_relationships';
import { NotificationStatus } from './notification_status';
@@ -91,6 +92,11 @@ export const NotificationGroup: React.FC<{
<NotificationMention unread={unread} notification={notificationGroup} />
);
break;
case 'quote':
content = (
<NotificationQuote unread={unread} notification={notificationGroup} />
);
break;
case 'follow':
content = (
<NotificationFollow unread={unread} notification={notificationGroup} />
@@ -0,0 +1,33 @@
import { FormattedMessage } from 'react-intl';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote.svg?react';
import type { NotificationGroupQuote } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationWithStatus } from './notification_with_status';
const quoteLabelRenderer: LabelRenderer = (displayName) => (
<FormattedMessage
id='notification.label.quote'
defaultMessage='{name} quoted your post'
values={{ name: displayName }}
/>
);
export const NotificationQuote: React.FC<{
notification: NotificationGroupQuote;
unread: boolean;
}> = ({ notification, unread }) => {
return (
<NotificationWithStatus
type='quote'
icon={FormatQuoteIcon}
iconId='quote'
accountIds={notification.sampleAccountIds}
count={notification.notifications_count}
statusId={notification.statusId}
labelRenderer={quoteLabelRenderer}
unread={unread}
/>
);
};
+1
View File
@@ -600,6 +600,7 @@
"notification.label.mention": "Mention",
"notification.label.private_mention": "Private mention",
"notification.label.private_reply": "Private reply",
"notification.label.quote": "{name} quoted your post",
"notification.label.reply": "Reply",
"notification.mention": "Mention",
"notification.mentioned_you": "{name} mentioned you",
+1 -1
View File
@@ -756,7 +756,7 @@
"reply_indicator.cancel": "Peruuta",
"reply_indicator.poll": "Äänestys",
"report.block": "Estä",
"report.block_explanation": "Et näe hänen julkaisujaan. Hän voi nähdä julkaisujasi eikä seurata sinua. Hän näkee, että olet estänyt hänet.",
"report.block_explanation": "Et näe hänen julkaisujaan. Hän ei voi nähdä julkaisujasi eikä seurata sinua. Hän näkee, että olet estänyt hänet.",
"report.categories.legal": "Lakiseikat",
"report.categories.other": "Muu",
"report.categories.spam": "Roskaposti",
+7
View File
@@ -1,6 +1,7 @@
{
"about.blocks": "Servī moderātī",
"about.contact": "Ratio:",
"about.default_locale": "Default",
"about.disclaimer": "Mastodon est software līberum, apertum fontem, et nōtam commercium Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Ratio abdere est",
"about.domain_blocks.preamble": "Mastodon genērāliter sinit tē contentum ex aliīs servientibus in fedīversō vidēre et cum usoribus ab iīs interāgere. Haē sunt exceptionēs quae in hōc particulārī servientē factae sunt.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Limitātus",
"about.domain_blocks.suspended.explanation": "Nulla data ab hōc servientē processābuntur, servābuntur aut commūtābuntur, faciendumque omnem interactionem aut communicātiōnem cum usoribus ab hōc servientē impossibilem.",
"about.domain_blocks.suspended.title": "suspensus",
"about.language_label": "Linguae",
"about.not_available": "Haec informātiō in hōc servientē nōn praebita est.",
"about.powered_by": "Nuntii socīālēs decentralizātī ā {mastodon} sustentātī.",
"about.rules": "Servo praecepta",
@@ -41,7 +43,12 @@
"account.followers": "Sectatores",
"account.followers.empty": "Nemo hunc usorem adhuc sequitur.",
"account.followers_counter": "{count, plural, one {{counter} sectator} other {{counter} sectatores}}",
"account.followers_you_know_counter": "{counter} scis",
"account.following": "Sequentia",
"account.following_counter": "{count, plural, one {{counter} sectans} other {{counter} sectans}}",
"account.follows.empty": "Hic usor adhuc neminem sequitur.",
"account.follows_you": "Sequitur te",
"account.go_to_profile": "Vade ad profile",
"account.moved_to": "{name} significavit eum suam rationem novam nunc esse:",
"account.muted": "Confutatus",
"account.requested_follow": "{name} postulavit ut te sequeretur",
+3
View File
@@ -876,6 +876,9 @@
"status.quote_error.filtered": "Lí所設定ê過濾器kā tse khàm起來",
"status.quote_error.not_available": "鋪文bē當看",
"status.quote_error.pending_approval": "鋪文當咧送",
"status.quote_error.pending_approval_popout.body": "因為無kâng ê服侍器有無kâng ê協定,佇聯邦宇宙分享ê引文可能愛開時間來顯示。",
"status.quote_error.pending_approval_popout.title": "Leh送引文?請sió等leh",
"status.quote_post_author": "引用 @{name} ê PO文ah",
"status.read_more": "讀詳細",
"status.reblog": "轉送",
"status.reblog_private": "照原PO ê通看見ê範圍轉送",
+69 -1
View File
@@ -1,6 +1,7 @@
{
"about.blocks": "Serwery moderowane",
"about.contact": "Kontakt:",
"about.default_locale": "Domyślny",
"about.disclaimer": "Mastodon jest darmowym, otwartym oprogramowaniem i znakiem towarowym Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Powód niedostępny",
"about.domain_blocks.preamble": "Domyślnie Mastodon pozwala ci przeglądać i reagować na treści od innych użytkowników z jakiegokolwiek serwera w fediwersum. Poniżej znajduje się lista wyjątków, które zostały stworzone na tym konkretnym serwerze.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Ograniczone",
"about.domain_blocks.suspended.explanation": "Żadne dane z tego serwera nie będą przetwarzane, przechowywane lub wymieniane, co uniemożliwia jakąkolwiek interakcję lub komunikację z użytkownikami z tego serwera.",
"about.domain_blocks.suspended.title": "Zawieszono",
"about.language_label": "Język",
"about.not_available": "Ta informacja nie została udostępniona na tym serwerze.",
"about.powered_by": "Zdecentralizowane media społecznościowe napędzane przez {mastodon}",
"about.rules": "Regulamin serwera",
@@ -19,13 +21,21 @@
"account.block_domain": "Blokuj wszystko z {domain}",
"account.block_short": "Zablokuj",
"account.blocked": "Zablokowany(-a)",
"account.blocking": "Blokowanie",
"account.cancel_follow_request": "Nie obserwuj",
"account.copy": "Skopiuj link do profilu",
"account.direct": "Napisz bezpośrednio do @{name}",
"account.disable_notifications": "Przestań powiadamiać mnie o wpisach @{name}",
"account.domain_blocking": "Blokowanie domeny",
"account.edit_profile": "Edytuj profil",
"account.enable_notifications": "Powiadamiaj mnie o wpisach @{name}",
"account.endorse": "Wyróżnij na profilu",
"account.familiar_followers_many": "Obserwowane przez: {name1}, {name2} i {othersCount, plural, one {jeszcze jedną osobę, którą znasz} few {# inne osoby, które znasz} many {# innych osób, które znasz} other {# innych osób, które znasz}}",
"account.familiar_followers_one": "Obserwowane przez {name1}",
"account.familiar_followers_two": "Obserwowane przez {name1} i {name2}",
"account.featured": "Wyróżnione",
"account.featured.accounts": "Profile",
"account.featured.hashtags": "Tagi",
"account.featured_tags.last_status_at": "Ostatni post {date}",
"account.featured_tags.last_status_never": "Brak postów",
"account.follow": "Obserwuj",
@@ -33,9 +43,11 @@
"account.followers": "Obserwujący",
"account.followers.empty": "Nikt jeszcze nie obserwuje tego użytkownika.",
"account.followers_counter": "{count, plural, one {{counter} obserwujący} few {{counter} obserwujących} many {{counter} obserwujących} other {{counter} obserwujących}}",
"account.followers_you_know_counter": "{counter} które znasz",
"account.following": "Obserwowani",
"account.following_counter": "{count, plural, one {{counter} obserwowany} few {{counter} obserwowanych} many {{counter} obserwowanych} other {{counter} obserwowanych}}",
"account.follows.empty": "Ten użytkownik nie obserwuje jeszcze nikogo.",
"account.follows_you": "Obserwuje cię",
"account.go_to_profile": "Przejdź do profilu",
"account.hide_reblogs": "Ukryj podbicia od @{name}",
"account.in_memoriam": "Ku pamięci.",
@@ -50,18 +62,23 @@
"account.mute_notifications_short": "Wycisz powiadomienia",
"account.mute_short": "Wycisz",
"account.muted": "Wyciszony",
"account.muting": "Wyciszenie",
"account.mutual": "Obserwujecie siebie nazwajem",
"account.no_bio": "Brak opisu.",
"account.open_original_page": "Otwórz stronę oryginalną",
"account.posts": "Wpisy",
"account.posts_with_replies": "Wpisy i odpowiedzi",
"account.remove_from_followers": "Usuń {name} z obserwujących",
"account.report": "Zgłoś @{name}",
"account.requested": "Oczekująca prośba, kliknij aby anulować",
"account.requested_follow": "{name} chce cię zaobserwować",
"account.requests_to_follow_you": "Prośby o obserwowanie",
"account.share": "Udostępnij profil @{name}",
"account.show_reblogs": "Pokazuj podbicia od @{name}",
"account.statuses_counter": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}}",
"account.unblock": "Odblokuj @{name}",
"account.unblock_domain": "Odblokuj domenę {domain}",
"account.unblock_domain_short": "Odblokuj",
"account.unblock_short": "Odblokuj",
"account.unendorse": "Nie wyświetlaj w profilu",
"account.unfollow": "Nie obserwuj",
@@ -202,6 +219,12 @@
"confirmations.delete_list.confirm": "Usuń",
"confirmations.delete_list.message": "Czy na pewno chcesz trwale usunąć tę listę?",
"confirmations.delete_list.title": "Usunąć listę?",
"confirmations.discard_draft.confirm": "Odrzuć i kontynuuj",
"confirmations.discard_draft.edit.cancel": "Wznów edytowanie",
"confirmations.discard_draft.edit.message": "Kontynuowanie spowoduje utratę wszystkich zmian wprowadzonych przez Ciebie w aktualnie edytowanym poście.",
"confirmations.discard_draft.edit.title": "Odrzucić zmiany w poście?",
"confirmations.discard_draft.post.cancel": "Wznów wersję roboczą",
"confirmations.discard_draft.post.title": "Anulować wersję roboczą?",
"confirmations.discard_edit_media.confirm": "Odrzuć",
"confirmations.discard_edit_media.message": "Masz niezapisane zmiany w opisie lub podglądzie, odrzucić je mimo to?",
"confirmations.follow_to_list.confirm": "Zaobserwuj i dodaj do listy",
@@ -218,6 +241,9 @@
"confirmations.redraft.confirm": "Usuń i popraw",
"confirmations.redraft.message": "Czy na pewno chcesz usunąć i poprawić ten wpis? Polubienia, podbicia i komentarze pierwotnego wpisu zostaną utracone.",
"confirmations.redraft.title": "Usunąć i poprawić wpis?",
"confirmations.remove_from_followers.confirm": "Usuń obserwującego",
"confirmations.remove_from_followers.message": "{name} przestanie Cię obserwować. Czy na pewno chcesz kontynuować?",
"confirmations.remove_from_followers.title": "Usunąć obserwującego?",
"confirmations.unfollow.confirm": "Nie obserwuj",
"confirmations.unfollow.message": "Czy na pewno nie chcesz obserwować {name}?",
"confirmations.unfollow.title": "Cofnąć obserwację?",
@@ -307,9 +333,15 @@
"errors.unexpected_crash.copy_stacktrace": "Skopiuj stacktrace do schowka",
"errors.unexpected_crash.report_issue": "Zgłoś problem",
"explore.suggested_follows": "Ludzie",
"explore.title": "Na czasie",
"explore.trending_links": "Aktualności",
"explore.trending_statuses": "Wpisy",
"explore.trending_tags": "Hasztagi",
"featured_carousel.header": "{count, plural, one {Przypięty post} other {Przypięte posty}}",
"featured_carousel.next": "Następny",
"featured_carousel.post": "Opublikuj",
"featured_carousel.previous": "Poprzedni",
"featured_carousel.slide": "{index} z {total}",
"filter_modal.added.context_mismatch_explanation": "To filtrowanie nie dotyczy kategorii, w której pojawił się ten wpis. Jeśli chcesz, aby wpis był filtrowany również w tym kontekście, musisz edytować ustawienia filtrowania.",
"filter_modal.added.context_mismatch_title": "Niewłaściwy kontekst!",
"filter_modal.added.expired_explanation": "Ta kategoria filtrowania wygasła, aby ją zastosować, należy zmienić datę wygaśnięcia.",
@@ -362,6 +394,8 @@
"generic.saved": "Zapisano",
"getting_started.heading": "Pierwsze kroki",
"hashtag.admin_moderation": "Otwórz interfejs moderacji #{name}",
"hashtag.browse": "Przeglądaj posty z #{hashtag}",
"hashtag.browse_from_account": "Przeglądaj posty od @{name} z #{hashtag}",
"hashtag.column_header.tag_mode.all": "i {additional}",
"hashtag.column_header.tag_mode.any": "lub {additional}",
"hashtag.column_header.tag_mode.none": "bez {additional}",
@@ -374,7 +408,10 @@
"hashtag.counter_by_accounts": "{count, plural, one {{counter} osoba} few {{counter} osoby} many {{counter} osób} other {{counter} osób}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}} dzisiaj",
"hashtag.feature": "Wyróżnij w profilu",
"hashtag.follow": "Obserwuj hasztag",
"hashtag.mute": "Wycisz #{hashtag}",
"hashtag.unfeature": "Nie wyróżniaj w profilu",
"hashtag.unfollow": "Przestań obserwować hashtag",
"hashtags.and_other": "…i {count, plural, other {jeszcze #}}",
"hints.profiles.followers_may_be_missing": "Niektórzy obserwujący ten profil mogą być niewidoczni.",
@@ -383,6 +420,7 @@
"hints.profiles.see_more_followers": "Zobacz więcej obserwujących na {domain}",
"hints.profiles.see_more_follows": "Zobacz więcej obserwowanych na {domain}",
"hints.profiles.see_more_posts": "Zobacz więcej wpisów na {domain}",
"home.column_settings.show_quotes": "Pokaż cytaty",
"home.column_settings.show_reblogs": "Pokazuj podbicia",
"home.column_settings.show_replies": "Pokazuj odpowiedzi",
"home.hide_announcements": "Ukryj ogłoszenia",
@@ -456,6 +494,8 @@
"keyboard_shortcuts.translate": "aby przetłumaczyć wpis",
"keyboard_shortcuts.unfocus": "Opuść pole tekstowe",
"keyboard_shortcuts.up": "Przesuń w górę na liście",
"learn_more_link.got_it": "Rozumiem",
"learn_more_link.learn_more": "Dowiedz się więcej",
"lightbox.close": "Zamknij",
"lightbox.next": "Następne",
"lightbox.previous": "Poprzednie",
@@ -505,8 +545,10 @@
"mute_modal.you_wont_see_mentions": "Nie zobaczysz wpisów wzmiankujących tę osobę.",
"mute_modal.you_wont_see_posts": "Nie zobaczysz wpisów tej osoby, ale ona może widzieć twoje.",
"navigation_bar.about": "O serwerze",
"navigation_bar.account_settings": "Hasło i zabezpieczenia",
"navigation_bar.administration": "Administracja",
"navigation_bar.advanced_interface": "Otwórz w widoku zaawansowanym",
"navigation_bar.automated_deletion": "Automatyczne usuwanie postów",
"navigation_bar.blocks": "Zablokowani",
"navigation_bar.bookmarks": "Zakładki",
"navigation_bar.direct": "Wzmianki bezpośrednie",
@@ -516,13 +558,21 @@
"navigation_bar.follow_requests": "Prośby o obserwowanie",
"navigation_bar.followed_tags": "Obserwowane hasztagi",
"navigation_bar.follows_and_followers": "Obserwowani i obserwujący",
"navigation_bar.import_export": "Import i eksport",
"navigation_bar.lists": "Listy",
"navigation_bar.logout": "Wyloguj",
"navigation_bar.moderation": "Moderacja",
"navigation_bar.more": "Więcej",
"navigation_bar.mutes": "Wyciszeni",
"navigation_bar.opened_in_classic_interface": "Wpisy, konta i inne określone strony są domyślnie otwierane w widoku klasycznym.",
"navigation_bar.preferences": "Ustawienia",
"navigation_bar.privacy_and_reach": "Prywatność i zasięg",
"navigation_bar.search": "Szukaj",
"navigation_bar.search_trends": "Szukaj / Na czasie",
"navigation_panel.collapse_followed_tags": "Zwiń menu obserwowanych hashtagów",
"navigation_panel.collapse_lists": "Zwiń menu listy",
"navigation_panel.expand_followed_tags": "Rozwiń menu obserwowanych hashtagów",
"navigation_panel.expand_lists": "Rozwiń menu listy",
"not_signed_in_indicator.not_signed_in": "Zaloguj się, aby uzyskać dostęp.",
"notification.admin.report": "{name} zgłosił {target}",
"notification.admin.report_account": "{name} zgłosił(a) {count, plural, one {1 wpis} few {# wpisy} other {# wpisów}} z {target} w kategorii {category}",
@@ -749,6 +799,7 @@
"report_notification.categories.violation": "Naruszenie zasad",
"report_notification.categories.violation_sentence": "naruszenie zasad",
"report_notification.open": "Otwórz zgłoszenie",
"search.clear": "Wyczyść wyszukiwanie",
"search.no_recent_searches": "Brak ostatnich wyszukiwań",
"search.placeholder": "Szukaj",
"search.quick_action.account_search": "Profile pasujące do {x}",
@@ -790,6 +841,8 @@
"status.bookmark": "Dodaj zakładkę",
"status.cancel_reblog_private": "Cofnij podbicie",
"status.cannot_reblog": "Ten wpis nie może zostać podbity",
"status.context.load_new_replies": "Dostępne są nowe odpowiedzi",
"status.context.loading": "Sprawdzanie kolejnych odpowiedzi",
"status.continued_thread": "Ciąg dalszy wątku",
"status.copy": "Skopiuj odnośnik do wpisu",
"status.delete": "Usuń",
@@ -815,6 +868,10 @@
"status.mute_conversation": "Wycisz konwersację",
"status.open": "Rozszerz ten wpis",
"status.pin": "Przypnij do profilu",
"status.quote_error.filtered": "Ukryte z powodu jednego z Twoich filtrów",
"status.quote_error.not_available": "Post niedostępny",
"status.quote_error.pending_approval": "Post oczekujący",
"status.quote_post_author": "Zacytowano post @{name}",
"status.read_more": "Czytaj dalej",
"status.reblog": "Podbij",
"status.reblog_private": "Podbij dla odbiorców oryginalnego wpisu",
@@ -844,8 +901,13 @@
"subscribed_languages.save": "Zapisz zmiany",
"subscribed_languages.target": "Zmień subskrybowane języki dla {target}",
"tabs_bar.home": "Strona główna",
"tabs_bar.menu": "Menu",
"tabs_bar.notifications": "Powiadomienia",
"tabs_bar.publish": "Nowy post",
"tabs_bar.search": "Szukaj",
"terms_of_service.effective_as_of": "Obowiązuje od {date}",
"terms_of_service.title": "Warunki korzystania z usługi",
"terms_of_service.upcoming_changes_on": "Nadchodzące zmiany od {date}",
"time_remaining.days": "{number, plural, one {Pozostał # dzień} few {Pozostały # dni} many {Pozostało # dni} other {Pozostało # dni}}",
"time_remaining.hours": "{number, plural, one {Pozostała # godzina} few {Pozostały # godziny} many {Pozostało # godzin} other {Pozostało # godzin}}",
"time_remaining.minutes": "{number, plural, one {Pozostała # minuta} few {Pozostały # minuty} many {Pozostało # minut} other {Pozostało # minut}}",
@@ -876,6 +938,12 @@
"video.expand": "Rozszerz film",
"video.fullscreen": "Pełny ekran",
"video.hide": "Ukryj film",
"video.mute": "Wycisz",
"video.pause": "Pauzuj",
"video.play": "Odtwórz"
"video.play": "Odtwórz",
"video.skip_backward": "Skocz do tyłu",
"video.skip_forward": "Skocz do przodu",
"video.unmute": "Wyłącz wyciszenie",
"video.volume_down": "Zmniejsz głośność",
"video.volume_up": "Zwiększ głośność"
}
+7
View File
@@ -498,6 +498,8 @@
"keyboard_shortcuts.translate": "bir gönderiyi çevirmek için",
"keyboard_shortcuts.unfocus": "Aramada bir gönderiye odaklanmamak için",
"keyboard_shortcuts.up": "Listede yukarıya çıkmak için",
"learn_more_link.got_it": "Anladım",
"learn_more_link.learn_more": "Daha fazla bilgi edin",
"lightbox.close": "Kapat",
"lightbox.next": "Sonraki",
"lightbox.previous": "Önceki",
@@ -873,6 +875,11 @@
"status.open": "Bu gönderiyi genişlet",
"status.pin": "Profile sabitle",
"status.quote_error.filtered": "Bazı filtrelerinizden dolayı gizlenmiştir",
"status.quote_error.not_available": "Gönderi kullanılamıyor",
"status.quote_error.pending_approval": "Gönderi beklemede",
"status.quote_error.pending_approval_popout.body": "Fediverse genelinde paylaşılan alıntıların görüntülenmesi zaman alabilir, çünkü farklı sunucuların farklı protokolleri vardır.",
"status.quote_error.pending_approval_popout.title": "Bekleyen bir teklif mi var? Sakin olun.",
"status.quote_post_author": "@{name} adlı kullanıcının bir gönderisini alıntıladı",
"status.read_more": "Devamını okuyun",
"status.reblog": "Yeniden paylaş",
"status.reblog_private": "Özgün görünürlük ile yeniden paylaş",
+4 -3
View File
@@ -45,7 +45,7 @@ const AccountRoleFactory = ImmutableRecord<AccountRoleShape>({
// Account
export interface AccountShape
extends Required<
Omit<ApiAccountJSON, 'emojis' | 'fields' | 'roles' | 'moved'>
Omit<ApiAccountJSON, 'emojis' | 'fields' | 'roles' | 'moved' | 'url'>
> {
emojis: ImmutableList<CustomEmoji>;
fields: ImmutableList<AccountField>;
@@ -55,6 +55,7 @@ export interface AccountShape
note_plain: string | null;
hidden: boolean;
moved: string | null;
url: string;
}
export type Account = RecordOf<AccountShape>;
@@ -148,8 +149,8 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) {
note_emojified: emojify(accountNote, emojiMap),
note_plain: unescapeHTML(accountNote),
url:
accountJSON.url.startsWith('http://') ||
accountJSON.url.startsWith('https://')
accountJSON.url?.startsWith('http://') ||
accountJSON.url?.startsWith('https://')
? accountJSON.url
: accountJSON.uri,
});
@@ -36,6 +36,7 @@ export type NotificationGroupFavourite =
export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>;
export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>;
export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>;
export type NotificationGroupQuote = BaseNotificationWithStatus<'quote'>;
export type NotificationGroupPoll = BaseNotificationWithStatus<'poll'>;
export type NotificationGroupUpdate = BaseNotificationWithStatus<'update'>;
export type NotificationGroupFollow = BaseNotification<'follow'>;
@@ -87,6 +88,7 @@ export type NotificationGroup =
| NotificationGroupReblog
| NotificationGroupStatus
| NotificationGroupMention
| NotificationGroupQuote
| NotificationGroupPoll
| NotificationGroupUpdate
| NotificationGroupFollow
@@ -137,6 +139,7 @@ export function createNotificationGroupFromJSON(
case 'reblog':
case 'status':
case 'mention':
case 'quote':
case 'poll':
case 'update': {
const { status_id: statusId, ...groupWithoutStatus } = group;
@@ -209,6 +212,7 @@ export function createNotificationGroupFromNotificationJSON(
case 'reblog':
case 'status':
case 'mention':
case 'quote':
case 'poll':
case 'update':
return {
@@ -20,5 +20,16 @@ export function loadPolyfills() {
loadIntlPolyfills(),
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- those properties might not exist in old browsers, even if they are always here in types
needsExtraPolyfills && importExtraPolyfills(),
loadEmojiPolyfills(),
]);
}
// In the case of no /v support, rely on the emojibase data.
async function loadEmojiPolyfills() {
if (!('unicodeSets' in RegExp.prototype)) {
emojiRegexPolyfill = (await import('emojibase-regex/emoji')).default;
}
}
// Null unless polyfill is needed.
export let emojiRegexPolyfill: RegExp | null = null;
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m228-240 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T458-480L320-240h-92Zm360 0 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480L680-240h-92Z"/></svg>

After

Width:  |  Height:  |  Size: 322 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m228-240 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T458-480L320-240h-92Zm360 0 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480L680-240h-92ZM320-500q25 0 42.5-17.5T380-560q0-25-17.5-42.5T320-620q-25 0-42.5 17.5T260-560q0 25 17.5 42.5T320-500Zm360 0q25 0 42.5-17.5T740-560q0-25-17.5-42.5T680-620q-25 0-42.5 17.5T620-560q0 25 17.5 42.5T680-500Zm0-60Zm-360 0Z"/></svg>

After

Width:  |  Height:  |  Size: 538 B