Merge commit '400c1f3e8ec0ffd33ad30d9334b9210cdb89b14c' into glitch-soc/merge-upstream

This commit is contained in:
Claire committed 2026-01-20 16:14:14 +01:00
commit 5d04c56c48
48 files changed
+671 -131

No files matched your search

+17 -14
View File
@@ -128,15 +128,18 @@ export default function api(withAuthorization = true) {
}
type ApiUrl = `v${1 | '1_alpha' | 2}/${string}`;
type RequestParamsOrData = Record<string, unknown>;
type RequestParamsOrData<T = unknown> = T | Record<string, unknown>;
export async function apiRequest<ApiResponse = unknown>(
export async function apiRequest<
ApiResponse = unknown,
ApiParamsOrData = unknown,
>(
method: Method,
url: string,
args: {
signal?: AbortSignal;
params?: RequestParamsOrData;
data?: RequestParamsOrData;
params?: RequestParamsOrData<ApiParamsOrData>;
data?: RequestParamsOrData<ApiParamsOrData>;
timeout?: number;
} = {},
) {
@@ -149,30 +152,30 @@ export async function apiRequest<ApiResponse = unknown>(
return data;
}
export async function apiRequestGet<ApiResponse = unknown>(
export async function apiRequestGet<ApiResponse = unknown, ApiParams = unknown>(
url: ApiUrl,
params?: RequestParamsOrData,
params?: RequestParamsOrData<ApiParams>,
) {
return apiRequest<ApiResponse>('GET', url, { params });
}
export async function apiRequestPost<ApiResponse = unknown>(
export async function apiRequestPost<ApiResponse = unknown, ApiData = unknown>(
url: ApiUrl,
data?: RequestParamsOrData,
data?: RequestParamsOrData<ApiData>,
) {
return apiRequest<ApiResponse>('POST', url, { data });
}
export async function apiRequestPut<ApiResponse = unknown>(
export async function apiRequestPut<ApiResponse = unknown, ApiData = unknown>(
url: ApiUrl,
data?: RequestParamsOrData,
data?: RequestParamsOrData<ApiData>,
) {
return apiRequest<ApiResponse>('PUT', url, { data });
}
export async function apiRequestDelete<ApiResponse = unknown>(
url: ApiUrl,
params?: RequestParamsOrData,
) {
export async function apiRequestDelete<
ApiResponse = unknown,
ApiParams = unknown,
>(url: ApiUrl, params?: RequestParamsOrData<ApiParams>) {
return apiRequest<ApiResponse>('DELETE', url, { params });
}
@@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import { useState, useRef, useCallback, useId } from 'react';
import { FormattedMessage } from 'react-intl';
@@ -15,7 +16,9 @@ export const DomainPill: React.FC<{
domain: string;
username: string;
isSelf: boolean;
}> = ({ domain, username, isSelf }) => {
children?: ReactNode;
className?: string;
}> = ({ domain, username, isSelf, children, className }) => {
const accessibilityId = useId();
const [open, setOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
@@ -32,7 +35,9 @@ export const DomainPill: React.FC<{
return (
<>
<button
className={classNames('account__domain-pill', { active: open })}
className={classNames('account__domain-pill', className, {
active: open,
})}
ref={triggerRef}
onClick={handleClick}
aria-expanded={open}
@@ -40,6 +45,7 @@ export const DomainPill: React.FC<{
type='button'
>
{domain}
{children}
</button>
<Overlay
@@ -1,25 +1,22 @@
import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { AccountBio } from '@/mastodon/components/account_bio';
import { DisplayName } from '@/mastodon/components/display_name';
import { AnimateEmojiProvider } from '@/mastodon/components/emoji/context';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import { openModal } from 'mastodon/actions/modal';
import { Avatar } from 'mastodon/components/avatar';
import { Icon } from 'mastodon/components/icon';
import { AccountNote } from 'mastodon/features/account/components/account_note';
import { DomainPill } from 'mastodon/features/account/components/domain_pill';
import FollowRequestNoteContainer from 'mastodon/features/account/containers/follow_request_note_container';
import { autoPlayGif, me, domain as localDomain } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account';
import { getAccountHidden } from 'mastodon/selectors/accounts';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { isRedesignEnabled } from '../common';
import { AccountName } from './account_name';
import { AccountBadges } from './badges';
import { AccountButtons } from './buttons';
import { FamiliarFollowers } from './familiar_followers';
@@ -28,6 +25,7 @@ import { AccountInfo } from './info';
import { MemorialNote } from './memorial_note';
import { MovedNote } from './moved_note';
import { AccountNumberFields } from './number_fields';
import redesignClasses from './redesign.module.scss';
import { AccountTabs } from './tabs';
const titleFromAccount = (account: Account) => {
@@ -47,7 +45,6 @@ export const AccountHeader: React.FC<{
hideTabs?: boolean;
}> = ({ accountId, hideTabs }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const account = useAppSelector((state) => state.accounts.get(accountId));
const relationship = useAppSelector((state) =>
state.relationships.get(accountId),
@@ -85,8 +82,6 @@ export const AccountHeader: React.FC<{
const suspendedOrHidden = hidden || account.suspended;
const isLocal = !account.acct.includes('@');
const username = account.acct.split('@')[0];
const domain = isLocal ? localDomain : account.acct.split('@')[1];
return (
<div className='account-timeline__header'>
@@ -133,38 +128,27 @@ export const AccountHeader: React.FC<{
/>
</a>
<AccountButtons
accountId={accountId}
className='account__header__buttons--desktop'
/>
{!isRedesignEnabled() && (
<AccountButtons
accountId={accountId}
className='account__header__buttons--desktop'
/>
)}
</div>
<div className='account__header__tabs__name'>
<h1>
<DisplayName account={account} variant='simple' />
<small>
<span>
@{username}
<span className='invisible'>@{domain}</span>
</span>
<DomainPill
username={username ?? ''}
domain={domain ?? ''}
isSelf={me === account.id}
/>
{account.locked && (
<Icon
id='lock'
icon={LockIcon}
aria-label={intl.formatMessage({
id: 'account.locked_info',
defaultMessage:
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
})}
/>
)}
</small>
</h1>
<div
className={classNames(
'account__header__tabs__name',
isRedesignEnabled() && redesignClasses.nameWrapper,
)}
>
<AccountName
accountId={accountId}
className={classNames(
isRedesignEnabled() && redesignClasses.name,
)}
/>
{isRedesignEnabled() && <AccountButtons accountId={accountId} />}
</div>
<AccountBadges accountId={accountId} />
@@ -0,0 +1,68 @@
import type { FC } from 'react';
import { useIntl } from 'react-intl';
import { DisplayName } from '@/mastodon/components/display_name';
import { Icon } from '@/mastodon/components/icon';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAppSelector } from '@/mastodon/store';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
import { DomainPill } from '../../account/components/domain_pill';
import { isRedesignEnabled } from '../common';
import classes from './redesign.module.scss';
export const AccountName: FC<{ accountId: string; className?: string }> = ({
accountId,
className,
}) => {
const intl = useIntl();
const account = useAccount(accountId);
const me = useAppSelector((state) => state.meta.get('me') as string);
const localDomain = useAppSelector(
(state) => state.meta.get('domain') as string,
);
if (!account) {
return null;
}
const [username = '', domain = localDomain] = account.acct.split('@');
return (
<h1 className={className}>
<DisplayName account={account} variant='simple' />
<small>
<span>
@{username}
{isRedesignEnabled() && '@'}
<span className='invisible'>
{!isRedesignEnabled() && '@'}
{domain}
</span>
</span>
<DomainPill
username={username}
domain={domain}
isSelf={me === account.id}
className={(isRedesignEnabled() && classes.domainPill) || ''}
>
{isRedesignEnabled() && <Icon id='info' icon={InfoIcon} />}
</DomainPill>
{!isRedesignEnabled() && account.locked && (
<Icon
id='lock'
icon={LockIcon}
aria-label={intl.formatMessage({
id: 'account.locked_info',
defaultMessage:
'This account privacy status is set to locked. The owner manually reviews who can follow them.',
})}
/>
)}
</small>
</h1>
);
};
@@ -1,3 +1,44 @@
.nameWrapper {
display: flex;
gap: 16px;
}
.name {
flex-grow: 1;
font-size: 22px;
white-space: initial;
text-overflow: initial;
line-height: normal;
:global(.icon-info) {
margin-left: 2px;
width: 1em;
height: 1em;
align-self: center;
}
}
// Overrides .account__header__tabs__name h1 small
h1.name > small {
gap: 0;
}
.domainPill {
appearance: none;
border: none;
background: none;
padding: 0;
text-decoration: underline;
color: inherit;
font-size: 1em;
font-weight: initial;
&:global(.active) {
background: none;
color: inherit;
}
}
.fieldList {
margin-top: 16px;
}
+25 -21
View File
@@ -2,15 +2,15 @@
"about.blocks": "Modereeritavad serverid",
"about.contact": "Kontakt:",
"about.default_locale": "Vaikimisi",
"about.disclaimer": "Mastodon on tasuta ja vaba tarkvara ning Mastodon gGmbH kaubamärk.",
"about.disclaimer": "Mastodon on vaba, tasuta ja avatud lähtekoodiga tarkvara ning Mastodon gGmbH kaubamärk.",
"about.domain_blocks.no_reason_available": "Põhjus on teadmata",
"about.domain_blocks.preamble": "Mastodon lubab tavaliselt vaadata sisu ning suhelda kasutajatega ükskõik millisest teisest fediversumi serverist. Need on erandid, mis on paika pandud sellel kindlal serveril.",
"about.domain_blocks.silenced.explanation": "Sa ei näe üldiselt profiile ja sisu sellelt serverilt, kui sa just tahtlikult seda ei otsi või jälgimise moel nõusolekut ei anna.",
"about.domain_blocks.preamble": "Mastodon lubab üldiselt vaadata sisu ning suhelda kasutajatega ükskõik millisest teisest födiversumi serverist. Need on erandid, mis kehtivad selles kindlas serveris.",
"about.domain_blocks.silenced.explanation": "Sa üldjuhul ei näe profiile ja sisu sellest serverist, kui sa just tahtlikult neid ei otsi või jälgimise moel nõusolekut ei anna.",
"about.domain_blocks.silenced.title": "Piiratud",
"about.domain_blocks.suspended.explanation": "Mitte mingeid andmeid sellelt serverilt ei töödelda, salvestata ega vahetata, tehes igasuguse interaktsiooni või kirjavahetuse selle serveri kasutajatega võimatuks.",
"about.domain_blocks.suspended.explanation": "Mitte mingeid andmeid sellelt serverilt ei töödelda, salvestata ega vahetata, tehes igasuguse suhestumise või infovahetuse selle serveri kasutajatega võimatuks.",
"about.domain_blocks.suspended.title": "Peatatud",
"about.language_label": "Keel",
"about.not_available": "See info ei ole sellel serveril saadavaks tehtud.",
"about.not_available": "See info ei ole selles serveris saadavaks tehtud.",
"about.powered_by": "Hajutatud sotsiaalmeedia, mille taga on {mastodon}",
"about.rules": "Serveri reeglid",
"account.account_note_header": "Isiklik märge",
@@ -18,19 +18,19 @@
"account.badges.bot": "Robot",
"account.badges.group": "Grupp",
"account.block": "Blokeeri @{name}",
"account.block_domain": "Peida kõik domeenist {domain}",
"account.block_domain": "Blokeeri kõik domeenist {domain}",
"account.block_short": "Blokeerimine",
"account.blocked": "Blokeeritud",
"account.blocking": "Blokeeritud kasutaja",
"account.cancel_follow_request": "Võta jälgimistaotlus tagasi",
"account.copy": "Kopeeri profiili link",
"account.direct": "Maini privaatselt @{name}",
"account.disable_notifications": "Peata teavitused @{name} postitustest",
"account.disable_notifications": "Ära teavita, kui @{name} postitab",
"account.domain_blocking": "Blokeeritud domeen",
"account.edit_profile": "Muuda profiili",
"account.edit_profile_short": "Muuda",
"account.enable_notifications": "Teavita mind @{name} postitustest",
"account.endorse": "Too profiilil esile",
"account.enable_notifications": "Teavita mind, kui {name} postitab",
"account.endorse": "Too profiilis esile",
"account.familiar_followers_many": "Jälgijateks {name1}, {name2} ja veel {othersCount, plural, one {üks kasutaja, keda tead} other {# kasutajat, keda tead}}",
"account.familiar_followers_one": "Jälgijaks {name1}",
"account.familiar_followers_two": "Jälgijateks {name1} ja {name2}",
@@ -57,11 +57,12 @@
"account.go_to_profile": "Mine profiilile",
"account.hide_reblogs": "Peida @{name} jagamised",
"account.in_memoriam": "In Memoriam.",
"account.joined_long": "Liitus {date}",
"account.joined_short": "Liitus",
"account.languages": "Muuda tellitud keeli",
"account.link_verified_on": "Selle lingi autorsust kontrolliti {date}",
"account.locked_info": "Selle konto privaatsussätteks on lukustatud. Omanik vaatab käsitsi üle, kes teda jälgida saab.",
"account.media": "Meedia",
"account.media": "Meedium",
"account.mention": "Maini @{name}",
"account.moved_to": "{name} on teada andnud, et ta uus konto on nüüd:",
"account.mute": "Summuta @{name}",
@@ -81,20 +82,22 @@
"account.share": "Jaga @{name} profiili",
"account.show_reblogs": "Näita @{name} jagamisi",
"account.statuses_counter": "{count, plural, one {{counter} postitus} other {{counter} postitust}}",
"account.unblock": "Eemalda blokeering @{name}",
"account.unblock_domain": "Tee {domain} nähtavaks",
"account.unblock": "Lõpeta {name} kasutaja blokeerimine",
"account.unblock_domain": "Lõpeta {domain} domeeni blokeerimine",
"account.unblock_domain_short": "Lõpeta blokeerimine",
"account.unblock_short": "Eemalda blokeering",
"account.unblock_short": "Lõpeta blokeerimine",
"account.unendorse": "Ära kuva profiilil",
"account.unfollow": "Jälgid",
"account.unfollow": "Ära jälgi",
"account.unmute": "Lõpeta {name} kasutaja summutamine",
"account.unmute_notifications_short": "Lõpeta teavituste summutamine",
"account.unmute_short": "Lõpeta summutamine",
"account_fields_modal.close": "Sulge",
"account_fields_modal.title": "Kasutaja teave: {name}",
"account_note.placeholder": "Klõpsa märke lisamiseks",
"admin.dashboard.daily_retention": "Kasutajate päevane allesjäämine peale registreerumist",
"admin.dashboard.monthly_retention": "Kasutajate kuine allesjäämine peale registreerumist",
"admin.dashboard.retention.average": "Keskmine",
"admin.dashboard.retention.cohort": "Registreerumiskuu",
"admin.dashboard.retention.cohort": "Liitumiskuu",
"admin.dashboard.retention.cohort_size": "Uued kasutajad",
"admin.impact_report.instance_accounts": "Kontode profiilid, mille see kustutaks",
"admin.impact_report.instance_followers": "Jälgijad, kelle meie kasutajad kaotaks",
@@ -103,11 +106,11 @@
"alert.rate_limited.message": "Palun proovi uuesti pärast {retry_time, time, medium}.",
"alert.rate_limited.title": "Kiiruspiirang",
"alert.unexpected.message": "Tekkis ootamatu viga.",
"alert.unexpected.title": "Oih!",
"alt_text_badge.title": "Alternatiivtekst",
"alt_text_modal.add_alt_text": "Lisa alt-tekst",
"alert.unexpected.title": "Vaat kus lops!",
"alt_text_badge.title": "Selgitustekst",
"alt_text_modal.add_alt_text": "Lisa selgitustekst",
"alt_text_modal.add_text_from_image": "Lisa tekst pildilt",
"alt_text_modal.cancel": "Tühista",
"alt_text_modal.cancel": "Katkesta",
"alt_text_modal.change_thumbnail": "Muuda pisipilti",
"alt_text_modal.describe_for_people_with_hearing_impairments": "Kirjelda seda kuulmispuudega inimeste jaoks…",
"alt_text_modal.describe_for_people_with_visual_impairments": "Kirjelda seda nägemispuudega inimeste jaoks…",
@@ -119,7 +122,7 @@
"annual_report.announcement.description": "Vaata teavet oma suhestumise kohta Mastodonis eelmisel aastal.",
"annual_report.announcement.title": "{year}. aasta Mastodoni kokkuvõte on valmis",
"annual_report.nav_item.badge": "Uus",
"annual_report.shared_page.donate": "Anneta",
"annual_report.shared_page.donate": "Toeta rahaliselt",
"annual_report.shared_page.footer": "Loodud {heart} Mastodoni meeskonna poolt",
"annual_report.shared_page.footer_server_info": "{username} kasutab {domain}-i, üht paljudest kogukondadest, mis toimivad Mastodonil.",
"annual_report.summary.archetype.booster.desc_public": "{name} jätkas postituste otsimist, et neid edendada, tugevdades teisi loojaid täiusliku täpsusega.",
@@ -589,6 +592,7 @@
"load_pending": "{count, plural, one {# uus kirje} other {# uut kirjet}}",
"loading_indicator.label": "Laadimine…",
"media_gallery.hide": "Peida",
"minicard.more_items": "+{count}",
"moved_to_account_banner.text": "Kontot {disabledAccount} ei ole praegu võimalik kasutada, sest kolisid kontole {movedToAccount}.",
"mute_modal.hide_from_notifications": "Peida teavituste hulgast",
"mute_modal.hide_options": "Peida valikud",
@@ -767,7 +771,7 @@
"onboarding.profile.upload_avatar": "Laadi üles profiilipilt",
"onboarding.profile.upload_header": "Laadi üles profiili päis",
"password_confirmation.exceeds_maxlength": "Salasõnakinnitus on pikem kui salasõna maksimumpikkus",
"password_confirmation.mismatching": "Salasõnakinnitus ei sobi kokku",
"password_confirmation.mismatching": "Salasõnad ei klapi",
"picture_in_picture.restore": "Pane tagasi",
"poll.closed": "Suletud",
"poll.refresh": "Värskenda",
+4
View File
@@ -57,6 +57,7 @@
"account.go_to_profile": "Profile git",
"account.hide_reblogs": "@{name} kişisinin yeniden paylaşımlarını gizle",
"account.in_memoriam": "Hatırasına.",
"account.joined_long": "{date} tarihinde katıldı",
"account.joined_short": "Katıldı",
"account.languages": "Abone olunan dilleri değiştir",
"account.link_verified_on": "Bu bağlantının sahipliği {date} tarihinde denetlendi",
@@ -90,6 +91,8 @@
"account.unmute": "@{name} adlı kişinin sesini aç",
"account.unmute_notifications_short": "Bildirimlerin sesini aç",
"account.unmute_short": "Susturmayı kaldır",
"account_fields_modal.close": "Kapat",
"account_fields_modal.title": "{name} bilgileri",
"account_note.placeholder": "Not eklemek için tıklayın",
"admin.dashboard.daily_retention": "Kayıttan sonra günlük kullanıcı saklama oranı",
"admin.dashboard.monthly_retention": "Kayıttan sonra aylık kullanıcı saklama oranı",
@@ -589,6 +592,7 @@
"load_pending": "{count, plural, one {# yeni öğe} other {# yeni öğe}}",
"loading_indicator.label": "Yükleniyor…",
"media_gallery.hide": "Gizle",
"minicard.more_items": "+{count}",
"moved_to_account_banner.text": "{disabledAccount} hesabınız, {movedToAccount} hesabına taşıdığınız için şu an devre dışı.",
"mute_modal.hide_from_notifications": "Bildirimlerde gizle",
"mute_modal.hide_options": "Seçenekleri gizle",