Merge pull request #3472 from glitch-soc/glitch-soc/merge-upstream

Merge upstream changes up to 73fc8d34d98a80cfb32d3c7c5dc4794fad5470d5
This commit is contained in:
Claire 2026-04-11 16:40:09 +02:00 committed by GitHub
commit 96aa458d22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
212 changed files with 2364 additions and 1238 deletions

View File

@ -25,8 +25,8 @@ FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.3.0-nightly.2023.11.09+pr-123456 # Example: v4.3.0-nightly.2023-11-09+pr-123456
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"] # Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023-11-09"]
ARG MASTODON_VERSION_PRERELEASE="" ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"] # Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
ARG MASTODON_VERSION_METADATA="" ARG MASTODON_VERSION_METADATA=""
@ -48,29 +48,27 @@ ARG GID="991"
# Apply Mastodon build options based on options above # Apply Mastodon build options based on options above
ENV \ ENV \
# Apply Mastodon version information
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \ MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
SOURCE_COMMIT="${SOURCE_COMMIT}" \ SOURCE_COMMIT="${SOURCE_COMMIT}" \
# Apply Mastodon static files and YJIT options RAILS_SERVE_STATIC_FILES="${RAILS_SERVE_STATIC_FILES}" \
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \ RUBY_YJIT_ENABLE="${RUBY_YJIT_ENABLE}" \
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \ TZ="${TZ}"
# Apply timezone
TZ=${TZ}
# Configure runtime environment
# BIND: IP to bind Mastodon to when serving traffic
# NODE_ENV/RAILS_ENV: production settings for Node.js and Ruby on Rails
# DEBIAN_FRONTEND: suppress interactive prompts
# PATH: add Ruby and Mastodon installation directories
# MALLOC_CONF: optimize jemalloc 5.x performance
# MASTODON_SIDEKIQ_READY_FILENAME: Sidekiq readiness check filename for Kubernetes
ENV \ ENV \
# Configure the IP to bind Mastodon to when serving traffic
BIND="0.0.0.0" \ BIND="0.0.0.0" \
# Use production settings for Yarn, Node.js and related tools
NODE_ENV="production" \ NODE_ENV="production" \
# Use production settings for Ruby on Rails
RAILS_ENV="production" \ RAILS_ENV="production" \
# Add Ruby and Mastodon installation to the PATH
DEBIAN_FRONTEND="noninteractive" \ DEBIAN_FRONTEND="noninteractive" \
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \ PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
# Optimize jemalloc 5.x performance
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \ MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
# Set default shell used for running commands # Set default shell used for running commands
@ -99,10 +97,10 @@ RUN \
# Mount Apt cache and lib directories from Docker buildx caches # Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ --mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Apt update & upgrade to check for security updates to Debian image # Update package list and upgrade system packages
apt-get update; \ apt-get update; \
apt-get dist-upgrade -yq; \ apt-get dist-upgrade -yq; \
# Install jemalloc, curl and other necessary components # Install jemalloc and other necessary components
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
curl \ curl \
file \ file \
@ -112,6 +110,42 @@ RUN \
tini \ tini \
tzdata \ tzdata \
wget \ wget \
# Mastodon components
libexpat1 \
libglib2.0-0t64 \
libicu76 \
libidn12 \
libpq5 \
libreadline8t64 \
libssl3t64 \
libyaml-0-2 \
# libvips components
libcgif0 \
libexif12 \
libheif1 \
libhwy1t64 \
libimagequant0 \
libjpeg62-turbo \
liblcms2-2 \
libspng0 \
libtiff6 \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
# ffmpeg components
libdav1d7 \
libmp3lame0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopus0 \
libsnappy1v5 \
libtheora0 \
libvorbis0a \
libvorbisenc2 \
libvorbisfile3 \
libvpx9 \
libx264-164 \
libx265-215 \
; \ ; \
# Patch Ruby to use jemalloc # Patch Ruby to use jemalloc
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \ patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
@ -120,42 +154,37 @@ RUN \
patchelf \ patchelf \
; ;
# Create temporary build layer from base image # Build stage for media libraries (libvips, ffmpeg)
FROM ruby AS build FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS media-build
ARG TARGETPLATFORM ARG TARGETPLATFORM
# Set default shell used for running commands
SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
# hadolint ignore=DL3008 # hadolint ignore=DL3008
RUN \ RUN \
# Mount Apt cache and lib directories from Docker buildx caches --mount=type=cache,id=apt-native-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-native-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ # Remove automatic apt cache Docker cleanup scripts
# Install build tools and bundler dependencies from APT rm -f /etc/apt/apt.conf.d/docker-clean; \
# Install build tools for native libraries
apt-get update; \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
autoconf \ autoconf \
automake \ automake \
build-essential \ build-essential \
cmake \
git \
libgdbm-dev \
libglib2.0-dev \
libgmp-dev \
libicu-dev \
libidn-dev \
libpq-dev \
libssl-dev \
libtool \ libtool \
libyaml-dev \
meson \ meson \
nasm \ nasm \
pkg-config \ pkg-config \
shared-mime-info \
xz-utils \ xz-utils \
# libvips components # libvips components
libcgif-dev \ libcgif-dev \
libexif-dev \ libexif-dev \
libexpat1-dev \ libexpat1-dev \
libgirepository1.0-dev \ libgirepository1.0-dev \
libglib2.0-dev \
libheif-dev \ libheif-dev \
libhwy-dev \ libhwy-dev \
libimagequant-dev \ libimagequant-dev \
@ -176,8 +205,8 @@ RUN \
libx265-dev \ libx265-dev \
; ;
# Create temporary libvips specific build layer from build layer # Create temporary libvips specific build layer
FROM build AS libvips FROM media-build AS libvips
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"] # libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips # renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
@ -192,15 +221,16 @@ RUN tar xf vips-${VIPS_VERSION}.tar.xz;
WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION} WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}
# Configure and compile libvips # Configure libvips
RUN \ RUN meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false
meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false; \
cd build; \
ninja; \
ninja install;
# Create temporary ffmpeg specific build layer from build layer WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}/build
FROM build AS ffmpeg
# Compile and install libvips
RUN ninja && ninja install
# Create temporary ffmpeg specific build layer
FROM media-build AS ffmpeg
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"] # ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg # renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
@ -241,17 +271,48 @@ RUN \
--enable-shared \ --enable-shared \
--enable-version3 \ --enable-version3 \
; \ ; \
make -j$(nproc); \ make -j"$(nproc)"; \
make install; make install;
# Create temporary build layer from base image for Ruby dependencies
FROM ruby AS ruby-build
ARG TARGETPLATFORM
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Install build tools and bundler dependencies from APT
apt-get install -y --no-install-recommends \
build-essential \
git \
libgdbm-dev \
libgmp-dev \
libicu-dev \
libidn-dev \
libpq-dev \
libssl-dev \
libyaml-dev \
shared-mime-info \
zlib1g-dev \
;
# Create temporary bundler specific build layer from build layer # Create temporary bundler specific build layer from build layer
FROM build AS bundler FROM ruby-build AS bundler
ARG TARGETPLATFORM ARG TARGETPLATFORM
# Copy Gemfile config into working directory # Copy Gemfile config into working directory
COPY Gemfile* /opt/mastodon/ COPY Gemfile* /opt/mastodon/
# Copy libvips for gems that need it during install
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
COPY --from=libvips /usr/local/libvips/include /usr/local/include
RUN ldconfig
RUN \ RUN \
# Mount Ruby Gem caches # Mount Ruby Gem caches
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \ --mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
@ -267,7 +328,7 @@ RUN \
bundle install -j"$(nproc)"; bundle install -j"$(nproc)";
# Create temporary assets build layer from build layer # Create temporary assets build layer from build layer
FROM build AS precompiler FROM ruby-build AS precompiler
ARG TARGETPLATFORM ARG TARGETPLATFORM
@ -311,53 +372,6 @@ FROM ruby AS mastodon
ARG TARGETPLATFORM ARG TARGETPLATFORM
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Mount Corepack and Yarn caches from Docker buildx caches
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
# Apt update install non-dev versions of necessary components
apt-get install -y --no-install-recommends \
libexpat1 \
libglib2.0-0t64 \
libicu76 \
libidn12 \
libpq5 \
libreadline8t64 \
libssl3t64 \
libyaml-0-2 \
# libvips components
libcgif0 \
libexif12 \
libheif1 \
libhwy1t64 \
libimagequant0 \
libjpeg62-turbo \
liblcms2-2 \
libspng0 \
libtiff6 \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
# ffmpeg components
libdav1d7 \
libmp3lame0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopus0 \
libsnappy1v5 \
libtheora0 \
libvorbis0a \
libvorbisenc2 \
libvorbisfile3 \
libvpx9 \
libx264-164 \
libx265-215 \
;
# Copy Mastodon sources into final layer # Copy Mastodon sources into final layer
COPY . /opt/mastodon/ COPY . /opt/mastodon/

View File

@ -102,10 +102,10 @@ gem 'rdf-normalize', '~> 0.5'
gem 'prometheus_exporter', '~> 2.2', require: false gem 'prometheus_exporter', '~> 2.2', require: false
gem 'opentelemetry-api', '~> 1.8.0' gem 'opentelemetry-api', '~> 1.9.0'
group :opentelemetry do group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.32.0', require: false gem 'opentelemetry-exporter-otlp', '~> 0.33.0', require: false
gem 'opentelemetry-instrumentation-active_job', '~> 0.10.0', require: false gem 'opentelemetry-instrumentation-active_job', '~> 0.10.0', require: false
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.24.0', require: false gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.24.0', require: false
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.24.0', require: false gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.24.0', require: false

View File

@ -99,7 +99,7 @@ GEM
ast (2.4.3) ast (2.4.3)
attr_required (1.0.2) attr_required (1.0.2)
aws-eventstream (1.4.0) aws-eventstream (1.4.0)
aws-partitions (1.1227.0) aws-partitions (1.1236.0)
aws-sdk-core (3.244.0) aws-sdk-core (3.244.0)
aws-eventstream (~> 1, >= 1.3.0) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0) aws-partitions (~> 1, >= 1.992.0)
@ -111,7 +111,7 @@ GEM
aws-sdk-kms (1.123.0) aws-sdk-kms (1.123.0)
aws-sdk-core (~> 3, >= 3.244.0) aws-sdk-core (~> 3, >= 3.244.0)
aws-sigv4 (~> 1.5) aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.217.0) aws-sdk-s3 (1.219.0)
aws-sdk-core (~> 3, >= 3.244.0) aws-sdk-core (~> 3, >= 3.244.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5) aws-sigv4 (~> 1.5)
@ -278,7 +278,7 @@ GEM
raabro (~> 1.4) raabro (~> 1.4)
globalid (1.3.0) globalid (1.3.0)
activesupport (>= 6.1) activesupport (>= 6.1)
google-protobuf (4.34.0) google-protobuf (4.34.1)
bigdecimal bigdecimal
rake (~> 13.3) rake (~> 13.3)
googleapis-common-protos-types (1.22.0) googleapis-common-protos-types (1.22.0)
@ -451,7 +451,7 @@ GEM
mime-types-data (3.2026.0317) mime-types-data (3.2026.0317)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.9) mini_portile2 (2.8.9)
minitest (6.0.2) minitest (6.0.3)
drb (~> 2.0) drb (~> 2.0)
prism (~> 1.5) prism (~> 1.5)
msgpack (1.8.0) msgpack (1.8.0)
@ -509,11 +509,11 @@ GEM
openssl (4.0.1) openssl (4.0.1)
openssl-signature_algorithm (1.3.0) openssl-signature_algorithm (1.3.0)
openssl (> 2.0) openssl (> 2.0)
opentelemetry-api (1.8.0) opentelemetry-api (1.9.0)
logger logger
opentelemetry-common (0.23.0) opentelemetry-common (0.24.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
opentelemetry-exporter-otlp (0.32.0) opentelemetry-exporter-otlp (0.33.0)
google-protobuf (>= 3.18) google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3) googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1) opentelemetry-api (~> 1.1)
@ -576,14 +576,15 @@ GEM
opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-sidekiq (0.28.1) opentelemetry-instrumentation-sidekiq (0.28.1)
opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-registry (0.4.0) opentelemetry-registry (0.5.0)
opentelemetry-api (~> 1.1) opentelemetry-api (~> 1.1)
opentelemetry-sdk (1.10.0) opentelemetry-sdk (1.11.0)
logger
opentelemetry-api (~> 1.1) opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20) opentelemetry-common (~> 0.20)
opentelemetry-registry (~> 0.2) opentelemetry-registry (~> 0.2)
opentelemetry-semantic_conventions opentelemetry-semantic_conventions
opentelemetry-semantic_conventions (1.36.0) opentelemetry-semantic_conventions (1.37.0)
opentelemetry-api (~> 1.0) opentelemetry-api (~> 1.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ostruct (0.6.3) ostruct (0.6.3)
@ -851,8 +852,8 @@ GEM
concurrent-ruby concurrent-ruby
zeitwerk zeitwerk
stringio (3.2.0) stringio (3.2.0)
strong_migrations (2.5.2) strong_migrations (2.6.0)
activerecord (>= 7.1) activerecord (>= 7.2)
swd (2.0.3) swd (2.0.3)
activesupport (>= 3) activesupport (>= 3)
attr_required (>= 0.0.5) attr_required (>= 0.0.5)
@ -1020,8 +1021,8 @@ DEPENDENCIES
omniauth-rails_csrf_protection (~> 2.0) omniauth-rails_csrf_protection (~> 2.0)
omniauth-saml (~> 2.0) omniauth-saml (~> 2.0)
omniauth_openid_connect (~> 0.8.0) omniauth_openid_connect (~> 0.8.0)
opentelemetry-api (~> 1.8.0) opentelemetry-api (~> 1.9.0)
opentelemetry-exporter-otlp (~> 0.32.0) opentelemetry-exporter-otlp (~> 0.33.0)
opentelemetry-instrumentation-active_job (~> 0.10.0) opentelemetry-instrumentation-active_job (~> 0.10.0)
opentelemetry-instrumentation-active_model_serializers (~> 0.24.0) opentelemetry-instrumentation-active_model_serializers (~> 0.24.0)
opentelemetry-instrumentation-concurrent_ruby (~> 0.24.0) opentelemetry-instrumentation-concurrent_ruby (~> 0.24.0)

View File

@ -53,7 +53,7 @@ export interface BaseApiAccountJSON {
header_static: string; header_static: string;
header_description: string; header_description: string;
id: string; id: string;
last_status_at: string; last_status_at: string | null;
locked: boolean; locked: boolean;
show_media: boolean; show_media: boolean;
show_media_replies: boolean; show_media_replies: boolean;

View File

@ -11,7 +11,6 @@ import { useRelationship } from 'flavours/glitch/hooks/useRelationship';
import type { Relationship } from 'flavours/glitch/models/relationship'; import type { Relationship } from 'flavours/glitch/models/relationship';
import { EmojiHTML } from '../emoji/html'; import { EmojiHTML } from '../emoji/html';
import { FamiliarFollowers } from '../familiar_followers';
import { FollowButton } from '../follow_button'; import { FollowButton } from '../follow_button';
import { FormattedDateWrapper } from '../formatted_date'; import { FormattedDateWrapper } from '../formatted_date';
import { NumberFields, NumberFieldsItem } from '../number_fields'; import { NumberFields, NumberFieldsItem } from '../number_fields';
@ -31,10 +30,11 @@ interface Props {
accountId: string | undefined; accountId: string | undefined;
stats?: Stat[]; stats?: Stat[];
renderButton?: (options: RenderButtonOptions) => React.ReactNode; renderButton?: (options: RenderButtonOptions) => React.ReactNode;
withBio?: boolean;
withBorder?: boolean; withBorder?: boolean;
} }
const DEFAULT_STATS: Stat[] = ['followers', 'following', 'joined']; const DEFAULT_STATS: Stat[] = ['followers', 'posts', 'last-active'];
/** /**
* Extended account list item with bio, verified link badge, * Extended account list item with bio, verified link badge,
@ -46,6 +46,7 @@ const DEFAULT_STATS: Stat[] = ['followers', 'following', 'joined'];
export const AccountListItem: React.FC<Props> = ({ export const AccountListItem: React.FC<Props> = ({
accountId, accountId,
stats = DEFAULT_STATS, stats = DEFAULT_STATS,
withBio = true,
withBorder = true, withBorder = true,
renderButton = defaultRenderButton, renderButton = defaultRenderButton,
}) => { }) => {
@ -147,11 +148,15 @@ export const AccountListItem: React.FC<Props> = ({
/> />
} }
> >
{account.last_status_at ? (
<RelativeTimestamp <RelativeTimestamp
long long
timestamp={account.last_status_at} timestamp={account.last_status_at}
noFuture noFuture
/> />
) : (
'-'
)}
</NumberFieldsItem> </NumberFieldsItem>
)} )}
{firstVerifiedField && ( {firstVerifiedField && (
@ -161,8 +166,7 @@ export const AccountListItem: React.FC<Props> = ({
/> />
)} )}
</NumberFields> </NumberFields>
<FamiliarFollowers accountId={accountId} /> {withBio && account.note.length > 0 && (
{account.note.length > 0 && (
<EmojiHTML <EmojiHTML
className={classNames(classes.bio, 'translate')} className={classNames(classes.bio, 'translate')}
htmlString={account.note_emojified} htmlString={account.note_emojified}

View File

@ -8,13 +8,6 @@
&[data-with-border='true'] { &[data-with-border='true'] {
border-bottom: 1px solid var(--color-border-primary); border-bottom: 1px solid var(--color-border-primary);
} }
:global(.account__note) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
}
} }
.header { .header {
@ -40,6 +33,12 @@
} }
.bio { .bio {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
:any-link { :any-link {
color: var(--color-text-status-links); color: var(--color-text-status-links);

View File

@ -73,7 +73,7 @@ const BackButton: React.FC<{
}; };
export interface Props { export interface Props {
title?: string; title?: React.ReactNode;
icon?: string; icon?: string;
iconComponent?: IconProp; iconComponent?: IconProp;
active?: boolean; active?: boolean;

View File

@ -3,16 +3,20 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
max-width: 600px; max-width: 600px;
padding: 20px; padding: 24px;
gap: 16px; gap: 16px;
text-align: center; text-align: center;
color: var(--color-text-primary); color: var(--color-text-primary);
} }
.content { .content {
max-width: 370px;
svg, svg,
img { img {
width: 240px; width: 200px;
aspect-ratio: 1;
object-fit: contain;
max-width: 100%; max-width: 100%;
margin-bottom: 16px; margin-bottom: 16px;
} }
@ -20,11 +24,14 @@
h3 { h3 {
font-size: 17px; font-size: 17px;
font-weight: 500; font-weight: 500;
text-wrap: balance;
line-height: 1.2;
} }
p { p {
font-size: 15px;
margin-top: 8px; margin-top: 8px;
font-size: 15px;
color: var(--color-text-secondary); color: var(--color-text-secondary);
text-wrap: pretty;
} }
} }

View File

@ -8,9 +8,9 @@
.menuButton { .menuButton {
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-end: 1px;
top: 0; top: 1px;
padding: 9px; padding: 7px;
&::before { &::before {
// Subtle divider line separating the button from the input field // Subtle divider line separating the button from the input field

View File

@ -8,7 +8,7 @@
.copyButton { .copyButton {
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-end: 1px;
top: 0; top: 1px;
padding: 9px; padding: 7px;
} }

View File

@ -1,13 +1,18 @@
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { openModal } from '@/flavours/glitch/actions/modal';
import { Button } from '@/flavours/glitch/components/button';
import { EmptyState } from '@/flavours/glitch/components/empty_state'; import { EmptyState } from '@/flavours/glitch/components/empty_state';
import { LimitedAccountHint } from '@/flavours/glitch/features/account_timeline/components/limited_account_hint'; import { LimitedAccountHint } from '@/flavours/glitch/features/account_timeline/components/limited_account_hint';
import { areCollectionsEnabled } from '@/flavours/glitch/features/collections/utils'; import { areCollectionsEnabled } from '@/flavours/glitch/features/collections/utils';
import { useCurrentAccountId } from '@/flavours/glitch/hooks/useAccountId'; import { useCurrentAccountId } from '@/flavours/glitch/hooks/useAccountId';
import { useTheme } from '@/flavours/glitch/hooks/useTheme'; import { useTheme } from '@/flavours/glitch/hooks/useTheme';
import { useAppDispatch } from '@/flavours/glitch/store';
import ElephantDarkImage from '@/images/elephant_ui_dark.svg?react'; import ElephantDarkImage from '@/images/elephant_ui_dark.svg?react';
import ElephantLightImage from '@/images/elephant_ui_light.svg?react'; import ElephantLightImage from '@/images/elephant_ui_light.svg?react';
@ -16,6 +21,7 @@ interface EmptyMessageProps {
hidden: boolean; hidden: boolean;
blockedBy: boolean; blockedBy: boolean;
accountId?: string; accountId?: string;
withoutAddCollectionButton?: boolean;
} }
export const EmptyMessage: React.FC<EmptyMessageProps> = ({ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
@ -23,6 +29,7 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
suspended, suspended,
hidden, hidden,
blockedBy, blockedBy,
withoutAddCollectionButton,
}) => { }) => {
const { acct } = useParams<{ acct?: string }>(); const { acct } = useParams<{ acct?: string }>();
const me = useCurrentAccountId(); const me = useCurrentAccountId();
@ -30,6 +37,17 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
const ElephantImage = const ElephantImage =
theme === 'dark' ? ElephantDarkImage : ElephantLightImage; theme === 'dark' ? ElephantDarkImage : ElephantLightImage;
const dispatch = useAppDispatch();
const confirmHideFeaturedTab = useCallback(() => {
void dispatch(
openModal({
modalType: 'ACCOUNT_HIDE_FEATURED_TAB',
modalProps: {},
}),
);
}, [dispatch]);
if (!accountId) { if (!accountId) {
return null; return null;
} }
@ -49,17 +67,31 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
image={image} image={image}
title={ title={
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.no_collections' id='empty_column.account_featured_self.showcase_accounts'
defaultMessage='No collections yet' defaultMessage='Showcase your favorite accounts'
/>
}
message={
<FormattedMessage
id='empty_column.account_featured_self.showcase_accounts_desc'
defaultMessage='Collections are curated lists of accounts to help others discover more of the Fediverse.'
/> />
} }
> >
{!withoutAddCollectionButton && (
<Link to='/collections/new' className='button'> <Link to='/collections/new' className='button'>
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.no_collections_button' id='empty_column.account_featured_self.no_collections_button'
defaultMessage='Create a collection' defaultMessage='Create a collection'
/> />
</Link> </Link>
)}
<Button secondary onClick={confirmHideFeaturedTab}>
<FormattedMessage
id='empty_column.account_featured_self.no_collections_hide_tab'
defaultMessage='Hide this tab instead'
/>
</Button>
</EmptyState> </EmptyState>
); );
} else { } else {
@ -72,7 +104,7 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
message = ( message = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.pre_collections_desc' id='empty_column.account_featured_self.pre_collections_desc'
defaultMessage='Collections (coming in Mastodon 4.6) allows you to create your own curated lists of accounts to recommend to others.' defaultMessage='Collections (coming in Mastodon 4.6) allow you to create your own curated lists of accounts to recommend to others.'
/> />
); );
} }
@ -93,49 +125,23 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
/> />
); );
} else { } else {
// Standard other account empty state. if (acct) {
title = ( title = (
<FormattedMessage
id='empty_column.account_featured_other.title'
defaultMessage='Nothing to see here'
/>
);
if (hasCollections) {
if (acct) {
message = (
<FormattedMessage
id='empty_column.account_featured_other.no_collections_desc'
defaultMessage='{acct} hasnt created any collections yet.'
values={{ acct }}
/>
);
} else {
message = (
<FormattedMessage
id='empty_column.account_featured_unknown.no_collections_desc'
defaultMessage='This account hasnt created any collections yet.'
/>
);
}
} else {
if (acct) {
message = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured.other' id='empty_column.account_featured.other'
defaultMessage='{acct} hasnt featured anything yet.' defaultMessage='{acct} has not featured anything yet.'
values={{ acct }} values={{ acct }}
/> />
); );
} else { } else {
message = ( title = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_unknown.other' id='empty_column.account_featured_unknown.other'
defaultMessage='This account hasnt featured anything yet.' defaultMessage='This account has not featured anything yet.'
/> />
); );
} }
} }
}
return <EmptyState title={title} message={message} image={image} />; return <EmptyState title={title} message={message} image={image} />;
}; };

View File

@ -0,0 +1,37 @@
.subheading {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 52px;
padding: 12px 16px;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
color: var(--color-text-primary);
background: var(--color-bg-secondary);
}
.link {
display: flex;
padding-block: 4px;
font-size: 13px;
font-weight: 500;
text-transform: none;
text-decoration: none;
color: inherit;
&:hover {
text-decoration: underline;
}
&:focus-visible {
outline: var(--outline-focus-default);
outline-offset: 2px;
}
:global(.icon) {
width: 18px;
height: 18px;
}
}

View File

@ -0,0 +1,42 @@
import type { ComponentPropsWithoutRef } from 'react';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import type { IconProp } from '@/flavours/glitch/components/icon';
import { Icon } from '@/flavours/glitch/components/icon';
import { polymorphicForwardRef } from '@/types/polymorphic';
import classes from './subheading.module.scss';
export const Subheading = polymorphicForwardRef<'h2'>(
({ as: Component = 'h2', children, className, ...props }, ref) => {
return (
<Component
ref={ref}
className={classNames(classes.subheading, className)}
{...props}
>
{children}
</Component>
);
},
);
interface SubheadingLinkProps extends ComponentPropsWithoutRef<typeof Link> {
icon: IconProp;
}
export const SubheadingLink: React.FC<SubheadingLinkProps> = ({
icon,
children,
className,
...props
}) => {
return (
<Link className={classNames(classes.link, className)} {...props}>
<Icon id='subheading-icon' icon={icon} />
{children}
</Link>
);
};

View File

@ -6,9 +6,10 @@ import { useHistory } from 'react-router';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import { AccountListItem } from '@/flavours/glitch/components/account_list_item';
import { useAccount } from '@/flavours/glitch/hooks/useAccount'; import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
import { fetchEndorsedAccounts } from 'flavours/glitch/actions/accounts'; import { fetchEndorsedAccounts } from 'flavours/glitch/actions/accounts';
import { Account } from 'flavours/glitch/components/account';
import { ColumnBackButton } from 'flavours/glitch/components/column_back_button'; import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import { RemoteHint } from 'flavours/glitch/components/remote_hint'; import { RemoteHint } from 'flavours/glitch/components/remote_hint';
@ -28,10 +29,13 @@ import {
} from 'flavours/glitch/reducers/slices/collections'; } from 'flavours/glitch/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
import { CollectionListItem } from '../collections/detail/collection_list_item'; import { CollectionListItem } from '../collections/components/collection_list_item';
import { areCollectionsEnabled } from '../collections/utils'; import { areCollectionsEnabled } from '../collections/utils';
import { EmptyMessage } from './components/empty_message'; import { EmptyMessage } from './components/empty_message';
import { Subheading, SubheadingLink } from './components/subheading';
const collectionsEnabled = areCollectionsEnabled();
const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
multiColumn, multiColumn,
@ -54,13 +58,12 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
if (accountId) { if (accountId) {
void dispatch(fetchEndorsedAccounts({ accountId })); void dispatch(fetchEndorsedAccounts({ accountId }));
if (areCollectionsEnabled()) { if (collectionsEnabled) {
void dispatch(fetchAccountCollections({ accountId })); void dispatch(fetchAccountCollections({ accountId }));
} }
} }
}, [accountId, dispatch]); }, [accountId, dispatch]);
const isLoading = !accountId;
const featuredAccountIds = useAppSelector( const featuredAccountIds = useAppSelector(
(state) => (state) =>
state.user_lists.getIn( state.user_lists.getIn(
@ -68,8 +71,8 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
ImmutableList(), ImmutableList(),
) as ImmutableList<string>, ) as ImmutableList<string>,
); );
const { collections, status } = useAppSelector((state) => const { collections, status: collectionsLoadStatus } = useAppSelector(
selectAccountCollections(state, accountId ?? null), (state) => selectAccountCollections(state, accountId ?? null),
); );
const listedCollections = collections.filter( const listedCollections = collections.filter(
// Hide unlisted and empty collections to avoid confusion // Hide unlisted and empty collections to avoid confusion
@ -78,6 +81,15 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
(item) => item.discoverable && !!item.item_count, (item) => item.discoverable && !!item.item_count,
); );
const hasCollections =
collectionsEnabled &&
collectionsLoadStatus === 'idle' &&
listedCollections.length > 0;
const hasFeaturedAccounts = !featuredAccountIds.isEmpty();
const isLoading = !accountId || collectionsLoadStatus !== 'idle';
if (accountId === null) { if (accountId === null) {
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />; return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
} }
@ -92,7 +104,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
); );
} }
if (featuredAccountIds.isEmpty() && listedCollections.length === 0) { if (!hasFeaturedAccounts && !hasCollections) {
return ( return (
<AccountFeaturedWrapper accountId={accountId}> <AccountFeaturedWrapper accountId={accountId}>
<EmptyMessage <EmptyMessage
@ -114,14 +126,45 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
{accountId && ( {accountId && (
<AccountHeader accountId={accountId} hideTabs={forceEmptyState} /> <AccountHeader accountId={accountId} hideTabs={forceEmptyState} />
)} )}
{listedCollections.length > 0 && status === 'idle' && ( {!featuredAccountIds.isEmpty() && (
<> <>
<h4 className='column-subheading'> <Subheading as='h2'>
<FormattedMessage
id='account.featured.accounts'
defaultMessage='Profiles'
/>
</Subheading>
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<AccountListItem accountId={featuredAccountId} />
</Article>
))}
</ItemList>
</>
)}
{collectionsEnabled && (
<>
<Subheading as='header'>
<h2>
<FormattedMessage <FormattedMessage
id='account.featured.collections' id='account.featured.collections'
defaultMessage='Collections' defaultMessage='Collections'
/> />
</h4> </h2>
<SubheadingLink to='/collections/new' icon={AddIcon}>
<FormattedMessage
id='account.featured.new_collection'
defaultMessage='New collection'
/>
</SubheadingLink>
</Subheading>
{hasCollections ? (
<ItemList> <ItemList>
{listedCollections.map((item, index) => ( {listedCollections.map((item, index) => (
<CollectionListItem <CollectionListItem
@ -134,28 +177,15 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
/> />
))} ))}
</ItemList> </ItemList>
</> ) : (
)} <EmptyMessage
{!featuredAccountIds.isEmpty() && ( withoutAddCollectionButton
<> blockedBy={blockedBy}
<h4 className='column-subheading'> hidden={hidden}
<FormattedMessage suspended={suspended}
id='account.featured.accounts' accountId={accountId}
defaultMessage='Profiles'
/> />
</h4> )}
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<Account id={featuredAccountId} />
</Article>
))}
</ItemList>
</> </>
)} )}
<RemoteHint accountId={accountId} /> <RemoteHint accountId={accountId} />

View File

@ -8,8 +8,6 @@ import { NavLink } from 'react-router-dom';
import { useAccount } from '@/flavours/glitch/hooks/useAccount'; import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import { useAccountId } from '@/flavours/glitch/hooks/useAccountId'; import { useAccountId } from '@/flavours/glitch/hooks/useAccountId';
import { areCollectionsEnabled } from '../../collections/utils';
import classes from './styles.module.scss'; import classes from './styles.module.scss';
const isActive: Required<NavLinkProps>['isActive'] = (match, location) => const isActive: Required<NavLinkProps>['isActive'] = (match, location) =>
@ -41,14 +39,7 @@ export const AccountTabs: FC = () => {
)} )}
{show_featured && ( {show_featured && (
<NavLink exact to={`/@${acct}/featured`}> <NavLink exact to={`/@${acct}/featured`}>
{areCollectionsEnabled() ? (
<FormattedMessage
id='account.featured.collections'
defaultMessage='Collections'
/>
) : (
<FormattedMessage id='account.featured' defaultMessage='Featured' /> <FormattedMessage id='account.featured' defaultMessage='Featured' />
)}
</NavLink> </NavLink>
)} )}
</div> </div>

View File

@ -0,0 +1,20 @@
.wrapper {
display: flex;
align-items: start;
padding: 12px 16px;
gap: 16px;
&:not(.wrapperWithoutBorder) {
border-bottom: 1px solid var(--color-border-primary);
}
}
.menuButton {
padding: 4px;
margin-top: -2px;
svg {
width: 20px;
height: 20px;
}
}

View File

@ -0,0 +1,50 @@
import { useId } from 'react';
import classNames from 'classnames';
import { Article } from 'flavours/glitch/components/scrollable_list/components';
import type { CollectionLockupProps } from 'flavours/glitch/features/collections/components/collection_lockup';
import { CollectionLockup } from 'flavours/glitch/features/collections/components/collection_lockup';
import { CollectionMenu } from 'flavours/glitch/features/collections/components/collection_menu';
import classes from './collection_list_item.module.scss';
interface CollectionListItemProps extends CollectionLockupProps {
withoutBorder?: boolean;
positionInList: number;
listSize: number;
}
export const CollectionListItem: React.FC<CollectionListItemProps> = ({
collection,
withoutBorder,
positionInList,
listSize,
...otherProps
}) => {
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
return (
<Article
focusable
className={classNames(
classes.wrapper,
withoutBorder && classes.wrapperWithoutBorder,
)}
aria-labelledby={linkId}
aria-describedby={infoId}
aria-posinset={positionInList}
aria-setsize={listSize}
>
<CollectionLockup collection={collection} {...otherProps} />
<CollectionMenu
context='list'
collection={collection}
className={classes.menuButton}
/>
</Article>
);
};

View File

@ -1,21 +1,11 @@
.wrapper {
display: flex;
align-items: start;
margin-inline: 24px;
padding-block: 12px;
gap: 16px;
&:not(.wrapperWithoutBorder) {
border-bottom: 1px solid var(--color-border-primary);
}
}
.content { .content {
position: relative; position: relative;
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
align-items: center; align-items: center;
column-gap: 12px; column-gap: 12px;
color: var(--color-text-primary);
line-height: 1.3;
} }
.avatarGrid { .avatarGrid {
@ -70,13 +60,3 @@
font-size: 13px; font-size: 13px;
color: var(--color-text-secondary); color: var(--color-text-secondary);
} }
.menuButton {
padding: 4px;
margin-top: -2px;
svg {
width: 20px;
height: 20px;
}
}

View File

@ -0,0 +1,115 @@
import { useId } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
import { AvatarById } from 'flavours/glitch/components/avatar';
import { useAccountHandle } from 'flavours/glitch/components/display_name/default';
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
import { useAccount } from 'flavours/glitch/hooks/useAccount';
import { domain } from 'flavours/glitch/initial_state';
import classes from './collection_lockup.module.scss';
export const AvatarGrid: React.FC<{
accountIds: (string | undefined)[];
sensitive?: boolean;
}> = ({ accountIds: ids, sensitive }) => {
const avatarIds = [ids[0], ids[1], ids[2], ids[3]];
return (
<div
className={classNames(
classes.avatarGrid,
sensitive ? classes.avatarGridSensitive : null,
)}
>
{avatarIds.map((id) => (
<AvatarById
animate={false}
key={id}
accountId={id}
className={classes.avatar}
size={25}
/>
))}
{sensitive && <WarningIcon className={classes.avatarSensitiveBadge} />}
</div>
);
};
export interface CollectionLockupProps {
collection: ApiCollectionJSON;
withAuthorHandle?: boolean;
withTimestamp?: boolean;
}
export const CollectionLockup: React.FC<CollectionLockupProps> = ({
collection,
withAuthorHandle = true,
withTimestamp,
}) => {
const { id, name } = collection;
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
const authorAccount = useAccount(collection.account_id);
const authorHandle = useAccountHandle(authorAccount, domain);
return (
<div className={classes.content}>
<AvatarGrid
accountIds={collection.items.map((item) => item.account_id)}
sensitive={collection.sensitive}
/>
<div>
<h2 id={linkId}>
<Link to={`/collections/${id}`} className={classes.link}>
{name}
</Link>
</h2>
<ul className={classes.info} id={infoId}>
{collection.sensitive && (
<li className='sr-only'>
<FormattedMessage
id='collections.sensitive'
defaultMessage='Sensitive'
/>
</li>
)}
{withAuthorHandle && authorAccount && (
<FormattedMessage
id='collections.by_account'
defaultMessage='by {account_handle}'
values={{
account_handle: authorHandle,
}}
tagName='li'
/>
)}
<FormattedMessage
id='collections.account_count'
defaultMessage='{count, plural, one {# account} other {# accounts}}'
values={{ count: collection.item_count }}
tagName='li'
/>
{withTimestamp && (
<FormattedMessage
id='collections.last_updated_at'
defaultMessage='Last updated: {date}'
values={{
date: (
<RelativeTimestamp timestamp={collection.updated_at} long />
),
}}
tagName='li'
/>
)}
</ul>
</div>
</div>
);
};

View File

@ -0,0 +1,8 @@
.wrapper {
display: flex;
align-items: start;
padding: 12px;
gap: 12px;
border-radius: 12px;
border: 1px solid var(--color-border-primary);
}

View File

@ -0,0 +1,20 @@
import type { CollectionLockupProps } from 'flavours/glitch/features/collections/components/collection_lockup';
import { CollectionLockup } from 'flavours/glitch/features/collections/components/collection_lockup';
import classes from './collection_preview_card.module.scss';
interface CollectionPreviewCardProps extends CollectionLockupProps {
onRemove?: () => void;
}
export const CollectionPreviewCard: React.FC<CollectionPreviewCardProps> = ({
collection,
onRemove,
...otherProps
}) => {
return (
<div className={classes.wrapper}>
<CollectionLockup collection={collection} {...otherProps} />
</div>
);
};

View File

@ -5,27 +5,7 @@
} }
.preview { .preview {
display: flex;
flex-wrap: wrap-reverse;
align-items: start;
justify-content: space-between;
gap: 8px;
padding: 16px;
margin-bottom: 16px; margin-bottom: 16px;
border-radius: 8px;
color: var(--color-text-primary);
background: linear-gradient(
145deg,
var(--color-bg-brand-soft),
var(--color-bg-primary)
);
border: 1px solid var(--color-bg-brand-base);
}
.previewHeading {
font-size: 22px;
line-height: 1.3;
margin-bottom: 4px;
} }
.actions { .actions {

View File

@ -8,8 +8,6 @@ import { me } from '@/flavours/glitch/initial_state';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { changeCompose, focusCompose } from 'flavours/glitch/actions/compose'; import { changeCompose, focusCompose } from 'flavours/glitch/actions/compose';
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections'; import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
import { AvatarById } from 'flavours/glitch/components/avatar';
import { AvatarGroup } from 'flavours/glitch/components/avatar_group';
import { Button } from 'flavours/glitch/components/button'; import { Button } from 'flavours/glitch/components/button';
import { CopyLinkField } from 'flavours/glitch/components/form_fields'; import { CopyLinkField } from 'flavours/glitch/components/form_fields';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
@ -20,7 +18,7 @@ import {
} from 'flavours/glitch/components/modal_shell'; } from 'flavours/glitch/components/modal_shell';
import { useAppDispatch } from 'flavours/glitch/store'; import { useAppDispatch } from 'flavours/glitch/store';
import { AuthorNote } from '.'; import { CollectionPreviewCard } from './collection_preview_card';
import classes from './share_modal.module.scss'; import classes from './share_modal.module.scss';
const messages = defineMessages({ const messages = defineMessages({
@ -95,24 +93,13 @@ export const CollectionShareModal: React.FC<{
/> />
<div className={classes.preview}> <div className={classes.preview}>
<div> <CollectionPreviewCard collection={collection} />
<h2 className={classes.previewHeading}>{collection.name}</h2>
<AuthorNote id={collection.account_id} />
</div>
<AvatarGroup>
{collection.items.slice(0, 5).map(({ account_id }) => {
if (!account_id) return;
return (
<AvatarById key={account_id} accountId={account_id} size={28} />
);
})}
</AvatarGroup>
</div> </div>
<CopyLinkField <CopyLinkField
label={intl.formatMessage({ label={intl.formatMessage({
id: 'collection.share_modal.share_link_label', id: 'collection.share_modal.share_link_label',
defaultMessage: 'Invite share link', defaultMessage: 'Share link',
})} })}
value={collectionLink} value={collectionLink}
/> />

View File

@ -146,7 +146,6 @@ export const CollectionAccountsList: React.FC<{
<AccountListItem <AccountListItem
accountId={account_id} accountId={account_id}
withBorder={index !== items.length - 1} withBorder={index !== items.length - 1}
stats={['followers', 'last-active']}
renderButton={renderAccountItemButton} renderButton={renderAccountItemButton}
/> />
</Article> </Article>

View File

@ -1,139 +0,0 @@
import { useId } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections';
import { AvatarById } from 'flavours/glitch/components/avatar';
import { useAccountHandle } from 'flavours/glitch/components/display_name/default';
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
import { Article } from 'flavours/glitch/components/scrollable_list/components';
import { useAccount } from 'flavours/glitch/hooks/useAccount';
import { domain } from 'flavours/glitch/initial_state';
import classes from './collection_list_item.module.scss';
import { CollectionMenu } from './collection_menu';
export const AvatarGrid: React.FC<{
accountIds: (string | undefined)[];
sensitive?: boolean;
}> = ({ accountIds: ids, sensitive }) => {
const avatarIds = [ids[0], ids[1], ids[2], ids[3]];
return (
<div
className={classNames(
classes.avatarGrid,
sensitive ? classes.avatarGridSensitive : null,
)}
>
{avatarIds.map((id) => (
<AvatarById
animate={false}
key={id}
accountId={id}
className={classes.avatar}
size={25}
/>
))}
{sensitive && <WarningIcon className={classes.avatarSensitiveBadge} />}
</div>
);
};
export const CollectionListItem: React.FC<{
collection: ApiCollectionJSON;
withoutBorder?: boolean;
withAuthorHandle?: boolean;
withTimestamp?: boolean;
positionInList: number;
listSize: number;
}> = ({
collection,
withoutBorder,
withAuthorHandle = true,
withTimestamp,
positionInList,
listSize,
}) => {
const { id, name } = collection;
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
const authorAccount = useAccount(collection.account_id);
const authorHandle = useAccountHandle(authorAccount, domain);
return (
<Article
focusable
className={classNames(
classes.wrapper,
withoutBorder && classes.wrapperWithoutBorder,
)}
aria-labelledby={linkId}
aria-describedby={infoId}
aria-posinset={positionInList}
aria-setsize={listSize}
>
<div className={classes.content}>
<AvatarGrid
accountIds={collection.items.map((item) => item.account_id)}
sensitive={collection.sensitive}
/>
<div>
<h2 id={linkId}>
<Link to={`/collections/${id}`} className={classes.link}>
{name}
</Link>
</h2>
<ul className={classes.info} id={infoId}>
{collection.sensitive && (
<li className='sr-only'>
<FormattedMessage
id='collections.sensitive'
defaultMessage='Sensitive'
/>
</li>
)}
{withAuthorHandle && authorAccount && (
<FormattedMessage
id='collections.by_account'
defaultMessage='by {account_handle}'
values={{
account_handle: authorHandle,
}}
tagName='li'
/>
)}
<FormattedMessage
id='collections.account_count'
defaultMessage='{count, plural, one {# account} other {# accounts}}'
values={{ count: collection.item_count }}
tagName='li'
/>
{withTimestamp && (
<FormattedMessage
id='collections.last_updated_at'
defaultMessage='Last updated: {date}'
values={{
date: (
<RelativeTimestamp timestamp={collection.updated_at} long />
),
}}
tagName='li'
/>
)}
</ul>
</div>
</div>
<CollectionMenu
context='list'
collection={collection}
className={classes.menuButton}
/>
</Article>
);
};

View File

@ -23,8 +23,9 @@ import { domain, me } from 'flavours/glitch/initial_state';
import { fetchCollection } from 'flavours/glitch/reducers/slices/collections'; import { fetchCollection } from 'flavours/glitch/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
import { CollectionMenu } from '../components/collection_menu';
import { CollectionAccountsList } from './accounts_list'; import { CollectionAccountsList } from './accounts_list';
import { CollectionMenu } from './collection_menu';
import { useConfirmRevoke } from './revoke_collection_inclusion_modal'; import { useConfirmRevoke } from './revoke_collection_inclusion_modal';
import classes from './styles.module.scss'; import classes from './styles.module.scss';
@ -66,7 +67,7 @@ export const AuthorNote: React.FC<{ id: string }> = ({ id }) => {
); );
}; };
export const RevokeControls: React.FC<{ const RevokeControls: React.FC<{
collection: ApiCollectionJSON; collection: ApiCollectionJSON;
}> = ({ collection }) => { }> = ({ collection }) => {
const authorAccount = useAccount(collection.account_id); const authorAccount = useAccount(collection.account_id);

View File

@ -61,9 +61,11 @@ const AddedAccountItem: React.FC<{
onRemove(accountId); onRemove(accountId);
}, [accountId, onRemove]); }, [accountId, onRemove]);
const lastStatusAt = account?.last_status_at;
const lastPostHint = useMemo( const lastPostHint = useMemo(
() => () =>
isOlderThanAWeek(account?.last_status_at) && ( (!lastStatusAt || isOlderThanAWeek(lastStatusAt)) && (
<Badge <Badge
label={ label={
<FormattedMessage <FormattedMessage
@ -75,7 +77,7 @@ const AddedAccountItem: React.FC<{
className={classes.accountBadge} className={classes.accountBadge}
/> />
), ),
[account?.last_status_at], [lastStatusAt],
); );
return ( return (

View File

@ -10,22 +10,32 @@ import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?r
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react'; import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { Column } from 'flavours/glitch/components/column'; import { Column } from 'flavours/glitch/components/column';
import { ColumnHeader } from 'flavours/glitch/components/column_header'; import { ColumnHeader } from 'flavours/glitch/components/column_header';
import { DisplayNameSimple } from 'flavours/glitch/components/display_name/simple';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { import {
ItemList, ItemList,
Scrollable, Scrollable,
} from 'flavours/glitch/components/scrollable_list/components'; } from 'flavours/glitch/components/scrollable_list/components';
import { useAccount } from 'flavours/glitch/hooks/useAccount';
import {
useAccountId,
useCurrentAccountId,
} from 'flavours/glitch/hooks/useAccountId';
import { import {
fetchAccountCollections, fetchAccountCollections,
selectAccountCollections, selectAccountCollections,
} from 'flavours/glitch/reducers/slices/collections'; } from 'flavours/glitch/reducers/slices/collections';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
import { CollectionListItem } from './detail/collection_list_item'; import { CollectionListItem } from './components/collection_list_item';
import { messages as editorMessages } from './editor'; import { messages as editorMessages } from './editor';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.collections', defaultMessage: 'My collections' }, headingMe: { id: 'column.my_collections', defaultMessage: 'My collections' },
headingOther: {
id: 'column.other_collections',
defaultMessage: 'Collections by {name}',
},
}); });
export const Collections: React.FC<{ export const Collections: React.FC<{
@ -33,17 +43,22 @@ export const Collections: React.FC<{
}> = ({ multiColumn }) => { }> = ({ multiColumn }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const me = useAppSelector((state) => state.meta.get('me') as string); const me = useCurrentAccountId();
const accountId = useAccountId();
const account = useAccount(accountId);
const { collections, status } = useAppSelector((state) => const { collections, status } = useAppSelector((state) =>
selectAccountCollections(state, me), selectAccountCollections(state, accountId),
); );
useEffect(() => { useEffect(() => {
void dispatch(fetchAccountCollections({ accountId: me })); if (accountId) {
}, [dispatch, me]); void dispatch(fetchAccountCollections({ accountId }));
}
}, [dispatch, accountId]);
const emptyMessage = const emptyMessage =
status === 'error' ? ( status === 'error' || !accountId ? (
<FormattedMessage <FormattedMessage
id='collections.error_loading_collections' id='collections.error_loading_collections'
defaultMessage='There was an error when trying to load your collections.' defaultMessage='There was an error when trying to load your collections.'
@ -67,17 +82,27 @@ export const Collections: React.FC<{
</> </>
); );
const isOwnCollection = accountId === me;
const titleMessage = isOwnCollection
? messages.headingMe
: messages.headingOther;
const pageTitle = intl.formatMessage(titleMessage, {
name: account?.get('display_name'),
});
const pageTitleHtml = intl.formatMessage(titleMessage, {
name: <DisplayNameSimple account={account} />,
});
return ( return (
<Column <Column bindToDocument={!multiColumn} label={pageTitle}>
bindToDocument={!multiColumn}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader <ColumnHeader
title={intl.formatMessage(messages.heading)} title={pageTitleHtml}
icon='collections' icon='collections'
iconComponent={CollectionsFilledIcon} iconComponent={CollectionsFilledIcon}
multiColumn={multiColumn} multiColumn={multiColumn}
extraButton={ extraButton={
isOwnCollection && (
<Link <Link
to='/collections/new' to='/collections/new'
className='column-header__button' className='column-header__button'
@ -86,6 +111,7 @@ export const Collections: React.FC<{
> >
<Icon id='plus' icon={AddIcon} /> <Icon id='plus' icon={AddIcon} />
</Link> </Link>
)
} }
/> />
@ -105,7 +131,7 @@ export const Collections: React.FC<{
</Scrollable> </Scrollable>
<Helmet> <Helmet>
<title>{intl.formatMessage(messages.heading)}</title> <title>{pageTitle}</title>
<meta name='robots' content='noindex' /> <meta name='robots' content='noindex' />
</Helmet> </Helmet>
</Column> </Column>

View File

@ -55,12 +55,20 @@ export const AccountList: FC<AccountListProps> = ({
} }
const children = const children =
list?.items.map((followerId) => ( list?.items.map((followerId) => (
<AccountListItem key={followerId} accountId={followerId} /> <AccountListItem
key={followerId}
accountId={followerId}
withBio={false}
/>
)) ?? []; )) ?? [];
if (prependAccountId) { if (prependAccountId) {
children.unshift( children.unshift(
<AccountListItem key={prependAccountId} accountId={prependAccountId} />, <AccountListItem
key={prependAccountId}
accountId={prependAccountId}
withBio={false}
/>,
); );
} }
return children; return children;

View File

@ -11,6 +11,7 @@ import type { Map as ImmutableMap } from 'immutable';
import { animated, useSpring } from '@react-spring/web'; import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react'; import { useDrag } from '@use-gesture/react';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import AddIcon from '@/material-icons/400-24px/add.svg?react'; import AddIcon from '@/material-icons/400-24px/add.svg?react';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
@ -226,6 +227,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
const { signedIn, permissions, disabledAccountId } = useIdentity(); const { signedIn, permissions, disabledAccountId } = useIdentity();
const location = useLocation(); const location = useLocation();
const showSearch = useBreakpoint('full') && !multiColumn; const showSearch = useBreakpoint('full') && !multiColumn;
const account = useAccount(me);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
let banner: React.ReactNode; let banner: React.ReactNode;
@ -361,7 +363,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
{areCollectionsEnabled() && ( {areCollectionsEnabled() && (
<ColumnLink <ColumnLink
transparent transparent
to='/collections' to={`/@${account?.acct}/collections`}
icon='collections' icon='collections'
iconComponent={CollectionsIcon} iconComponent={CollectionsIcon}
activeIconComponent={CollectionsActiveIcon} activeIconComponent={CollectionsActiveIcon}

View File

@ -0,0 +1,69 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { DisplayNameSimple } from '@/flavours/glitch/components/display_name/simple';
import { Icon } from '@/flavours/glitch/components/icon';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?react';
import type {
NotificationGroupAddedToCollection,
NotificationGroupCollectionUpdate,
} from 'flavours/glitch/models/notification_group';
import { CollectionPreviewCard } from '../../collections/components/collection_preview_card';
export const NotificationCollection: React.FC<{
notification:
| NotificationGroupAddedToCollection
| NotificationGroupCollectionUpdate;
unread: boolean;
}> = ({ notification, unread }) => {
const { collection, type } = notification;
const collectionCreatorAccount = useAccount(collection.account_id);
return (
<div
className={classNames(
'notification-group',
`notification-group--${type}`,
{ 'notification-group--unread': unread },
)}
>
<div className='notification-group__icon'>
<Icon id='collection' icon={CollectionsFilledIcon} />
</div>
<div className='notification-group__main'>
<div className='notification-group__main__header'>
<div className='notification-group__main__header__label'>
{type === 'added_to_collection' && (
<FormattedMessage
id='notification.added_to_collection'
defaultMessage='{name} added you to a collection'
values={{
name: (
<DisplayNameSimple account={collectionCreatorAccount} />
),
}}
/>
)}
{type === 'collection_update' && (
<FormattedMessage
id='notification.collection_update'
defaultMessage='{name} edited a collection youre in'
values={{
name: (
<DisplayNameSimple account={collectionCreatorAccount} />
),
}}
/>
)}
</div>
</div>
<CollectionPreviewCard collection={collection} />
</div>
</div>
);
};

View File

@ -9,6 +9,7 @@ import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
import { NotificationAdminReport } from './notification_admin_report'; import { NotificationAdminReport } from './notification_admin_report';
import { NotificationAdminSignUp } from './notification_admin_sign_up'; import { NotificationAdminSignUp } from './notification_admin_sign_up';
import { NotificationAnnualReport } from './notification_annual_report'; import { NotificationAnnualReport } from './notification_annual_report';
import { NotificationCollection } from './notification_collection';
import { NotificationFavourite } from './notification_favourite'; import { NotificationFavourite } from './notification_favourite';
import { NotificationFollow } from './notification_follow'; import { NotificationFollow } from './notification_follow';
import { NotificationFollowRequest } from './notification_follow_request'; import { NotificationFollowRequest } from './notification_follow_request';
@ -156,6 +157,15 @@ export const NotificationGroup: React.FC<{
/> />
); );
break; break;
case 'added_to_collection':
case 'collection_update':
content = (
<NotificationCollection
unread={unread}
notification={notificationGroup}
/>
);
break;
default: default:
return null; return null;
} }

View File

@ -1,15 +0,0 @@
import PropTypes from 'prop-types';
const ColumnSubheading = ({ text }) => {
return (
<div className='column-subheading'>
{text}
</div>
);
};
ColumnSubheading.propTypes = {
text: PropTypes.string.isRequired,
};
export default ColumnSubheading;

View File

@ -4,6 +4,8 @@ import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import { me } from '@/flavours/glitch/initial_state';
import { deleteCollection } from 'flavours/glitch/reducers/slices/collections'; import { deleteCollection } from 'flavours/glitch/reducers/slices/collections';
import { useAppDispatch } from 'flavours/glitch/store'; import { useAppDispatch } from 'flavours/glitch/store';
@ -34,11 +36,12 @@ export const ConfirmDeleteCollectionModal: React.FC<
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const { acct: currentUserName } = useAccount(me) ?? {};
const onConfirm = useCallback(() => { const onConfirm = useCallback(() => {
void dispatch(deleteCollection({ collectionId: id })); void dispatch(deleteCollection({ collectionId: id }));
history.push('/collections'); history.push(`/@${currentUserName}/collections`);
}, [dispatch, history, id]); }, [dispatch, history, id, currentUserName]);
return ( return (
<ConfirmationModal <ConfirmationModal

View File

@ -0,0 +1,73 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import { useCurrentAccountId } from '@/flavours/glitch/hooks/useAccountId';
import { domain } from '@/flavours/glitch/initial_state';
import { patchProfile } from '@/flavours/glitch/reducers/slices/profile_edit';
import { useAppDispatch } from 'flavours/glitch/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
title: {
id: 'confirmations.hide_featured_tab.title',
defaultMessage: 'Hide "Featured" tab?',
},
intro: {
id: 'confirmations.hide_featured_tab.intro',
defaultMessage:
'You can change this at any time under <i>Edit profile > Profile tab settings</i>.',
},
message: {
id: 'confirmations.hide_featured_tab.message',
defaultMessage:
'This will hide the tab for users on {serverName} and other servers running the latest version of Mastodon. Other displays may vary.',
},
confirm: {
id: 'confirmations.hide_featured_tab.confirm',
defaultMessage: 'Hide tab',
},
});
export const ConfirmHideFeaturedTabModal: React.FC<
BaseConfirmationModalProps
> = ({ onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const history = useHistory();
const currentAccountId = useCurrentAccountId();
const { acct: currentUserName } = useAccount(currentAccountId) ?? {};
const onConfirm = useCallback(() => {
void dispatch(patchProfile({ show_featured: false }));
history.push(`/@${currentUserName}`);
}, [currentUserName, dispatch, history]);
return (
<ConfirmationModal
title={intl.formatMessage(messages.title)}
extraContent={
<div className='prose'>
<p>
{intl.formatMessage(messages.intro, {
i: (words) => <i>{words}</i>,
})}
</p>
<p>
{intl.formatMessage(messages.message, {
serverName: domain,
})}
</p>
</div>
}
confirm={intl.formatMessage(messages.confirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};

View File

@ -85,7 +85,7 @@ export const MODAL_COMPONENTS = {
'DOMAIN_BLOCK': DomainBlockModal, 'DOMAIN_BLOCK': DomainBlockModal,
'REPORT': ReportModal, 'REPORT': ReportModal,
'REPORT_COLLECTION': ReportCollectionModal, 'REPORT_COLLECTION': ReportCollectionModal,
'SHARE_COLLECTION': () => import('@/flavours/glitch/features/collections/detail/share_modal').then(module => ({ default: module.CollectionShareModal })), 'SHARE_COLLECTION': () => import('@/flavours/glitch/features/collections/components/share_modal').then(module => ({ default: module.CollectionShareModal })),
'REVOKE_COLLECTION_INCLUSION': () => import('@/flavours/glitch/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })), 'REVOKE_COLLECTION_INCLUSION': () => import('@/flavours/glitch/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })),
'SETTINGS': SettingsModal, 'SETTINGS': SettingsModal,
'DEPRECATED_SETTINGS': () => Promise.resolve({ default: DeprecatedSettingsModal }), 'DEPRECATED_SETTINGS': () => Promise.resolve({ default: DeprecatedSettingsModal }),
@ -113,6 +113,7 @@ export const MODAL_COMPONENTS = {
'ACCOUNT_EDIT_IMAGE_ALT': accountEditModal('ImageAltModal'), 'ACCOUNT_EDIT_IMAGE_ALT': accountEditModal('ImageAltModal'),
'ACCOUNT_EDIT_IMAGE_DELETE': accountEditModal('ImageDeleteModal'), 'ACCOUNT_EDIT_IMAGE_DELETE': accountEditModal('ImageDeleteModal'),
'ACCOUNT_EDIT_IMAGE_UPLOAD': accountEditModal('ImageUploadModal'), 'ACCOUNT_EDIT_IMAGE_UPLOAD': accountEditModal('ImageUploadModal'),
'ACCOUNT_HIDE_FEATURED_TAB': () => import('@/flavours/glitch/features/ui/components/confirmation_modals/hide_featured_tab').then(module => ({ default: module.ConfirmHideFeaturedTabModal })),
}; };
/** @arg {keyof import('@/flavours/glitch/features/account_edit/modals')} type */ /** @arg {keyof import('@/flavours/glitch/features/account_edit/modals')} type */

View File

@ -240,6 +240,13 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} /> <WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} />
<WrappedRoute path={['/@:acct/featured', '/accounts/:id/featured']} component={AccountFeatured} content={children} /> <WrappedRoute path={['/@:acct/featured', '/accounts/:id/featured']} component={AccountFeatured} content={children} />
{areCollectionsEnabled() &&
[
<WrappedRoute path={['/@:acct/collections']} component={Collections} content={children} key='collections-list' />,
<WrappedRoute path={['/collections/new', '/collections/:id/edit']} component={CollectionsEditor} content={children} key='collections-editor' />,
<WrappedRoute path='/collections/:id' component={CollectionDetail} content={children} key='collections-detail' />,
]
}
<WrappedRoute path='/@:acct/tagged/:tagged?' exact component={AccountTimeline} content={children} /> <WrappedRoute path='/@:acct/tagged/:tagged?' exact component={AccountTimeline} content={children} />
<WrappedRoute path={['/@:acct/with_replies', '/accounts/:id/with_replies']} component={AccountTimeline} content={children} componentParams={{ withReplies: true }} /> <WrappedRoute path={['/@:acct/with_replies', '/accounts/:id/with_replies']} component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
<WrappedRoute path={['/accounts/:id/followers', '/users/:acct/followers', '/@:acct/followers']} component={Followers} content={children} /> <WrappedRoute path={['/accounts/:id/followers', '/users/:acct/followers', '/@:acct/followers']} component={Followers} content={children} />
@ -263,13 +270,6 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path='/followed_tags' component={FollowedTags} content={children} /> <WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
<WrappedRoute path='/mutes' component={Mutes} content={children} /> <WrappedRoute path='/mutes' component={Mutes} content={children} />
<WrappedRoute path='/lists' component={Lists} content={children} /> <WrappedRoute path='/lists' component={Lists} content={children} />
{areCollectionsEnabled() &&
[
<WrappedRoute path={['/collections/new', '/collections/:id/edit']} component={CollectionsEditor} content={children} key='collections-editor' />,
<WrappedRoute path='/collections/:id' component={CollectionDetail} content={children} key='collections-detail' />,
<WrappedRoute path='/collections' component={Collections} content={children} key='collections-list' />
]
}
<Route component={BundleColumnError} /> <Route component={BundleColumnError} />
</WrappedSwitch> </WrappedSwitch>
</ColumnsArea> </ColumnsArea>

View File

@ -311,7 +311,7 @@ interface AccountCollectionQuery {
export const selectAccountCollections = createAppSelector( export const selectAccountCollections = createAppSelector(
[ [
(_, accountId: string | null) => accountId, (_, accountId?: string | null) => accountId,
(state) => state.collections.accountCollections, (state) => state.collections.accountCollections,
(state) => state.collections.collections, (state) => state.collections.collections,
], ],

View File

@ -4170,15 +4170,6 @@ a.account__display-name {
margin: -6px 10px; margin: -6px 10px;
} }
.column-subheading {
background: var(--color-bg-secondary);
color: var(--color-text-secondary);
padding: 12px 24px;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
}
.getting-started__wrapper { .getting-started__wrapper {
flex: 0 0 auto; flex: 0 0 auto;
} }

View File

@ -1,7 +1,7 @@
@use 'variables' as *; @use 'variables' as *;
.modal-layout { .modal-layout {
background: var(--color-bg-brand-softest); background: var(--color-bg-primary);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
@ -37,6 +37,8 @@
} }
.with-zig-zag-decoration { .with-zig-zag-decoration {
isolation: isolate;
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;

View File

@ -53,7 +53,7 @@ export interface BaseApiAccountJSON {
header_static: string; header_static: string;
header_description: string; header_description: string;
id: string; id: string;
last_status_at: string; last_status_at: string | null;
locked: boolean; locked: boolean;
show_media: boolean; show_media: boolean;
show_media_replies: boolean; show_media_replies: boolean;

View File

@ -11,7 +11,6 @@ import { useRelationship } from 'mastodon/hooks/useRelationship';
import type { Relationship } from 'mastodon/models/relationship'; import type { Relationship } from 'mastodon/models/relationship';
import { EmojiHTML } from '../emoji/html'; import { EmojiHTML } from '../emoji/html';
import { FamiliarFollowers } from '../familiar_followers';
import { FollowButton } from '../follow_button'; import { FollowButton } from '../follow_button';
import { FormattedDateWrapper } from '../formatted_date'; import { FormattedDateWrapper } from '../formatted_date';
import { NumberFields, NumberFieldsItem } from '../number_fields'; import { NumberFields, NumberFieldsItem } from '../number_fields';
@ -31,10 +30,11 @@ interface Props {
accountId: string | undefined; accountId: string | undefined;
stats?: Stat[]; stats?: Stat[];
renderButton?: (options: RenderButtonOptions) => React.ReactNode; renderButton?: (options: RenderButtonOptions) => React.ReactNode;
withBio?: boolean;
withBorder?: boolean; withBorder?: boolean;
} }
const DEFAULT_STATS: Stat[] = ['followers', 'following', 'joined']; const DEFAULT_STATS: Stat[] = ['followers', 'posts', 'last-active'];
/** /**
* Extended account list item with bio, verified link badge, * Extended account list item with bio, verified link badge,
@ -46,6 +46,7 @@ const DEFAULT_STATS: Stat[] = ['followers', 'following', 'joined'];
export const AccountListItem: React.FC<Props> = ({ export const AccountListItem: React.FC<Props> = ({
accountId, accountId,
stats = DEFAULT_STATS, stats = DEFAULT_STATS,
withBio = true,
withBorder = true, withBorder = true,
renderButton = defaultRenderButton, renderButton = defaultRenderButton,
}) => { }) => {
@ -147,11 +148,15 @@ export const AccountListItem: React.FC<Props> = ({
/> />
} }
> >
{account.last_status_at ? (
<RelativeTimestamp <RelativeTimestamp
long long
timestamp={account.last_status_at} timestamp={account.last_status_at}
noFuture noFuture
/> />
) : (
'-'
)}
</NumberFieldsItem> </NumberFieldsItem>
)} )}
{firstVerifiedField && ( {firstVerifiedField && (
@ -161,8 +166,7 @@ export const AccountListItem: React.FC<Props> = ({
/> />
)} )}
</NumberFields> </NumberFields>
<FamiliarFollowers accountId={accountId} /> {withBio && account.note.length > 0 && (
{account.note.length > 0 && (
<EmojiHTML <EmojiHTML
className={classNames(classes.bio, 'translate')} className={classNames(classes.bio, 'translate')}
htmlString={account.note_emojified} htmlString={account.note_emojified}

View File

@ -8,13 +8,6 @@
&[data-with-border='true'] { &[data-with-border='true'] {
border-bottom: 1px solid var(--color-border-primary); border-bottom: 1px solid var(--color-border-primary);
} }
:global(.account__note) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
}
} }
.header { .header {
@ -40,6 +33,12 @@
} }
.bio { .bio {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
:any-link { :any-link {
color: var(--color-text-status-links); color: var(--color-text-status-links);

View File

@ -73,7 +73,7 @@ const BackButton: React.FC<{
}; };
export interface Props { export interface Props {
title?: string; title?: React.ReactNode;
icon?: string; icon?: string;
iconComponent?: IconProp; iconComponent?: IconProp;
active?: boolean; active?: boolean;

View File

@ -3,16 +3,20 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
max-width: 600px; max-width: 600px;
padding: 20px; padding: 24px;
gap: 16px; gap: 16px;
text-align: center; text-align: center;
color: var(--color-text-primary); color: var(--color-text-primary);
} }
.content { .content {
max-width: 370px;
svg, svg,
img { img {
width: 240px; width: 200px;
aspect-ratio: 1;
object-fit: contain;
max-width: 100%; max-width: 100%;
margin-bottom: 16px; margin-bottom: 16px;
} }
@ -20,11 +24,14 @@
h3 { h3 {
font-size: 17px; font-size: 17px;
font-weight: 500; font-weight: 500;
text-wrap: balance;
line-height: 1.2;
} }
p { p {
font-size: 15px;
margin-top: 8px; margin-top: 8px;
font-size: 15px;
color: var(--color-text-secondary); color: var(--color-text-secondary);
text-wrap: pretty;
} }
} }

View File

@ -8,9 +8,9 @@
.menuButton { .menuButton {
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-end: 1px;
top: 0; top: 1px;
padding: 9px; padding: 7px;
&::before { &::before {
// Subtle divider line separating the button from the input field // Subtle divider line separating the button from the input field

View File

@ -8,7 +8,7 @@
.copyButton { .copyButton {
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-end: 1px;
top: 0; top: 1px;
padding: 9px; padding: 7px;
} }

View File

@ -1,3 +1,5 @@
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useParams } from 'react-router'; import { useParams } from 'react-router';
@ -5,17 +7,21 @@ import { Link } from 'react-router-dom';
import ElephantDarkImage from '@/images/elephant_ui_dark.svg?react'; import ElephantDarkImage from '@/images/elephant_ui_dark.svg?react';
import ElephantLightImage from '@/images/elephant_ui_light.svg?react'; import ElephantLightImage from '@/images/elephant_ui_light.svg?react';
import { openModal } from '@/mastodon/actions/modal';
import { Button } from '@/mastodon/components/button';
import { EmptyState } from '@/mastodon/components/empty_state'; import { EmptyState } from '@/mastodon/components/empty_state';
import { LimitedAccountHint } from '@/mastodon/features/account_timeline/components/limited_account_hint'; import { LimitedAccountHint } from '@/mastodon/features/account_timeline/components/limited_account_hint';
import { areCollectionsEnabled } from '@/mastodon/features/collections/utils'; import { areCollectionsEnabled } from '@/mastodon/features/collections/utils';
import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId'; import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId';
import { useTheme } from '@/mastodon/hooks/useTheme'; import { useTheme } from '@/mastodon/hooks/useTheme';
import { useAppDispatch } from '@/mastodon/store';
interface EmptyMessageProps { interface EmptyMessageProps {
suspended: boolean; suspended: boolean;
hidden: boolean; hidden: boolean;
blockedBy: boolean; blockedBy: boolean;
accountId?: string; accountId?: string;
withoutAddCollectionButton?: boolean;
} }
export const EmptyMessage: React.FC<EmptyMessageProps> = ({ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
@ -23,6 +29,7 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
suspended, suspended,
hidden, hidden,
blockedBy, blockedBy,
withoutAddCollectionButton,
}) => { }) => {
const { acct } = useParams<{ acct?: string }>(); const { acct } = useParams<{ acct?: string }>();
const me = useCurrentAccountId(); const me = useCurrentAccountId();
@ -30,6 +37,17 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
const ElephantImage = const ElephantImage =
theme === 'dark' ? ElephantDarkImage : ElephantLightImage; theme === 'dark' ? ElephantDarkImage : ElephantLightImage;
const dispatch = useAppDispatch();
const confirmHideFeaturedTab = useCallback(() => {
void dispatch(
openModal({
modalType: 'ACCOUNT_HIDE_FEATURED_TAB',
modalProps: {},
}),
);
}, [dispatch]);
if (!accountId) { if (!accountId) {
return null; return null;
} }
@ -49,17 +67,31 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
image={image} image={image}
title={ title={
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.no_collections' id='empty_column.account_featured_self.showcase_accounts'
defaultMessage='No collections yet' defaultMessage='Showcase your favorite accounts'
/>
}
message={
<FormattedMessage
id='empty_column.account_featured_self.showcase_accounts_desc'
defaultMessage='Collections are curated lists of accounts to help others discover more of the Fediverse.'
/> />
} }
> >
{!withoutAddCollectionButton && (
<Link to='/collections/new' className='button'> <Link to='/collections/new' className='button'>
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.no_collections_button' id='empty_column.account_featured_self.no_collections_button'
defaultMessage='Create a collection' defaultMessage='Create a collection'
/> />
</Link> </Link>
)}
<Button secondary onClick={confirmHideFeaturedTab}>
<FormattedMessage
id='empty_column.account_featured_self.no_collections_hide_tab'
defaultMessage='Hide this tab instead'
/>
</Button>
</EmptyState> </EmptyState>
); );
} else { } else {
@ -72,7 +104,7 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
message = ( message = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_self.pre_collections_desc' id='empty_column.account_featured_self.pre_collections_desc'
defaultMessage='Collections (coming in Mastodon 4.6) allows you to create your own curated lists of accounts to recommend to others.' defaultMessage='Collections (coming in Mastodon 4.6) allow you to create your own curated lists of accounts to recommend to others.'
/> />
); );
} }
@ -93,49 +125,23 @@ export const EmptyMessage: React.FC<EmptyMessageProps> = ({
/> />
); );
} else { } else {
// Standard other account empty state. if (acct) {
title = ( title = (
<FormattedMessage
id='empty_column.account_featured_other.title'
defaultMessage='Nothing to see here'
/>
);
if (hasCollections) {
if (acct) {
message = (
<FormattedMessage
id='empty_column.account_featured_other.no_collections_desc'
defaultMessage='{acct} hasnt created any collections yet.'
values={{ acct }}
/>
);
} else {
message = (
<FormattedMessage
id='empty_column.account_featured_unknown.no_collections_desc'
defaultMessage='This account hasnt created any collections yet.'
/>
);
}
} else {
if (acct) {
message = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured.other' id='empty_column.account_featured.other'
defaultMessage='{acct} hasnt featured anything yet.' defaultMessage='{acct} has not featured anything yet.'
values={{ acct }} values={{ acct }}
/> />
); );
} else { } else {
message = ( title = (
<FormattedMessage <FormattedMessage
id='empty_column.account_featured_unknown.other' id='empty_column.account_featured_unknown.other'
defaultMessage='This account hasnt featured anything yet.' defaultMessage='This account has not featured anything yet.'
/> />
); );
} }
} }
}
return <EmptyState title={title} message={message} image={image} />; return <EmptyState title={title} message={message} image={image} />;
}; };

View File

@ -0,0 +1,37 @@
.subheading {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 52px;
padding: 12px 16px;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
color: var(--color-text-primary);
background: var(--color-bg-secondary);
}
.link {
display: flex;
padding-block: 4px;
font-size: 13px;
font-weight: 500;
text-transform: none;
text-decoration: none;
color: inherit;
&:hover {
text-decoration: underline;
}
&:focus-visible {
outline: var(--outline-focus-default);
outline-offset: 2px;
}
:global(.icon) {
width: 18px;
height: 18px;
}
}

View File

@ -0,0 +1,42 @@
import type { ComponentPropsWithoutRef } from 'react';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import type { IconProp } from '@/mastodon/components/icon';
import { Icon } from '@/mastodon/components/icon';
import { polymorphicForwardRef } from '@/types/polymorphic';
import classes from './subheading.module.scss';
export const Subheading = polymorphicForwardRef<'h2'>(
({ as: Component = 'h2', children, className, ...props }, ref) => {
return (
<Component
ref={ref}
className={classNames(classes.subheading, className)}
{...props}
>
{children}
</Component>
);
},
);
interface SubheadingLinkProps extends ComponentPropsWithoutRef<typeof Link> {
icon: IconProp;
}
export const SubheadingLink: React.FC<SubheadingLinkProps> = ({
icon,
children,
className,
...props
}) => {
return (
<Link className={classNames(classes.link, className)} {...props}>
<Icon id='subheading-icon' icon={icon} />
{children}
</Link>
);
};

View File

@ -6,9 +6,10 @@ import { useHistory } from 'react-router';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import { AccountListItem } from '@/mastodon/components/account_list_item';
import { useAccount } from '@/mastodon/hooks/useAccount'; import { useAccount } from '@/mastodon/hooks/useAccount';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
import { fetchEndorsedAccounts } from 'mastodon/actions/accounts'; import { fetchEndorsedAccounts } from 'mastodon/actions/accounts';
import { Account } from 'mastodon/components/account';
import { ColumnBackButton } from 'mastodon/components/column_back_button'; import { ColumnBackButton } from 'mastodon/components/column_back_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { RemoteHint } from 'mastodon/components/remote_hint'; import { RemoteHint } from 'mastodon/components/remote_hint';
@ -28,10 +29,13 @@ import {
} from 'mastodon/reducers/slices/collections'; } from 'mastodon/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { CollectionListItem } from '../collections/detail/collection_list_item'; import { CollectionListItem } from '../collections/components/collection_list_item';
import { areCollectionsEnabled } from '../collections/utils'; import { areCollectionsEnabled } from '../collections/utils';
import { EmptyMessage } from './components/empty_message'; import { EmptyMessage } from './components/empty_message';
import { Subheading, SubheadingLink } from './components/subheading';
const collectionsEnabled = areCollectionsEnabled();
const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
multiColumn, multiColumn,
@ -54,13 +58,12 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
if (accountId) { if (accountId) {
void dispatch(fetchEndorsedAccounts({ accountId })); void dispatch(fetchEndorsedAccounts({ accountId }));
if (areCollectionsEnabled()) { if (collectionsEnabled) {
void dispatch(fetchAccountCollections({ accountId })); void dispatch(fetchAccountCollections({ accountId }));
} }
} }
}, [accountId, dispatch]); }, [accountId, dispatch]);
const isLoading = !accountId;
const featuredAccountIds = useAppSelector( const featuredAccountIds = useAppSelector(
(state) => (state) =>
state.user_lists.getIn( state.user_lists.getIn(
@ -68,8 +71,8 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
ImmutableList(), ImmutableList(),
) as ImmutableList<string>, ) as ImmutableList<string>,
); );
const { collections, status } = useAppSelector((state) => const { collections, status: collectionsLoadStatus } = useAppSelector(
selectAccountCollections(state, accountId ?? null), (state) => selectAccountCollections(state, accountId ?? null),
); );
const listedCollections = collections.filter( const listedCollections = collections.filter(
// Hide unlisted and empty collections to avoid confusion // Hide unlisted and empty collections to avoid confusion
@ -78,6 +81,15 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
(item) => item.discoverable && !!item.item_count, (item) => item.discoverable && !!item.item_count,
); );
const hasCollections =
collectionsEnabled &&
collectionsLoadStatus === 'idle' &&
listedCollections.length > 0;
const hasFeaturedAccounts = !featuredAccountIds.isEmpty();
const isLoading = !accountId || collectionsLoadStatus !== 'idle';
if (accountId === null) { if (accountId === null) {
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />; return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
} }
@ -92,7 +104,7 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
); );
} }
if (featuredAccountIds.isEmpty() && listedCollections.length === 0) { if (!hasFeaturedAccounts && !hasCollections) {
return ( return (
<AccountFeaturedWrapper accountId={accountId}> <AccountFeaturedWrapper accountId={accountId}>
<EmptyMessage <EmptyMessage
@ -114,14 +126,45 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
{accountId && ( {accountId && (
<AccountHeader accountId={accountId} hideTabs={forceEmptyState} /> <AccountHeader accountId={accountId} hideTabs={forceEmptyState} />
)} )}
{listedCollections.length > 0 && status === 'idle' && ( {!featuredAccountIds.isEmpty() && (
<> <>
<h4 className='column-subheading'> <Subheading as='h2'>
<FormattedMessage
id='account.featured.accounts'
defaultMessage='Profiles'
/>
</Subheading>
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<AccountListItem accountId={featuredAccountId} />
</Article>
))}
</ItemList>
</>
)}
{collectionsEnabled && (
<>
<Subheading as='header'>
<h2>
<FormattedMessage <FormattedMessage
id='account.featured.collections' id='account.featured.collections'
defaultMessage='Collections' defaultMessage='Collections'
/> />
</h4> </h2>
<SubheadingLink to='/collections/new' icon={AddIcon}>
<FormattedMessage
id='account.featured.new_collection'
defaultMessage='New collection'
/>
</SubheadingLink>
</Subheading>
{hasCollections ? (
<ItemList> <ItemList>
{listedCollections.map((item, index) => ( {listedCollections.map((item, index) => (
<CollectionListItem <CollectionListItem
@ -134,28 +177,15 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
/> />
))} ))}
</ItemList> </ItemList>
</> ) : (
)} <EmptyMessage
{!featuredAccountIds.isEmpty() && ( withoutAddCollectionButton
<> blockedBy={blockedBy}
<h4 className='column-subheading'> hidden={hidden}
<FormattedMessage suspended={suspended}
id='account.featured.accounts' accountId={accountId}
defaultMessage='Profiles'
/> />
</h4> )}
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<Account id={featuredAccountId} />
</Article>
))}
</ItemList>
</> </>
)} )}
<RemoteHint accountId={accountId} /> <RemoteHint accountId={accountId} />

View File

@ -8,8 +8,6 @@ import { NavLink } from 'react-router-dom';
import { useAccount } from '@/mastodon/hooks/useAccount'; import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAccountId } from '@/mastodon/hooks/useAccountId'; import { useAccountId } from '@/mastodon/hooks/useAccountId';
import { areCollectionsEnabled } from '../../collections/utils';
import classes from './styles.module.scss'; import classes from './styles.module.scss';
const isActive: Required<NavLinkProps>['isActive'] = (match, location) => const isActive: Required<NavLinkProps>['isActive'] = (match, location) =>
@ -41,14 +39,7 @@ export const AccountTabs: FC = () => {
)} )}
{show_featured && ( {show_featured && (
<NavLink exact to={`/@${acct}/featured`}> <NavLink exact to={`/@${acct}/featured`}>
{areCollectionsEnabled() ? (
<FormattedMessage
id='account.featured.collections'
defaultMessage='Collections'
/>
) : (
<FormattedMessage id='account.featured' defaultMessage='Featured' /> <FormattedMessage id='account.featured' defaultMessage='Featured' />
)}
</NavLink> </NavLink>
)} )}
</div> </div>

View File

@ -0,0 +1,20 @@
.wrapper {
display: flex;
align-items: start;
padding: 12px 16px;
gap: 16px;
&:not(.wrapperWithoutBorder) {
border-bottom: 1px solid var(--color-border-primary);
}
}
.menuButton {
padding: 4px;
margin-top: -2px;
svg {
width: 20px;
height: 20px;
}
}

View File

@ -0,0 +1,50 @@
import { useId } from 'react';
import classNames from 'classnames';
import { Article } from 'mastodon/components/scrollable_list/components';
import type { CollectionLockupProps } from 'mastodon/features/collections/components/collection_lockup';
import { CollectionLockup } from 'mastodon/features/collections/components/collection_lockup';
import { CollectionMenu } from 'mastodon/features/collections/components/collection_menu';
import classes from './collection_list_item.module.scss';
interface CollectionListItemProps extends CollectionLockupProps {
withoutBorder?: boolean;
positionInList: number;
listSize: number;
}
export const CollectionListItem: React.FC<CollectionListItemProps> = ({
collection,
withoutBorder,
positionInList,
listSize,
...otherProps
}) => {
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
return (
<Article
focusable
className={classNames(
classes.wrapper,
withoutBorder && classes.wrapperWithoutBorder,
)}
aria-labelledby={linkId}
aria-describedby={infoId}
aria-posinset={positionInList}
aria-setsize={listSize}
>
<CollectionLockup collection={collection} {...otherProps} />
<CollectionMenu
context='list'
collection={collection}
className={classes.menuButton}
/>
</Article>
);
};

View File

@ -1,21 +1,11 @@
.wrapper {
display: flex;
align-items: start;
margin-inline: 24px;
padding-block: 12px;
gap: 16px;
&:not(.wrapperWithoutBorder) {
border-bottom: 1px solid var(--color-border-primary);
}
}
.content { .content {
position: relative; position: relative;
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
align-items: center; align-items: center;
column-gap: 12px; column-gap: 12px;
color: var(--color-text-primary);
line-height: 1.3;
} }
.avatarGrid { .avatarGrid {
@ -70,13 +60,3 @@
font-size: 13px; font-size: 13px;
color: var(--color-text-secondary); color: var(--color-text-secondary);
} }
.menuButton {
padding: 4px;
margin-top: -2px;
svg {
width: 20px;
height: 20px;
}
}

View File

@ -0,0 +1,115 @@
import { useId } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { AvatarById } from 'mastodon/components/avatar';
import { useAccountHandle } from 'mastodon/components/display_name/default';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { useAccount } from 'mastodon/hooks/useAccount';
import { domain } from 'mastodon/initial_state';
import classes from './collection_lockup.module.scss';
export const AvatarGrid: React.FC<{
accountIds: (string | undefined)[];
sensitive?: boolean;
}> = ({ accountIds: ids, sensitive }) => {
const avatarIds = [ids[0], ids[1], ids[2], ids[3]];
return (
<div
className={classNames(
classes.avatarGrid,
sensitive ? classes.avatarGridSensitive : null,
)}
>
{avatarIds.map((id) => (
<AvatarById
animate={false}
key={id}
accountId={id}
className={classes.avatar}
size={25}
/>
))}
{sensitive && <WarningIcon className={classes.avatarSensitiveBadge} />}
</div>
);
};
export interface CollectionLockupProps {
collection: ApiCollectionJSON;
withAuthorHandle?: boolean;
withTimestamp?: boolean;
}
export const CollectionLockup: React.FC<CollectionLockupProps> = ({
collection,
withAuthorHandle = true,
withTimestamp,
}) => {
const { id, name } = collection;
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
const authorAccount = useAccount(collection.account_id);
const authorHandle = useAccountHandle(authorAccount, domain);
return (
<div className={classes.content}>
<AvatarGrid
accountIds={collection.items.map((item) => item.account_id)}
sensitive={collection.sensitive}
/>
<div>
<h2 id={linkId}>
<Link to={`/collections/${id}`} className={classes.link}>
{name}
</Link>
</h2>
<ul className={classes.info} id={infoId}>
{collection.sensitive && (
<li className='sr-only'>
<FormattedMessage
id='collections.sensitive'
defaultMessage='Sensitive'
/>
</li>
)}
{withAuthorHandle && authorAccount && (
<FormattedMessage
id='collections.by_account'
defaultMessage='by {account_handle}'
values={{
account_handle: authorHandle,
}}
tagName='li'
/>
)}
<FormattedMessage
id='collections.account_count'
defaultMessage='{count, plural, one {# account} other {# accounts}}'
values={{ count: collection.item_count }}
tagName='li'
/>
{withTimestamp && (
<FormattedMessage
id='collections.last_updated_at'
defaultMessage='Last updated: {date}'
values={{
date: (
<RelativeTimestamp timestamp={collection.updated_at} long />
),
}}
tagName='li'
/>
)}
</ul>
</div>
</div>
);
};

View File

@ -0,0 +1,8 @@
.wrapper {
display: flex;
align-items: start;
padding: 12px;
gap: 12px;
border-radius: 12px;
border: 1px solid var(--color-border-primary);
}

View File

@ -0,0 +1,20 @@
import type { CollectionLockupProps } from 'mastodon/features/collections/components/collection_lockup';
import { CollectionLockup } from 'mastodon/features/collections/components/collection_lockup';
import classes from './collection_preview_card.module.scss';
interface CollectionPreviewCardProps extends CollectionLockupProps {
onRemove?: () => void;
}
export const CollectionPreviewCard: React.FC<CollectionPreviewCardProps> = ({
collection,
onRemove,
...otherProps
}) => {
return (
<div className={classes.wrapper}>
<CollectionLockup collection={collection} {...otherProps} />
</div>
);
};

View File

@ -5,27 +5,7 @@
} }
.preview { .preview {
display: flex;
flex-wrap: wrap-reverse;
align-items: start;
justify-content: space-between;
gap: 8px;
padding: 16px;
margin-bottom: 16px; margin-bottom: 16px;
border-radius: 8px;
color: var(--color-text-primary);
background: linear-gradient(
145deg,
var(--color-bg-brand-soft),
var(--color-bg-primary)
);
border: 1px solid var(--color-bg-brand-base);
}
.previewHeading {
font-size: 22px;
line-height: 1.3;
margin-bottom: 4px;
} }
.actions { .actions {

View File

@ -8,8 +8,6 @@ import { me } from '@/mastodon/initial_state';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { changeCompose, focusCompose } from 'mastodon/actions/compose'; import { changeCompose, focusCompose } from 'mastodon/actions/compose';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections'; import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { AvatarById } from 'mastodon/components/avatar';
import { AvatarGroup } from 'mastodon/components/avatar_group';
import { Button } from 'mastodon/components/button'; import { Button } from 'mastodon/components/button';
import { CopyLinkField } from 'mastodon/components/form_fields'; import { CopyLinkField } from 'mastodon/components/form_fields';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
@ -20,7 +18,7 @@ import {
} from 'mastodon/components/modal_shell'; } from 'mastodon/components/modal_shell';
import { useAppDispatch } from 'mastodon/store'; import { useAppDispatch } from 'mastodon/store';
import { AuthorNote } from '.'; import { CollectionPreviewCard } from './collection_preview_card';
import classes from './share_modal.module.scss'; import classes from './share_modal.module.scss';
const messages = defineMessages({ const messages = defineMessages({
@ -95,24 +93,13 @@ export const CollectionShareModal: React.FC<{
/> />
<div className={classes.preview}> <div className={classes.preview}>
<div> <CollectionPreviewCard collection={collection} />
<h2 className={classes.previewHeading}>{collection.name}</h2>
<AuthorNote id={collection.account_id} />
</div>
<AvatarGroup>
{collection.items.slice(0, 5).map(({ account_id }) => {
if (!account_id) return;
return (
<AvatarById key={account_id} accountId={account_id} size={28} />
);
})}
</AvatarGroup>
</div> </div>
<CopyLinkField <CopyLinkField
label={intl.formatMessage({ label={intl.formatMessage({
id: 'collection.share_modal.share_link_label', id: 'collection.share_modal.share_link_label',
defaultMessage: 'Invite share link', defaultMessage: 'Share link',
})} })}
value={collectionLink} value={collectionLink}
/> />

View File

@ -146,7 +146,6 @@ export const CollectionAccountsList: React.FC<{
<AccountListItem <AccountListItem
accountId={account_id} accountId={account_id}
withBorder={index !== items.length - 1} withBorder={index !== items.length - 1}
stats={['followers', 'last-active']}
renderButton={renderAccountItemButton} renderButton={renderAccountItemButton}
/> />
</Article> </Article>

View File

@ -1,139 +0,0 @@
import { useId } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
import { AvatarById } from 'mastodon/components/avatar';
import { useAccountHandle } from 'mastodon/components/display_name/default';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { Article } from 'mastodon/components/scrollable_list/components';
import { useAccount } from 'mastodon/hooks/useAccount';
import { domain } from 'mastodon/initial_state';
import classes from './collection_list_item.module.scss';
import { CollectionMenu } from './collection_menu';
export const AvatarGrid: React.FC<{
accountIds: (string | undefined)[];
sensitive?: boolean;
}> = ({ accountIds: ids, sensitive }) => {
const avatarIds = [ids[0], ids[1], ids[2], ids[3]];
return (
<div
className={classNames(
classes.avatarGrid,
sensitive ? classes.avatarGridSensitive : null,
)}
>
{avatarIds.map((id) => (
<AvatarById
animate={false}
key={id}
accountId={id}
className={classes.avatar}
size={25}
/>
))}
{sensitive && <WarningIcon className={classes.avatarSensitiveBadge} />}
</div>
);
};
export const CollectionListItem: React.FC<{
collection: ApiCollectionJSON;
withoutBorder?: boolean;
withAuthorHandle?: boolean;
withTimestamp?: boolean;
positionInList: number;
listSize: number;
}> = ({
collection,
withoutBorder,
withAuthorHandle = true,
withTimestamp,
positionInList,
listSize,
}) => {
const { id, name } = collection;
const uniqueId = useId();
const linkId = `${uniqueId}-link`;
const infoId = `${uniqueId}-info`;
const authorAccount = useAccount(collection.account_id);
const authorHandle = useAccountHandle(authorAccount, domain);
return (
<Article
focusable
className={classNames(
classes.wrapper,
withoutBorder && classes.wrapperWithoutBorder,
)}
aria-labelledby={linkId}
aria-describedby={infoId}
aria-posinset={positionInList}
aria-setsize={listSize}
>
<div className={classes.content}>
<AvatarGrid
accountIds={collection.items.map((item) => item.account_id)}
sensitive={collection.sensitive}
/>
<div>
<h2 id={linkId}>
<Link to={`/collections/${id}`} className={classes.link}>
{name}
</Link>
</h2>
<ul className={classes.info} id={infoId}>
{collection.sensitive && (
<li className='sr-only'>
<FormattedMessage
id='collections.sensitive'
defaultMessage='Sensitive'
/>
</li>
)}
{withAuthorHandle && authorAccount && (
<FormattedMessage
id='collections.by_account'
defaultMessage='by {account_handle}'
values={{
account_handle: authorHandle,
}}
tagName='li'
/>
)}
<FormattedMessage
id='collections.account_count'
defaultMessage='{count, plural, one {# account} other {# accounts}}'
values={{ count: collection.item_count }}
tagName='li'
/>
{withTimestamp && (
<FormattedMessage
id='collections.last_updated_at'
defaultMessage='Last updated: {date}'
values={{
date: (
<RelativeTimestamp timestamp={collection.updated_at} long />
),
}}
tagName='li'
/>
)}
</ul>
</div>
</div>
<CollectionMenu
context='list'
collection={collection}
className={classes.menuButton}
/>
</Article>
);
};

View File

@ -23,8 +23,9 @@ import { domain, me } from 'mastodon/initial_state';
import { fetchCollection } from 'mastodon/reducers/slices/collections'; import { fetchCollection } from 'mastodon/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { CollectionMenu } from '../components/collection_menu';
import { CollectionAccountsList } from './accounts_list'; import { CollectionAccountsList } from './accounts_list';
import { CollectionMenu } from './collection_menu';
import { useConfirmRevoke } from './revoke_collection_inclusion_modal'; import { useConfirmRevoke } from './revoke_collection_inclusion_modal';
import classes from './styles.module.scss'; import classes from './styles.module.scss';
@ -66,7 +67,7 @@ export const AuthorNote: React.FC<{ id: string }> = ({ id }) => {
); );
}; };
export const RevokeControls: React.FC<{ const RevokeControls: React.FC<{
collection: ApiCollectionJSON; collection: ApiCollectionJSON;
}> = ({ collection }) => { }> = ({ collection }) => {
const authorAccount = useAccount(collection.account_id); const authorAccount = useAccount(collection.account_id);

View File

@ -61,9 +61,11 @@ const AddedAccountItem: React.FC<{
onRemove(accountId); onRemove(accountId);
}, [accountId, onRemove]); }, [accountId, onRemove]);
const lastStatusAt = account?.last_status_at;
const lastPostHint = useMemo( const lastPostHint = useMemo(
() => () =>
isOlderThanAWeek(account?.last_status_at) && ( (!lastStatusAt || isOlderThanAWeek(lastStatusAt)) && (
<Badge <Badge
label={ label={
<FormattedMessage <FormattedMessage
@ -75,7 +77,7 @@ const AddedAccountItem: React.FC<{
className={classes.accountBadge} className={classes.accountBadge}
/> />
), ),
[account?.last_status_at], [lastStatusAt],
); );
return ( return (

View File

@ -10,22 +10,29 @@ import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?r
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react'; import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { Column } from 'mastodon/components/column'; import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header'; import { ColumnHeader } from 'mastodon/components/column_header';
import { DisplayNameSimple } from 'mastodon/components/display_name/simple';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { import {
ItemList, ItemList,
Scrollable, Scrollable,
} from 'mastodon/components/scrollable_list/components'; } from 'mastodon/components/scrollable_list/components';
import { useAccount } from 'mastodon/hooks/useAccount';
import { useAccountId, useCurrentAccountId } from 'mastodon/hooks/useAccountId';
import { import {
fetchAccountCollections, fetchAccountCollections,
selectAccountCollections, selectAccountCollections,
} from 'mastodon/reducers/slices/collections'; } from 'mastodon/reducers/slices/collections';
import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { CollectionListItem } from './detail/collection_list_item'; import { CollectionListItem } from './components/collection_list_item';
import { messages as editorMessages } from './editor'; import { messages as editorMessages } from './editor';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.collections', defaultMessage: 'My collections' }, headingMe: { id: 'column.my_collections', defaultMessage: 'My collections' },
headingOther: {
id: 'column.other_collections',
defaultMessage: 'Collections by {name}',
},
}); });
export const Collections: React.FC<{ export const Collections: React.FC<{
@ -33,17 +40,22 @@ export const Collections: React.FC<{
}> = ({ multiColumn }) => { }> = ({ multiColumn }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const intl = useIntl(); const intl = useIntl();
const me = useAppSelector((state) => state.meta.get('me') as string); const me = useCurrentAccountId();
const accountId = useAccountId();
const account = useAccount(accountId);
const { collections, status } = useAppSelector((state) => const { collections, status } = useAppSelector((state) =>
selectAccountCollections(state, me), selectAccountCollections(state, accountId),
); );
useEffect(() => { useEffect(() => {
void dispatch(fetchAccountCollections({ accountId: me })); if (accountId) {
}, [dispatch, me]); void dispatch(fetchAccountCollections({ accountId }));
}
}, [dispatch, accountId]);
const emptyMessage = const emptyMessage =
status === 'error' ? ( status === 'error' || !accountId ? (
<FormattedMessage <FormattedMessage
id='collections.error_loading_collections' id='collections.error_loading_collections'
defaultMessage='There was an error when trying to load your collections.' defaultMessage='There was an error when trying to load your collections.'
@ -67,17 +79,27 @@ export const Collections: React.FC<{
</> </>
); );
const isOwnCollection = accountId === me;
const titleMessage = isOwnCollection
? messages.headingMe
: messages.headingOther;
const pageTitle = intl.formatMessage(titleMessage, {
name: account?.get('display_name'),
});
const pageTitleHtml = intl.formatMessage(titleMessage, {
name: <DisplayNameSimple account={account} />,
});
return ( return (
<Column <Column bindToDocument={!multiColumn} label={pageTitle}>
bindToDocument={!multiColumn}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader <ColumnHeader
title={intl.formatMessage(messages.heading)} title={pageTitleHtml}
icon='collections' icon='collections'
iconComponent={CollectionsFilledIcon} iconComponent={CollectionsFilledIcon}
multiColumn={multiColumn} multiColumn={multiColumn}
extraButton={ extraButton={
isOwnCollection && (
<Link <Link
to='/collections/new' to='/collections/new'
className='column-header__button' className='column-header__button'
@ -86,6 +108,7 @@ export const Collections: React.FC<{
> >
<Icon id='plus' icon={AddIcon} /> <Icon id='plus' icon={AddIcon} />
</Link> </Link>
)
} }
/> />
@ -105,7 +128,7 @@ export const Collections: React.FC<{
</Scrollable> </Scrollable>
<Helmet> <Helmet>
<title>{intl.formatMessage(messages.heading)}</title> <title>{pageTitle}</title>
<meta name='robots' content='noindex' /> <meta name='robots' content='noindex' />
</Helmet> </Helmet>
</Column> </Column>

View File

@ -53,12 +53,20 @@ export const AccountList: FC<AccountListProps> = ({
} }
const children = const children =
list?.items.map((followerId) => ( list?.items.map((followerId) => (
<AccountListItem key={followerId} accountId={followerId} /> <AccountListItem
key={followerId}
accountId={followerId}
withBio={false}
/>
)) ?? []; )) ?? [];
if (prependAccountId) { if (prependAccountId) {
children.unshift( children.unshift(
<AccountListItem key={prependAccountId} accountId={prependAccountId} />, <AccountListItem
key={prependAccountId}
accountId={prependAccountId}
withBio={false}
/>,
); );
} }
return children; return children;

View File

@ -10,6 +10,7 @@ import type { Map as ImmutableMap } from 'immutable';
import { animated, useSpring } from '@react-spring/web'; import { animated, useSpring } from '@react-spring/web';
import { useDrag } from '@use-gesture/react'; import { useDrag } from '@use-gesture/react';
import { useAccount } from '@/mastodon/hooks/useAccount';
import AddIcon from '@/material-icons/400-24px/add.svg?react'; import AddIcon from '@/material-icons/400-24px/add.svg?react';
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
@ -212,6 +213,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
const { signedIn, permissions, disabledAccountId } = useIdentity(); const { signedIn, permissions, disabledAccountId } = useIdentity();
const location = useLocation(); const location = useLocation();
const showSearch = useBreakpoint('full') && !multiColumn; const showSearch = useBreakpoint('full') && !multiColumn;
const account = useAccount(me);
let banner: React.ReactNode; let banner: React.ReactNode;
@ -335,7 +337,7 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
{areCollectionsEnabled() && ( {areCollectionsEnabled() && (
<ColumnLink <ColumnLink
transparent transparent
to='/collections' to={`/@${account?.acct}/collections`}
icon='collections' icon='collections'
iconComponent={CollectionsIcon} iconComponent={CollectionsIcon}
activeIconComponent={CollectionsActiveIcon} activeIconComponent={CollectionsActiveIcon}

View File

@ -0,0 +1,69 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { DisplayNameSimple } from '@/mastodon/components/display_name/simple';
import { Icon } from '@/mastodon/components/icon';
import { useAccount } from '@/mastodon/hooks/useAccount';
import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?react';
import type {
NotificationGroupAddedToCollection,
NotificationGroupCollectionUpdate,
} from 'mastodon/models/notification_group';
import { CollectionPreviewCard } from '../../collections/components/collection_preview_card';
export const NotificationCollection: React.FC<{
notification:
| NotificationGroupAddedToCollection
| NotificationGroupCollectionUpdate;
unread: boolean;
}> = ({ notification, unread }) => {
const { collection, type } = notification;
const collectionCreatorAccount = useAccount(collection.account_id);
return (
<div
className={classNames(
'notification-group',
`notification-group--${type}`,
{ 'notification-group--unread': unread },
)}
>
<div className='notification-group__icon'>
<Icon id='collection' icon={CollectionsFilledIcon} />
</div>
<div className='notification-group__main'>
<div className='notification-group__main__header'>
<div className='notification-group__main__header__label'>
{type === 'added_to_collection' && (
<FormattedMessage
id='notification.added_to_collection'
defaultMessage='{name} added you to a collection'
values={{
name: (
<DisplayNameSimple account={collectionCreatorAccount} />
),
}}
/>
)}
{type === 'collection_update' && (
<FormattedMessage
id='notification.collection_update'
defaultMessage='{name} edited a collection youre in'
values={{
name: (
<DisplayNameSimple account={collectionCreatorAccount} />
),
}}
/>
)}
</div>
</div>
<CollectionPreviewCard collection={collection} />
</div>
</div>
);
};

View File

@ -9,6 +9,7 @@ import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { NotificationAdminReport } from './notification_admin_report'; import { NotificationAdminReport } from './notification_admin_report';
import { NotificationAdminSignUp } from './notification_admin_sign_up'; import { NotificationAdminSignUp } from './notification_admin_sign_up';
import { NotificationAnnualReport } from './notification_annual_report'; import { NotificationAnnualReport } from './notification_annual_report';
import { NotificationCollection } from './notification_collection';
import { NotificationFavourite } from './notification_favourite'; import { NotificationFavourite } from './notification_favourite';
import { NotificationFollow } from './notification_follow'; import { NotificationFollow } from './notification_follow';
import { NotificationFollowRequest } from './notification_follow_request'; import { NotificationFollowRequest } from './notification_follow_request';
@ -156,6 +157,15 @@ export const NotificationGroup: React.FC<{
/> />
); );
break; break;
case 'added_to_collection':
case 'collection_update':
content = (
<NotificationCollection
unread={unread}
notification={notificationGroup}
/>
);
break;
default: default:
return null; return null;
} }

View File

@ -1,15 +0,0 @@
import PropTypes from 'prop-types';
const ColumnSubheading = ({ text }) => {
return (
<div className='column-subheading'>
{text}
</div>
);
};
ColumnSubheading.propTypes = {
text: PropTypes.string.isRequired,
};
export default ColumnSubheading;

View File

@ -4,6 +4,8 @@ import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { me } from '@/mastodon/initial_state';
import { deleteCollection } from 'mastodon/reducers/slices/collections'; import { deleteCollection } from 'mastodon/reducers/slices/collections';
import { useAppDispatch } from 'mastodon/store'; import { useAppDispatch } from 'mastodon/store';
@ -34,11 +36,12 @@ export const ConfirmDeleteCollectionModal: React.FC<
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const history = useHistory(); const history = useHistory();
const { acct: currentUserName } = useAccount(me) ?? {};
const onConfirm = useCallback(() => { const onConfirm = useCallback(() => {
void dispatch(deleteCollection({ collectionId: id })); void dispatch(deleteCollection({ collectionId: id }));
history.push('/collections'); history.push(`/@${currentUserName}/collections`);
}, [dispatch, history, id]); }, [dispatch, history, id, currentUserName]);
return ( return (
<ConfirmationModal <ConfirmationModal

View File

@ -0,0 +1,73 @@
import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useHistory } from 'react-router';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId';
import { domain } from '@/mastodon/initial_state';
import { patchProfile } from '@/mastodon/reducers/slices/profile_edit';
import { useAppDispatch } from 'mastodon/store';
import type { BaseConfirmationModalProps } from './confirmation_modal';
import { ConfirmationModal } from './confirmation_modal';
const messages = defineMessages({
title: {
id: 'confirmations.hide_featured_tab.title',
defaultMessage: 'Hide "Featured" tab?',
},
intro: {
id: 'confirmations.hide_featured_tab.intro',
defaultMessage:
'You can change this at any time under <i>Edit profile > Profile tab settings</i>.',
},
message: {
id: 'confirmations.hide_featured_tab.message',
defaultMessage:
'This will hide the tab for users on {serverName} and other servers running the latest version of Mastodon. Other displays may vary.',
},
confirm: {
id: 'confirmations.hide_featured_tab.confirm',
defaultMessage: 'Hide tab',
},
});
export const ConfirmHideFeaturedTabModal: React.FC<
BaseConfirmationModalProps
> = ({ onClose }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const history = useHistory();
const currentAccountId = useCurrentAccountId();
const { acct: currentUserName } = useAccount(currentAccountId) ?? {};
const onConfirm = useCallback(() => {
void dispatch(patchProfile({ show_featured: false }));
history.push(`/@${currentUserName}`);
}, [currentUserName, dispatch, history]);
return (
<ConfirmationModal
title={intl.formatMessage(messages.title)}
extraContent={
<div className='prose'>
<p>
{intl.formatMessage(messages.intro, {
i: (words) => <i>{words}</i>,
})}
</p>
<p>
{intl.formatMessage(messages.message, {
serverName: domain,
})}
</p>
</div>
}
confirm={intl.formatMessage(messages.confirm)}
onConfirm={onConfirm}
onClose={onClose}
/>
);
};

View File

@ -79,7 +79,7 @@ export const MODAL_COMPONENTS = {
'DOMAIN_BLOCK': DomainBlockModal, 'DOMAIN_BLOCK': DomainBlockModal,
'REPORT': ReportModal, 'REPORT': ReportModal,
'REPORT_COLLECTION': ReportCollectionModal, 'REPORT_COLLECTION': ReportCollectionModal,
'SHARE_COLLECTION': () => import('@/mastodon/features/collections/detail/share_modal').then(module => ({ default: module.CollectionShareModal })), 'SHARE_COLLECTION': () => import('@/mastodon/features/collections/components/share_modal').then(module => ({ default: module.CollectionShareModal })),
'REVOKE_COLLECTION_INCLUSION': () => import('@/mastodon/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })), 'REVOKE_COLLECTION_INCLUSION': () => import('@/mastodon/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })),
'ACTIONS': () => Promise.resolve({ default: ActionsModal }), 'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
'EMBED': EmbedModal, 'EMBED': EmbedModal,
@ -105,6 +105,7 @@ export const MODAL_COMPONENTS = {
'ACCOUNT_EDIT_IMAGE_ALT': accountEditModal('ImageAltModal'), 'ACCOUNT_EDIT_IMAGE_ALT': accountEditModal('ImageAltModal'),
'ACCOUNT_EDIT_IMAGE_DELETE': accountEditModal('ImageDeleteModal'), 'ACCOUNT_EDIT_IMAGE_DELETE': accountEditModal('ImageDeleteModal'),
'ACCOUNT_EDIT_IMAGE_UPLOAD': accountEditModal('ImageUploadModal'), 'ACCOUNT_EDIT_IMAGE_UPLOAD': accountEditModal('ImageUploadModal'),
'ACCOUNT_HIDE_FEATURED_TAB': () => import('@/mastodon/features/ui/components/confirmation_modals/hide_featured_tab').then(module => ({ default: module.ConfirmHideFeaturedTabModal })),
}; };
/** @arg {keyof import('@/mastodon/features/account_edit/modals')} type */ /** @arg {keyof import('@/mastodon/features/account_edit/modals')} type */

View File

@ -232,6 +232,13 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} /> <WrappedRoute path={['/@:acct', '/accounts/:id']} exact component={AccountTimeline} content={children} />
<WrappedRoute path={['/@:acct/featured', '/accounts/:id/featured']} component={AccountFeatured} content={children} /> <WrappedRoute path={['/@:acct/featured', '/accounts/:id/featured']} component={AccountFeatured} content={children} />
{areCollectionsEnabled() &&
[
<WrappedRoute path={['/@:acct/collections']} component={Collections} content={children} key='collections-list' />,
<WrappedRoute path={['/collections/new', '/collections/:id/edit']} component={CollectionsEditor} content={children} key='collections-editor' />,
<WrappedRoute path='/collections/:id' component={CollectionDetail} content={children} key='collections-detail' />,
]
}
<WrappedRoute path='/@:acct/tagged/:tagged?' exact component={AccountTimeline} content={children} /> <WrappedRoute path='/@:acct/tagged/:tagged?' exact component={AccountTimeline} content={children} />
<WrappedRoute path={['/@:acct/with_replies', '/accounts/:id/with_replies']} component={AccountTimeline} content={children} componentParams={{ withReplies: true }} /> <WrappedRoute path={['/@:acct/with_replies', '/accounts/:id/with_replies']} component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
<WrappedRoute path={['/accounts/:id/followers', '/users/:acct/followers', '/@:acct/followers']} component={Followers} content={children} /> <WrappedRoute path={['/accounts/:id/followers', '/users/:acct/followers', '/@:acct/followers']} component={Followers} content={children} />
@ -255,13 +262,6 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path='/followed_tags' component={FollowedTags} content={children} /> <WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
<WrappedRoute path='/mutes' component={Mutes} content={children} /> <WrappedRoute path='/mutes' component={Mutes} content={children} />
<WrappedRoute path='/lists' component={Lists} content={children} /> <WrappedRoute path='/lists' component={Lists} content={children} />
{areCollectionsEnabled() &&
[
<WrappedRoute path={['/collections/new', '/collections/:id/edit']} component={CollectionsEditor} content={children} key='collections-editor' />,
<WrappedRoute path='/collections/:id' component={CollectionDetail} content={children} key='collections-detail' />,
<WrappedRoute path='/collections' component={Collections} content={children} key='collections-list' />
]
}
<Route component={BundleColumnError} /> <Route component={BundleColumnError} />
</WrappedSwitch> </WrappedSwitch>
</ColumnsArea> </ColumnsArea>

View File

@ -31,6 +31,7 @@
"account.disable_notifications": "توقف عن إشعاري عندما ينشر @{name}", "account.disable_notifications": "توقف عن إشعاري عندما ينشر @{name}",
"account.domain_blocking": "نطاق محظور", "account.domain_blocking": "نطاق محظور",
"account.edit_profile": "تعديل الملف الشخصي", "account.edit_profile": "تعديل الملف الشخصي",
"account.edit_profile_short": "تعديل",
"account.enable_notifications": "أشعرني عندما ينشر @{name}", "account.enable_notifications": "أشعرني عندما ينشر @{name}",
"account.endorse": "أوصِ به على صفحتك الشخصية", "account.endorse": "أوصِ به على صفحتك الشخصية",
"account.familiar_followers_many": "يتبعه {name1}، {name2} و{othersCount, plural, one {شخص آخر تعرفه} other {# أشخاص آخرون تعرفهم}}", "account.familiar_followers_many": "يتبعه {name1}، {name2} و{othersCount, plural, one {شخص آخر تعرفه} other {# أشخاص آخرون تعرفهم}}",
@ -97,6 +98,27 @@
"account.unmute": "إلغاء الكَتم عن @{name}", "account.unmute": "إلغاء الكَتم عن @{name}",
"account.unmute_notifications_short": "إلغاء كَتم الإشعارات", "account.unmute_notifications_short": "إلغاء كَتم الإشعارات",
"account.unmute_short": "إلغاء الكتم", "account.unmute_short": "إلغاء الكتم",
"account_edit.bio.add_label": "إضافة سيرة ذاتية",
"account_edit.bio_modal.add_title": "إضافة سيرة ذاتية",
"account_edit.custom_fields.add_label": "إضافة حقل",
"account_edit.field_actions.edit": "تعديل الحقل",
"account_edit.field_delete_modal.delete_button": "حذف",
"account_edit.field_edit_modal.value_label": "قيمة",
"account_edit.image_alt_modal.text_label": "نص بديل",
"account_edit.image_delete_modal.delete_button": "حذف",
"account_edit.image_edit.add_button": "إضافة صورة",
"account_edit.image_edit.alt_add_button": "إضافة نص بديل",
"account_edit.save": "حفظ",
"account_edit.upload_modal.back": "العودة",
"account_edit.upload_modal.done": "تمّ",
"account_edit.upload_modal.next": "التالي",
"account_edit.upload_modal.step_upload.dragging": "إسقاط للتحميل",
"account_edit.upload_modal.step_upload.header": "اختيار صورة",
"account_edit.upload_modal.title_add.avatar": "إضافة صورة الملف الشخصي",
"account_edit.upload_modal.title_add.header": "إضافة صورة الغلاف",
"account_edit.upload_modal.title_replace.avatar": "استبدال صورة الملف الشخصي",
"account_edit.upload_modal.title_replace.header": "استبدال صورة الغلاف",
"account_edit_tags.add_tag": "إضافة #{tagName}",
"account_note.placeholder": "اضغط لإضافة مُلاحظة", "account_note.placeholder": "اضغط لإضافة مُلاحظة",
"admin.dashboard.daily_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالأيام", "admin.dashboard.daily_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالأيام",
"admin.dashboard.monthly_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالشهور", "admin.dashboard.monthly_retention": "معدّل بقاء المستخدمين بعد إنشاء الحسابات، بالشهور",
@ -120,6 +142,9 @@
"alt_text_modal.describe_for_people_with_visual_impairments": "قم بوصفها للأشخاص ذوي الإعاقة البصرية…", "alt_text_modal.describe_for_people_with_visual_impairments": "قم بوصفها للأشخاص ذوي الإعاقة البصرية…",
"alt_text_modal.done": "تمّ", "alt_text_modal.done": "تمّ",
"announcement.announcement": "إعلان", "announcement.announcement": "إعلان",
"annual_report.announcement.action_dismiss": "لا شكراً",
"annual_report.nav_item.badge": "جديد",
"annual_report.shared_page.donate": "تبرع",
"annual_report.summary.archetype.replier.name": "الفراشة", "annual_report.summary.archetype.replier.name": "الفراشة",
"annual_report.summary.close": "اغلق", "annual_report.summary.close": "اغلق",
"annual_report.summary.copy_link": "نسخ الرابط", "annual_report.summary.copy_link": "نسخ الرابط",
@ -162,11 +187,15 @@
"closed_registrations_modal.preamble": "ماستدون لامركزي، لذلك بغض النظر عن مكان إنشاء حسابك، سيكون بإمكانك المتابعة والتفاعل مع أي شخص على هذا الخادم. يمكنك حتى أن تستضيفه ذاتياً!", "closed_registrations_modal.preamble": "ماستدون لامركزي، لذلك بغض النظر عن مكان إنشاء حسابك، سيكون بإمكانك المتابعة والتفاعل مع أي شخص على هذا الخادم. يمكنك حتى أن تستضيفه ذاتياً!",
"closed_registrations_modal.title": "إنشاء حساب على ماستدون", "closed_registrations_modal.title": "إنشاء حساب على ماستدون",
"collections.collection_description": "الوصف", "collections.collection_description": "الوصف",
"collections.collection_language": "اللغة",
"collections.collection_name": "الاسم", "collections.collection_name": "الاسم",
"collections.collection_topic": "الموضوع", "collections.collection_topic": "الموضوع",
"collections.content_warning": "تحذير عن المحتوى", "collections.content_warning": "تحذير عن المحتوى",
"collections.continue": "مواصلة", "collections.continue": "مواصلة",
"collections.create.steps": "الخطوة {step}/{total}", "collections.create.steps": "الخطوة {step}/{total}",
"collections.detail.accounts_heading": "الحسابات",
"collections.detail.sensitive_content": "محتوى حساس",
"collections.edit_details": "تعديل التفاصيل",
"collections.manage_accounts": "إدارة الحسابات", "collections.manage_accounts": "إدارة الحسابات",
"column.about": "عن", "column.about": "عن",
"column.blocks": "المُستَخدِمون المَحظورون", "column.blocks": "المُستَخدِمون المَحظورون",
@ -195,6 +224,9 @@
"column_header.show_settings": "إظهار الإعدادات", "column_header.show_settings": "إظهار الإعدادات",
"column_header.unpin": "إلغاء التَّثبيت", "column_header.unpin": "إلغاء التَّثبيت",
"column_search.cancel": "إلغاء", "column_search.cancel": "إلغاء",
"combobox.close_results": "إغلاق النتائج",
"combobox.loading": "جاري التحميل",
"combobox.open_results": "فتح النتائج",
"community.column_settings.local_only": "المحلي فقط", "community.column_settings.local_only": "المحلي فقط",
"community.column_settings.media_only": "الوسائط فقط", "community.column_settings.media_only": "الوسائط فقط",
"community.column_settings.remote_only": "عن بُعد فقط", "community.column_settings.remote_only": "عن بُعد فقط",
@ -268,11 +300,13 @@
"content_warning.hide": "إخفاء المنشور", "content_warning.hide": "إخفاء المنشور",
"content_warning.show": "إظهار على أي حال", "content_warning.show": "إظهار على أي حال",
"content_warning.show_more": "إظهار المزيد", "content_warning.show_more": "إظهار المزيد",
"content_warning.show_short": "إظهار",
"conversation.delete": "احذف المحادثة", "conversation.delete": "احذف المحادثة",
"conversation.mark_as_read": "اعتبرها كمقروءة", "conversation.mark_as_read": "اعتبرها كمقروءة",
"conversation.open": "اعرض المحادثة", "conversation.open": "اعرض المحادثة",
"conversation.with": "مع {names}", "conversation.with": "مع {names}",
"copy_icon_button.copied": "نُسِخ إلى الحافظة", "copy_icon_button.copied": "نُسِخ إلى الحافظة",
"copy_icon_button.copy_this_text": "نسخ الرابط إلى الحافظة",
"copypaste.copied": "تم نسخه", "copypaste.copied": "تم نسخه",
"copypaste.copy_to_clipboard": "نسخ إلى الحافظة", "copypaste.copy_to_clipboard": "نسخ إلى الحافظة",
"directory.federated": "مِن الفديفرس المعروف", "directory.federated": "مِن الفديفرس المعروف",
@ -307,6 +341,8 @@
"domain_pill.your_server": "موطِنك الرقمي، حيث توجد فيه كافة منشوراتك. ألا يعجبك المكان؟ يمكنك الانتقال بين الخوادم في أي وقت واصطحاب متابعيك أيضاً.", "domain_pill.your_server": "موطِنك الرقمي، حيث توجد فيه كافة منشوراتك. ألا يعجبك المكان؟ يمكنك الانتقال بين الخوادم في أي وقت واصطحاب متابعيك أيضاً.",
"domain_pill.your_username": "معرفك الفريد على هذا الخادم. من الممكن العثور على مستخدمين بنفس إسم المستخدم على خوادم مختلفة.", "domain_pill.your_username": "معرفك الفريد على هذا الخادم. من الممكن العثور على مستخدمين بنفس إسم المستخدم على خوادم مختلفة.",
"dropdown.empty": "حدد خيارا", "dropdown.empty": "حدد خيارا",
"email_subscriptions.email": "البريد الإلكتروني",
"email_subscriptions.form.action": "اشترك",
"embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.", "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
"embed.preview": "إليك ما سيبدو عليه:", "embed.preview": "إليك ما سيبدو عليه:",
"emoji_button.activity": "الأنشطة", "emoji_button.activity": "الأنشطة",
@ -324,7 +360,6 @@
"emoji_button.search_results": "نتائج البحث", "emoji_button.search_results": "نتائج البحث",
"emoji_button.symbols": "رموز", "emoji_button.symbols": "رموز",
"emoji_button.travel": "الأماكن والسفر", "emoji_button.travel": "الأماكن والسفر",
"empty_column.account_featured.other": "{acct} لم يعرض أي شيء حتى الآن. هل تعلم أنه يمكنك عرض الهاشتاقات التي تستخدمها، وحتى حسابات أصدقاءك على ملفك الشخصي؟",
"empty_column.account_hides_collections": "اختار هذا المستخدم عدم إتاحة هذه المعلومات للعامة", "empty_column.account_hides_collections": "اختار هذا المستخدم عدم إتاحة هذه المعلومات للعامة",
"empty_column.account_suspended": "حساب معلق", "empty_column.account_suspended": "حساب معلق",
"empty_column.account_timeline": "لا توجد منشورات هنا!", "empty_column.account_timeline": "لا توجد منشورات هنا!",
@ -346,6 +381,7 @@
"empty_column.notification_requests": "لا يوجد شيء هنا. عندما تتلقى إشعارات جديدة، سوف تظهر هنا وفقًا لإعداداتك.", "empty_column.notification_requests": "لا يوجد شيء هنا. عندما تتلقى إشعارات جديدة، سوف تظهر هنا وفقًا لإعداداتك.",
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
"empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات", "empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
"empty_state.no_results": "لا توجد نتائج",
"error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.", "error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.",
"error.unexpected_crash.explanation_addons": "لا يمكن عرض هذه الصفحة بشكل صحيح. من المحتمل أن يكون هذا الخطأ بسبب إضافة متصفح أو أدوات ترجمة تلقائية.", "error.unexpected_crash.explanation_addons": "لا يمكن عرض هذه الصفحة بشكل صحيح. من المحتمل أن يكون هذا الخطأ بسبب إضافة متصفح أو أدوات ترجمة تلقائية.",
"error.unexpected_crash.next_steps": "حاول إعادة إنعاش الصفحة. إن لم تُحلّ المشكلة، يمكنك دائمًا استخدام ماستدون عبر متصفّح آخر أو تطبيق أصلي.", "error.unexpected_crash.next_steps": "حاول إعادة إنعاش الصفحة. إن لم تُحلّ المشكلة، يمكنك دائمًا استخدام ماستدون عبر متصفّح آخر أو تطبيق أصلي.",
@ -358,6 +394,8 @@
"explore.trending_statuses": "المنشورات", "explore.trending_statuses": "المنشورات",
"explore.trending_tags": "وُسُوم", "explore.trending_tags": "وُسُوم",
"featured_carousel.header": "{count, plural, zero {}one {منشور معروض} two {منشور معروضَين} few {منشورات معروضة} many {منشورات معروضة} other {منشورات معروضة}}", "featured_carousel.header": "{count, plural, zero {}one {منشور معروض} two {منشور معروضَين} few {منشورات معروضة} many {منشورات معروضة} other {منشورات معروضة}}",
"featured_tags.suggestions.add": "إضافة",
"featured_tags.suggestions.dismiss": "لا شكراً",
"filter_modal.added.context_mismatch_explanation": "فئة عامل التصفية هذه لا تنطبق على السياق الذي وصلت فيه إلى هذه المشاركة. إذا كنت ترغب في تصفية المنشور في هذا السياق أيضا، فسيتعين عليك تعديل عامل التصفية.", "filter_modal.added.context_mismatch_explanation": "فئة عامل التصفية هذه لا تنطبق على السياق الذي وصلت فيه إلى هذه المشاركة. إذا كنت ترغب في تصفية المنشور في هذا السياق أيضا، فسيتعين عليك تعديل عامل التصفية.",
"filter_modal.added.context_mismatch_title": "عدم تطابق السياق!", "filter_modal.added.context_mismatch_title": "عدم تطابق السياق!",
"filter_modal.added.expired_explanation": "انتهت صلاحية فئة عامل التصفية هذه، سوف تحتاج إلى تغيير تاريخ انتهاء الصلاحية لتطبيقها.", "filter_modal.added.expired_explanation": "انتهت صلاحية فئة عامل التصفية هذه، سوف تحتاج إلى تغيير تاريخ انتهاء الصلاحية لتطبيقها.",
@ -867,6 +905,8 @@
"status.cancel_reblog_private": "إلغاء إعادة النشر", "status.cancel_reblog_private": "إلغاء إعادة النشر",
"status.cannot_quote": "غير مصرح لك باقتباس هذا المنشور", "status.cannot_quote": "غير مصرح لك باقتباس هذا المنشور",
"status.cannot_reblog": "لا يمكن إعادة نشر هذا المنشور", "status.cannot_reblog": "لا يمكن إعادة نشر هذا المنشور",
"status.context.retry": "حاول مجددًا",
"status.context.show": "إظهار",
"status.continued_thread": "تكملة للخيط", "status.continued_thread": "تكملة للخيط",
"status.copy": "انسخ رابط الرسالة", "status.copy": "انسخ رابط الرسالة",
"status.delete": "احذف", "status.delete": "احذف",

View File

@ -45,6 +45,7 @@
"account.featured": "Выбранае", "account.featured": "Выбранае",
"account.featured.accounts": "Профілі", "account.featured.accounts": "Профілі",
"account.featured.collections": "Калекцыі", "account.featured.collections": "Калекцыі",
"account.featured.new_collection": "Новая калекцыя",
"account.field_overflow": "Паказаць усё змесціва", "account.field_overflow": "Паказаць усё змесціва",
"account.filters.all": "Уся актыўнасць", "account.filters.all": "Уся актыўнасць",
"account.filters.boosts_toggle": "Паказваць пашырэнні", "account.filters.boosts_toggle": "Паказваць пашырэнні",
@ -416,7 +417,6 @@
"column.about": "Пра нас", "column.about": "Пра нас",
"column.blocks": "Заблакіраваныя карыстальнікі", "column.blocks": "Заблакіраваныя карыстальнікі",
"column.bookmarks": "Закладкі", "column.bookmarks": "Закладкі",
"column.collections": "Мае калекцыі",
"column.community": "Лакальная стужка", "column.community": "Лакальная стужка",
"column.create_list": "Стварыць спіс", "column.create_list": "Стварыць спіс",
"column.direct": "Прыватныя згадванні", "column.direct": "Прыватныя згадванні",
@ -432,7 +432,9 @@
"column.list_members": "Кіраванне ўдзельнікамі спіса", "column.list_members": "Кіраванне ўдзельнікамі спіса",
"column.lists": "Спісы", "column.lists": "Спісы",
"column.mutes": "Ігнараваныя карыстальнікі", "column.mutes": "Ігнараваныя карыстальнікі",
"column.my_collections": "Мае калекцыі",
"column.notifications": "Апавяшчэнні", "column.notifications": "Апавяшчэнні",
"column.other_collections": "Калекцыі {name}",
"column.pins": "Замацаваныя допісы", "column.pins": "Замацаваныя допісы",
"column.public": "Інтэграваная стужка", "column.public": "Інтэграваная стужка",
"column_back_button.label": "Назад", "column_back_button.label": "Назад",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Падпісацца і дадаць у спіс", "confirmations.follow_to_list.confirm": "Падпісацца і дадаць у спіс",
"confirmations.follow_to_list.message": "Вам трэба падпісацца на {name}, каб дадаць яго/яе ў спіс.", "confirmations.follow_to_list.message": "Вам трэба падпісацца на {name}, каб дадаць яго/яе ў спіс.",
"confirmations.follow_to_list.title": "Падпісацца на карыстальніка?", "confirmations.follow_to_list.title": "Падпісацца на карыстальніка?",
"confirmations.hide_featured_tab.confirm": "Схаваць укладку",
"confirmations.hide_featured_tab.intro": "Вы можаце змяніць гэтую наладу ў любы момант у <i>Рэдагаваць профіль > Налады ўкладкі профілю</i>.",
"confirmations.hide_featured_tab.message": "Гэтае дзеянне схавае ўкладку ад карыстальнікаў {serverName} і іншых сервераў з апошняй версіяй Mastodon. Яе адлюстраванне на іншых серверах можа адрознівацца.",
"confirmations.hide_featured_tab.title": "Схаваць укладку \"Выбранае\"?",
"confirmations.logout.confirm": "Выйсці", "confirmations.logout.confirm": "Выйсці",
"confirmations.logout.message": "Вы ўпэўненыя, што хочаце выйсці?", "confirmations.logout.message": "Вы ўпэўненыя, што хочаце выйсці?",
"confirmations.logout.title": "Выйсці?", "confirmations.logout.title": "Выйсці?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Вынікі пошуку", "emoji_button.search_results": "Вынікі пошуку",
"emoji_button.symbols": "Сімвалы", "emoji_button.symbols": "Сімвалы",
"emoji_button.travel": "Падарожжы і месцы", "emoji_button.travel": "Падарожжы і месцы",
"empty_column.account_featured.other": "{acct} яшчэ нічога не адабраў для паказу. Ці ведалі Вы, што ў сваім профілі Вы можаце паказаць свае хэштэгі, якімі найбольш карыстаецеся, і нават профілі сваіх сяброў?", "empty_column.account_featured.other": "{acct} пакуль нічога не адабраў(-ла) для паказу.",
"empty_column.account_featured_other.no_collections_desc": "{acct} пакуль не стварыў(-ла) аніводнай калекцыі.",
"empty_column.account_featured_other.title": "Тут нічога няма",
"empty_column.account_featured_self.no_collections": "Пакуль няма калекцый",
"empty_column.account_featured_self.no_collections_button": "Стварыць калекцыю", "empty_column.account_featured_self.no_collections_button": "Стварыць калекцыю",
"empty_column.account_featured_self.no_collections_hide_tab": "Або схаваць гэтую ўкладку",
"empty_column.account_featured_self.pre_collections": "Рыхтуйцеся да Калекцый", "empty_column.account_featured_self.pre_collections": "Рыхтуйцеся да Калекцый",
"empty_column.account_featured_self.pre_collections_desc": "Калекцыі (новая функцыя Mastodon 4.6) дазволяць Вам ствараць свае ўласныя спісы ўліковых запісаў, каб раіць іх іншым.", "empty_column.account_featured_self.pre_collections_desc": "Калекцыі (новая функцыя Mastodon 4.6) дазволяць Вам ствараць свае ўласныя спісы ўліковых запісаў, каб раіць іх іншым.",
"empty_column.account_featured_unknown.no_collections_desc": "Гэты ўліковы запіс пакуль не стварыў аніводнай калекцыі.", "empty_column.account_featured_self.showcase_accounts": "Паказаць Вашыя ўлюбёныя ўліковыя запісы",
"empty_column.account_featured_self.showcase_accounts_desc": "Калекцыі — спісы адабраных уліковых запісаў, якія дапамагаюць іншым знаходзіць нешта новае ў федэральным сусвеце.",
"empty_column.account_featured_unknown.other": "Гэты ўліковы запіс пакуль нічога не адабраў для паказу.", "empty_column.account_featured_unknown.other": "Гэты ўліковы запіс пакуль нічога не адабраў для паказу.",
"empty_column.account_hides_collections": "Гэты карыстальнік вырашыў схаваць гэтую інфармацыю", "empty_column.account_hides_collections": "Гэты карыстальнік вырашыў схаваць гэтую інфармацыю",
"empty_column.account_suspended": "Уліковы запіс прыпынены", "empty_column.account_suspended": "Уліковы запіс прыпынены",

View File

@ -219,7 +219,6 @@
"column.about": "Quant a", "column.about": "Quant a",
"column.blocks": "Usuaris blocats", "column.blocks": "Usuaris blocats",
"column.bookmarks": "Marcadors", "column.bookmarks": "Marcadors",
"column.collections": "Les meves coŀleccions",
"column.community": "Línia de temps local", "column.community": "Línia de temps local",
"column.create_list": "Crea una llista", "column.create_list": "Crea una llista",
"column.direct": "Mencions privades", "column.direct": "Mencions privades",
@ -388,7 +387,6 @@
"emoji_button.search_results": "Resultats de la cerca", "emoji_button.search_results": "Resultats de la cerca",
"emoji_button.symbols": "Símbols", "emoji_button.symbols": "Símbols",
"emoji_button.travel": "Viatges i llocs", "emoji_button.travel": "Viatges i llocs",
"empty_column.account_featured.other": "{acct} encara no ha destacat res. Sabeu que podeu destacar les etiquetes que més feu servir i fins i tot els comptes dels vostres amics al vostre perfil?",
"empty_column.account_hides_collections": "Aquest usuari ha decidit no mostrar aquesta informació", "empty_column.account_hides_collections": "Aquest usuari ha decidit no mostrar aquesta informació",
"empty_column.account_suspended": "Compte suspès", "empty_column.account_suspended": "Compte suspès",
"empty_column.account_timeline": "No hi ha tuts aquí!", "empty_column.account_timeline": "No hi ha tuts aquí!",

View File

@ -266,7 +266,6 @@
"column.about": "O aplikaci", "column.about": "O aplikaci",
"column.blocks": "Blokovaní uživatelé", "column.blocks": "Blokovaní uživatelé",
"column.bookmarks": "Záložky", "column.bookmarks": "Záložky",
"column.collections": "Mé sbírky",
"column.community": "Místní časová osa", "column.community": "Místní časová osa",
"column.create_list": "Vytvořit seznam", "column.create_list": "Vytvořit seznam",
"column.direct": "Soukromé zmínky", "column.direct": "Soukromé zmínky",
@ -439,7 +438,6 @@
"emoji_button.search_results": "Výsledky hledání", "emoji_button.search_results": "Výsledky hledání",
"emoji_button.symbols": "Symboly", "emoji_button.symbols": "Symboly",
"emoji_button.travel": "Cestování a místa", "emoji_button.travel": "Cestování a místa",
"empty_column.account_featured.other": "{acct} zatím nic nezvýraznili. Věděli jste, že na svém profilu můžete zvýraznit hashtagy, které používáte nejvíce, a dokonce účty vašich přátel?",
"empty_column.account_hides_collections": "Tento uživatel se rozhodl tuto informaci nezveřejňovat", "empty_column.account_hides_collections": "Tento uživatel se rozhodl tuto informaci nezveřejňovat",
"empty_column.account_suspended": "Účet je pozastaven", "empty_column.account_suspended": "Účet je pozastaven",
"empty_column.account_timeline": "Nejsou tu žádné příspěvky!", "empty_column.account_timeline": "Nejsou tu žádné příspěvky!",

View File

@ -383,7 +383,6 @@
"column.about": "Ynghylch", "column.about": "Ynghylch",
"column.blocks": "Defnyddwyr wedi'u rhwystro", "column.blocks": "Defnyddwyr wedi'u rhwystro",
"column.bookmarks": "Llyfrnodau", "column.bookmarks": "Llyfrnodau",
"column.collections": "Fy nghasgliadau",
"column.community": "Ffrwd lleol", "column.community": "Ffrwd lleol",
"column.create_list": "Creu rhestr", "column.create_list": "Creu rhestr",
"column.direct": "Crybwylliadau preifat", "column.direct": "Crybwylliadau preifat",
@ -564,7 +563,6 @@
"emoji_button.search_results": "Canlyniadau chwilio", "emoji_button.search_results": "Canlyniadau chwilio",
"emoji_button.symbols": "Symbolau", "emoji_button.symbols": "Symbolau",
"emoji_button.travel": "Teithio a Llefydd", "emoji_button.travel": "Teithio a Llefydd",
"empty_column.account_featured.other": "Dyw {acct} heb gynnwys unrhyw beth eto. Oeddech chi'n gwybod y gallwch chi gynnwys yr hashnodau rydych chi'n eu defnyddio fwyaf, a hyd yn oed cyfrifon eich ffrindiau ar eich proffil?",
"empty_column.account_hides_collections": "Mae'r defnyddiwr wedi dewis i beidio rhannu'r wybodaeth yma", "empty_column.account_hides_collections": "Mae'r defnyddiwr wedi dewis i beidio rhannu'r wybodaeth yma",
"empty_column.account_suspended": "Cyfrif wedi'i atal", "empty_column.account_suspended": "Cyfrif wedi'i atal",
"empty_column.account_timeline": "Dim postiadau yma!", "empty_column.account_timeline": "Dim postiadau yma!",

View File

@ -45,6 +45,7 @@
"account.featured": "Fremhævet", "account.featured": "Fremhævet",
"account.featured.accounts": "Profiler", "account.featured.accounts": "Profiler",
"account.featured.collections": "Samlinger", "account.featured.collections": "Samlinger",
"account.featured.new_collection": "Ny samling",
"account.field_overflow": "Vis fuldt indhold", "account.field_overflow": "Vis fuldt indhold",
"account.filters.all": "Al aktivitet", "account.filters.all": "Al aktivitet",
"account.filters.boosts_toggle": "Vis fremhævelser", "account.filters.boosts_toggle": "Vis fremhævelser",
@ -416,7 +417,6 @@
"column.about": "Om", "column.about": "Om",
"column.blocks": "Blokerede brugere", "column.blocks": "Blokerede brugere",
"column.bookmarks": "Bogmærker", "column.bookmarks": "Bogmærker",
"column.collections": "Mine samlinger",
"column.community": "Lokal tidslinje", "column.community": "Lokal tidslinje",
"column.create_list": "Opret liste", "column.create_list": "Opret liste",
"column.direct": "Private omtaler", "column.direct": "Private omtaler",
@ -432,7 +432,9 @@
"column.list_members": "Håndtér listemedlemmer", "column.list_members": "Håndtér listemedlemmer",
"column.lists": "Lister", "column.lists": "Lister",
"column.mutes": "Skjulte brugere", "column.mutes": "Skjulte brugere",
"column.my_collections": "Mine samlinger",
"column.notifications": "Notifikationer", "column.notifications": "Notifikationer",
"column.other_collections": "Samlinger af {name}",
"column.pins": "Fastgjorte indlæg", "column.pins": "Fastgjorte indlæg",
"column.public": "Fælles tidslinje", "column.public": "Fælles tidslinje",
"column_back_button.label": "Tilbage", "column_back_button.label": "Tilbage",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Følg og føj til liste", "confirmations.follow_to_list.confirm": "Følg og føj til liste",
"confirmations.follow_to_list.message": "Du skal følge {name} for at føje vedkommende til en liste.", "confirmations.follow_to_list.message": "Du skal følge {name} for at føje vedkommende til en liste.",
"confirmations.follow_to_list.title": "Følg bruger?", "confirmations.follow_to_list.title": "Følg bruger?",
"confirmations.hide_featured_tab.confirm": "Skjul fane",
"confirmations.hide_featured_tab.intro": "Du kan til enhver tid ændre dette under <i>Rediger profil > Indstillinger for profil-fane</i>.",
"confirmations.hide_featured_tab.message": "Dette vil skjule fanen for brugere på {serverName} og andre servere, der kører den nyeste version af Mastodon. Andre visninger kan variere.",
"confirmations.hide_featured_tab.title": "Skjul fanen \"Fremhævet\"?",
"confirmations.logout.confirm": "Log ud", "confirmations.logout.confirm": "Log ud",
"confirmations.logout.message": "Er du sikker på, at du vil logge ud?", "confirmations.logout.message": "Er du sikker på, at du vil logge ud?",
"confirmations.logout.title": "Log ud?", "confirmations.logout.title": "Log ud?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Søgeresultater", "emoji_button.search_results": "Søgeresultater",
"emoji_button.symbols": "Symboler", "emoji_button.symbols": "Symboler",
"emoji_button.travel": "Rejser og steder", "emoji_button.travel": "Rejser og steder",
"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": "{acct} har ikke fremhævet noget endnu.",
"empty_column.account_featured_other.no_collections_desc": "{acct} har endnu ikke oprettet nogen samlinger.",
"empty_column.account_featured_other.title": "Intet at se her",
"empty_column.account_featured_self.no_collections": "Ingen samlinger endnu",
"empty_column.account_featured_self.no_collections_button": "Opret en samling", "empty_column.account_featured_self.no_collections_button": "Opret en samling",
"empty_column.account_featured_self.no_collections_hide_tab": "Skjul denne fane i stedet",
"empty_column.account_featured_self.pre_collections": "Hold udkig efter Samlinger", "empty_column.account_featured_self.pre_collections": "Hold udkig efter Samlinger",
"empty_column.account_featured_self.pre_collections_desc": "Med samlinger (kommer i Mastodon 4.6) kan du oprette dine egne kuraterede lister over konti, som du kan anbefale til andre.", "empty_column.account_featured_self.pre_collections_desc": "Med samlinger (kommer i Mastodon 4.6) kan du oprette dine egne kuraterede lister over konti, som du kan anbefale til andre.",
"empty_column.account_featured_unknown.no_collections_desc": "Denne konto har endnu ikke oprettet nogen samlinger.", "empty_column.account_featured_self.showcase_accounts": "Vis dine favoritkonti",
"empty_column.account_featured_self.showcase_accounts_desc": "Samlinger er kuraterede lister over konti, der skal hjælpe andre med at opdage mere af Fediverset.",
"empty_column.account_featured_unknown.other": "Denne konto har ikke fremhævet noget endnu.", "empty_column.account_featured_unknown.other": "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_hides_collections": "Brugeren har valgt ikke at gøre denne information tilgængelig",
"empty_column.account_suspended": "Konto suspenderet", "empty_column.account_suspended": "Konto suspenderet",

View File

@ -45,6 +45,7 @@
"account.featured": "Vorgestellt", "account.featured": "Vorgestellt",
"account.featured.accounts": "Profile", "account.featured.accounts": "Profile",
"account.featured.collections": "Sammlungen", "account.featured.collections": "Sammlungen",
"account.featured.new_collection": "Neue Sammlung",
"account.field_overflow": "Vollständigen Inhalt anzeigen", "account.field_overflow": "Vollständigen Inhalt anzeigen",
"account.filters.all": "Alle Aktivitäten", "account.filters.all": "Alle Aktivitäten",
"account.filters.boosts_toggle": "Geteilte Beiträge anzeigen", "account.filters.boosts_toggle": "Geteilte Beiträge anzeigen",
@ -416,7 +417,6 @@
"column.about": "Über", "column.about": "Über",
"column.blocks": "Blockierte Profile", "column.blocks": "Blockierte Profile",
"column.bookmarks": "Lesezeichen", "column.bookmarks": "Lesezeichen",
"column.collections": "Meine Sammlungen",
"column.community": "Lokale Timeline", "column.community": "Lokale Timeline",
"column.create_list": "Liste erstellen", "column.create_list": "Liste erstellen",
"column.direct": "Private Erwähnungen", "column.direct": "Private Erwähnungen",
@ -432,7 +432,9 @@
"column.list_members": "Listenmitglieder verwalten", "column.list_members": "Listenmitglieder verwalten",
"column.lists": "Listen", "column.lists": "Listen",
"column.mutes": "Stummgeschaltete Profile", "column.mutes": "Stummgeschaltete Profile",
"column.my_collections": "Meine Sammlungen",
"column.notifications": "Benachrichtigungen", "column.notifications": "Benachrichtigungen",
"column.other_collections": "Sammlungen von {name}",
"column.pins": "Angeheftete Beiträge", "column.pins": "Angeheftete Beiträge",
"column.public": "Föderierte Timeline", "column.public": "Föderierte Timeline",
"column_back_button.label": "Zurück", "column_back_button.label": "Zurück",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Folgen und zur Liste hinzufügen", "confirmations.follow_to_list.confirm": "Folgen und zur Liste hinzufügen",
"confirmations.follow_to_list.message": "Du musst {name} folgen, um das Profil zu einer Liste hinzufügen zu können.", "confirmations.follow_to_list.message": "Du musst {name} folgen, um das Profil zu einer Liste hinzufügen zu können.",
"confirmations.follow_to_list.title": "Profil folgen?", "confirmations.follow_to_list.title": "Profil folgen?",
"confirmations.hide_featured_tab.confirm": "Tab ausblenden",
"confirmations.hide_featured_tab.intro": "Du kannst die Einstellungen jederzeit unter <i>Profil bearbeiten > Profil-Tab-Einstellungen</i> ändern.",
"confirmations.hide_featured_tab.message": "Dieser Tab wird auf {serverName} und anderen Servern mit der neuesten Mastodon-Version nicht angezeigt. Darstellung kann darüber hinaus abweichen.",
"confirmations.hide_featured_tab.title": "„Vorgestellt“-Tab ausblenden?",
"confirmations.logout.confirm": "Abmelden", "confirmations.logout.confirm": "Abmelden",
"confirmations.logout.message": "Möchtest du dich wirklich ausloggen?", "confirmations.logout.message": "Möchtest du dich wirklich ausloggen?",
"confirmations.logout.title": "Abmelden?", "confirmations.logout.title": "Abmelden?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Suchergebnisse", "emoji_button.search_results": "Suchergebnisse",
"emoji_button.symbols": "Symbole", "emoji_button.symbols": "Symbole",
"emoji_button.travel": "Reisen & Orte", "emoji_button.travel": "Reisen & Orte",
"empty_column.account_featured.other": "{acct} hat bisher noch nichts vorgestellt. Wusstest du, dass du deine häufig verwendeten Hashtags und sogar Profile von Freund*innen vorstellen kannst?", "empty_column.account_featured.other": "{acct} hat noch nichts vorgestellt.",
"empty_column.account_featured_other.no_collections_desc": "{acct} hat noch keine Sammlungen erstellt.",
"empty_column.account_featured_other.title": "Hier gibt es nichts zu sehen",
"empty_column.account_featured_self.no_collections": "Noch keine Sammlungen",
"empty_column.account_featured_self.no_collections_button": "Sammlung erstellen", "empty_column.account_featured_self.no_collections_button": "Sammlung erstellen",
"empty_column.account_featured_self.no_collections_hide_tab": "Diesen Tab ausblenden",
"empty_column.account_featured_self.pre_collections": "Sammlungen sind bald verfügbar", "empty_column.account_featured_self.pre_collections": "Sammlungen sind bald verfügbar",
"empty_column.account_featured_self.pre_collections_desc": "Mit Sammlungen (neue Funktion in Mastodon 4.6) kannst du deine eigenen kuratierten Listen erstellen, um ausgewählte Konten zu empfehlen.", "empty_column.account_featured_self.pre_collections_desc": "Mit Sammlungen (neue Funktion in Mastodon 4.6) kannst du deine eigenen kuratierten Listen erstellen, um ausgewählte Konten zu empfehlen.",
"empty_column.account_featured_unknown.no_collections_desc": "Dieser Account hat noch keine Sammlungen erstellt.", "empty_column.account_featured_self.showcase_accounts": "Präsentiere deine liebsten Konten",
"empty_column.account_featured_self.showcase_accounts_desc": "Sammlungen sind kuratierte Listen, die anderen helfen, mehr Konten im Fediverse zu entdecken.",
"empty_column.account_featured_unknown.other": "Dieser Account hat noch nichts vorgestellt.", "empty_column.account_featured_unknown.other": "Dieser Account hat noch nichts vorgestellt.",
"empty_column.account_hides_collections": "Das Profil hat sich entschieden, diese Information nicht zu veröffentlichen", "empty_column.account_hides_collections": "Das Profil hat sich entschieden, diese Information nicht zu veröffentlichen",
"empty_column.account_suspended": "Konto dauerhaft gesperrt", "empty_column.account_suspended": "Konto dauerhaft gesperrt",

View File

@ -45,6 +45,7 @@
"account.featured": "Αναδεδειγμένα", "account.featured": "Αναδεδειγμένα",
"account.featured.accounts": "Προφίλ", "account.featured.accounts": "Προφίλ",
"account.featured.collections": "Συλλογές", "account.featured.collections": "Συλλογές",
"account.featured.new_collection": "Νέα συλλογή",
"account.field_overflow": "Εμφάνιση πλήρους περιεχομένου", "account.field_overflow": "Εμφάνιση πλήρους περιεχομένου",
"account.filters.all": "Όλη η δραστηριότητα", "account.filters.all": "Όλη η δραστηριότητα",
"account.filters.boosts_toggle": "Εμφάνιση ενισχύσεων", "account.filters.boosts_toggle": "Εμφάνιση ενισχύσεων",
@ -217,7 +218,7 @@
"account_edit.profile_tab.show_media_replies.description": "Όταν ενεργοποιηθεί, η καρτέλα Πολυμέσα εμφανίζει τόσο τις αναρτήσεις σας όσο και τις απαντήσεις σας σε αναρτήσεις άλλων ατόμων.", "account_edit.profile_tab.show_media_replies.description": "Όταν ενεργοποιηθεί, η καρτέλα Πολυμέσα εμφανίζει τόσο τις αναρτήσεις σας όσο και τις απαντήσεις σας σε αναρτήσεις άλλων ατόμων.",
"account_edit.profile_tab.show_media_replies.title": "Συμπερίληψη απαντήσεων στην καρτέλα «Πολυμέσα»", "account_edit.profile_tab.show_media_replies.title": "Συμπερίληψη απαντήσεων στην καρτέλα «Πολυμέσα»",
"account_edit.profile_tab.subtitle": "Προσαρμόστε τις καρτέλες στο προφίλ σας και τι εμφανίζουν.", "account_edit.profile_tab.subtitle": "Προσαρμόστε τις καρτέλες στο προφίλ σας και τι εμφανίζουν.",
"account_edit.profile_tab.title": "Ρυθμίσεις καρτέλας προφίλ", "account_edit.profile_tab.title": "Ρυθμίσεις καρτελών προφίλ",
"account_edit.save": "Αποθήκευση", "account_edit.save": "Αποθήκευση",
"account_edit.upload_modal.back": "Πίσω", "account_edit.upload_modal.back": "Πίσω",
"account_edit.upload_modal.done": "Έγινε", "account_edit.upload_modal.done": "Έγινε",
@ -416,7 +417,6 @@
"column.about": "Σχετικά με", "column.about": "Σχετικά με",
"column.blocks": "Αποκλεισμένοι χρήστες", "column.blocks": "Αποκλεισμένοι χρήστες",
"column.bookmarks": "Σελιδοδείκτες", "column.bookmarks": "Σελιδοδείκτες",
"column.collections": "Οι συλλογές μου",
"column.community": "Τοπική ροή", "column.community": "Τοπική ροή",
"column.create_list": "Δημιουργία λίστας", "column.create_list": "Δημιουργία λίστας",
"column.direct": "Ιδιωτικές επισημάνσεις", "column.direct": "Ιδιωτικές επισημάνσεις",
@ -432,7 +432,9 @@
"column.list_members": "Διαχείριση μελών λίστας", "column.list_members": "Διαχείριση μελών λίστας",
"column.lists": "Λίστες", "column.lists": "Λίστες",
"column.mutes": "Αποσιωπημένοι χρήστες", "column.mutes": "Αποσιωπημένοι χρήστες",
"column.my_collections": "Οι συλλογές μου",
"column.notifications": "Ειδοποιήσεις", "column.notifications": "Ειδοποιήσεις",
"column.other_collections": "Συλλογές από {name}",
"column.pins": "Καρφιτσωμένες αναρτήσεις", "column.pins": "Καρφιτσωμένες αναρτήσεις",
"column.public": "Ομοσπονδιακή ροή", "column.public": "Ομοσπονδιακή ροή",
"column_back_button.label": "Πίσω", "column_back_button.label": "Πίσω",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Ακολούθησε και πρόσθεσε στη λίστα", "confirmations.follow_to_list.confirm": "Ακολούθησε και πρόσθεσε στη λίστα",
"confirmations.follow_to_list.message": "Πρέπει να ακολουθήσεις τον χρήστη {name} για να τον προσθέσεις σε μια λίστα.", "confirmations.follow_to_list.message": "Πρέπει να ακολουθήσεις τον χρήστη {name} για να τον προσθέσεις σε μια λίστα.",
"confirmations.follow_to_list.title": "Ακολούθηση χρήστη;", "confirmations.follow_to_list.title": "Ακολούθηση χρήστη;",
"confirmations.hide_featured_tab.confirm": "Απόκρυψη καρτέλας",
"confirmations.hide_featured_tab.intro": "Μπορείτε να το αλλάξετε ανά πάσα στιγμή κάτω από το <i>Επεξεργασία προφίλ> Ρυθμίσεις καρτελών προφίλ</i>.",
"confirmations.hide_featured_tab.message": "Αυτό θα κρύψει την καρτέλα για τους χρήστες στο {serverName} και σε άλλους διακομιστές που εκτελούν την τελευταία έκδοση του Mastodon. Άλλες εμφανίσεις μπορεί να ποικίλλουν.",
"confirmations.hide_featured_tab.title": "Απόκρυψη καρτέλας \"Αναδεδειγμένα\";",
"confirmations.logout.confirm": "Αποσύνδεση", "confirmations.logout.confirm": "Αποσύνδεση",
"confirmations.logout.message": "Σίγουρα θέλεις να αποσυνδεθείς;", "confirmations.logout.message": "Σίγουρα θέλεις να αποσυνδεθείς;",
"confirmations.logout.title": "Αποσύνδεση;", "confirmations.logout.title": "Αποσύνδεση;",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Αποτελέσματα αναζήτησης", "emoji_button.search_results": "Αποτελέσματα αναζήτησης",
"emoji_button.symbols": "Σύμβολα", "emoji_button.symbols": "Σύμβολα",
"emoji_button.travel": "Ταξίδια & Τοποθεσίες", "emoji_button.travel": "Ταξίδια & Τοποθεσίες",
"empty_column.account_featured.other": "Ο/Η {acct} δεν έχει αναδείξει τίποτα ακόμη. Γνώριζες ότι μπορείς να αναδείξεις τις ετικέτες που χρησιμοποιείς περισσότερο, ακόμη και τους λογαριασμούς των φίλων σου στο προφίλ σου;", "empty_column.account_featured.other": "Ο/Η {acct} δεν έχει αναδείξει τίποτα ακόμη.",
"empty_column.account_featured_other.no_collections_desc": "Ο χρήστης {acct} δεν έχει δημιουργήσει ακόμη καμία συλλογή.",
"empty_column.account_featured_other.title": "Δεν υπάρχει τίποτα να δείτε εδώ",
"empty_column.account_featured_self.no_collections": "Καμία συλλογή ακόμη",
"empty_column.account_featured_self.no_collections_button": "Δημιουργήστε μια συλλογή", "empty_column.account_featured_self.no_collections_button": "Δημιουργήστε μια συλλογή",
"empty_column.account_featured_self.no_collections_hide_tab": "Απόκρυψη αυτής της καρτέλας αντ' αυτού",
"empty_column.account_featured_self.pre_collections": "Μείνετε συντονισμένοι για τις Συλλογές", "empty_column.account_featured_self.pre_collections": "Μείνετε συντονισμένοι για τις Συλλογές",
"empty_column.account_featured_self.pre_collections_desc": "Οι Συλλογές (που έρχονται στο Mastodon 4.6) σας επιτρέπουν να δημιουργήσετε τις δικές σας επιμελημένες λίστες λογαριασμών για να συστήσετε σε άλλους.", "empty_column.account_featured_self.pre_collections_desc": "Οι Συλλογές (που έρχονται στο Mastodon 4.6) επιτρέπουν να δημιουργήσετε τις δικές σας επιμελημένες λίστες λογαριασμών για να συστήσετε σε άλλους.",
"empty_column.account_featured_unknown.no_collections_desc": "Αυτός ο λογαριασμός δεν έχει δημιουργήσει ακόμη καμία συλλογή.", "empty_column.account_featured_self.showcase_accounts": "Παρουσιάστε τους αγαπημένους σας λογαριασμούς",
"empty_column.account_featured_self.showcase_accounts_desc": "Οι συλλογές είναι επιμελημένες λίστες λογαριασμών για να βοηθήσουν άλλους να ανακαλύψουν περισσότερα από το Fediverse.",
"empty_column.account_featured_unknown.other": "Αυτός ο λογαριασμός δεν έχει αναδείξει τίποτα ακόμη.", "empty_column.account_featured_unknown.other": "Αυτός ο λογαριασμός δεν έχει αναδείξει τίποτα ακόμη.",
"empty_column.account_hides_collections": "Αυτός ο χρήστης έχει επιλέξει να μην καταστήσει αυτές τις πληροφορίες διαθέσιμες", "empty_column.account_hides_collections": "Αυτός ο χρήστης έχει επιλέξει να μην καταστήσει αυτές τις πληροφορίες διαθέσιμες",
"empty_column.account_suspended": "Λογαριασμός σε αναστολή", "empty_column.account_suspended": "Λογαριασμός σε αναστολή",

View File

@ -45,6 +45,7 @@
"account.featured": "Featured", "account.featured": "Featured",
"account.featured.accounts": "Profiles", "account.featured.accounts": "Profiles",
"account.featured.collections": "Collections", "account.featured.collections": "Collections",
"account.featured.new_collection": "New collection",
"account.field_overflow": "Show full content", "account.field_overflow": "Show full content",
"account.filters.all": "All activity", "account.filters.all": "All activity",
"account.filters.boosts_toggle": "Show boosts", "account.filters.boosts_toggle": "Show boosts",
@ -102,6 +103,7 @@
"account.muted": "Muted", "account.muted": "Muted",
"account.muting": "Muting", "account.muting": "Muting",
"account.mutual": "You follow each other", "account.mutual": "You follow each other",
"account.name.copy": "Copy handle",
"account.name.help.domain": "{domain} is the server that hosts the users profile and posts.", "account.name.help.domain": "{domain} is the server that hosts the users profile and posts.",
"account.name.help.domain_self": "{domain} is your server that hosts your profile and posts.", "account.name.help.domain_self": "{domain} is your server that hosts your profile and posts.",
"account.name.help.footer": "Just like you can send emails to people using different email clients, you can interact with people on other Mastodon servers and with anyone on other social apps powered by the same set of rules as Mastodon uses (the ActivityPub protocol).", "account.name.help.footer": "Just like you can send emails to people using different email clients, you can interact with people on other Mastodon servers and with anyone on other social apps powered by the same set of rules as Mastodon uses (the ActivityPub protocol).",
@ -138,6 +140,9 @@
"account.unmute": "Unmute @{name}", "account.unmute": "Unmute @{name}",
"account.unmute_notifications_short": "Unmute notifications", "account.unmute_notifications_short": "Unmute notifications",
"account.unmute_short": "Unmute", "account.unmute_short": "Unmute",
"account_edit.advanced_settings.bot_hint": "Signal to others that the account mainly performs automated actions and might not be monitored",
"account_edit.advanced_settings.bot_label": "Automated account",
"account_edit.advanced_settings.title": "Advanced settings",
"account_edit.bio.add_label": "", "account_edit.bio.add_label": "",
"account_edit.bio.edit_label": "Edit bio", "account_edit.bio.edit_label": "Edit bio",
"account_edit.bio.placeholder": "Add a short introduction to help others identify you.", "account_edit.bio.placeholder": "Add a short introduction to help others identify you.",
@ -346,7 +351,7 @@
"closed_registrations_modal.find_another_server": "Find another server", "closed_registrations_modal.find_another_server": "Find another server",
"closed_registrations_modal.preamble": "Mastodon is decentralised, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!", "closed_registrations_modal.preamble": "Mastodon is decentralised, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
"closed_registrations_modal.title": "Signing up on Mastodon", "closed_registrations_modal.title": "Signing up on Mastodon",
"collection.share_modal.share_link_label": "Invite share link", "collection.share_modal.share_link_label": "Share link",
"collection.share_modal.share_via_post": "Post on Mastodon", "collection.share_modal.share_via_post": "Post on Mastodon",
"collection.share_modal.share_via_system": "Share to…", "collection.share_modal.share_via_system": "Share to…",
"collection.share_modal.title": "Share collection", "collection.share_modal.title": "Share collection",
@ -380,6 +385,7 @@
"collections.detail.sensitive_content": "Sensitive content", "collections.detail.sensitive_content": "Sensitive content",
"collections.detail.sensitive_note": "This collection contains accounts and content that may be sensitive to some users.", "collections.detail.sensitive_note": "This collection contains accounts and content that may be sensitive to some users.",
"collections.detail.share": "Share this collection", "collections.detail.share": "Share this collection",
"collections.detail.you_are_in_this_collection": "You're featured in this collection",
"collections.edit_details": "Edit details", "collections.edit_details": "Edit details",
"collections.error_loading_collections": "There was an error when trying to load your collections.", "collections.error_loading_collections": "There was an error when trying to load your collections.",
"collections.hints.accounts_counter": "{count} / {max} accounts", "collections.hints.accounts_counter": "{count} / {max} accounts",
@ -411,7 +417,6 @@
"column.about": "About", "column.about": "About",
"column.blocks": "Blocked users", "column.blocks": "Blocked users",
"column.bookmarks": "Bookmarks", "column.bookmarks": "Bookmarks",
"column.collections": "My collections",
"column.community": "Local timeline", "column.community": "Local timeline",
"column.create_list": "Create list", "column.create_list": "Create list",
"column.direct": "Private mentions", "column.direct": "Private mentions",
@ -427,7 +432,9 @@
"column.list_members": "Manage list members", "column.list_members": "Manage list members",
"column.lists": "Lists", "column.lists": "Lists",
"column.mutes": "Muted users", "column.mutes": "Muted users",
"column.my_collections": "My collections",
"column.notifications": "Notifications", "column.notifications": "Notifications",
"column.other_collections": "Collections by {name}",
"column.pins": "Pinned posts", "column.pins": "Pinned posts",
"column.public": "Federated timeline", "column.public": "Federated timeline",
"column_back_button.label": "Back", "column_back_button.label": "Back",
@ -497,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Follow and add to list", "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.message": "You need to be following {name} to add them to a list.",
"confirmations.follow_to_list.title": "Follow user?", "confirmations.follow_to_list.title": "Follow user?",
"confirmations.hide_featured_tab.confirm": "Hide tab",
"confirmations.hide_featured_tab.intro": "You can change this at any time under <i>Edit profile > Profile tab settings</i>.",
"confirmations.hide_featured_tab.message": "This will hide the tab for users on {serverName} and other servers running the latest version of Mastodon. Other displays may vary.",
"confirmations.hide_featured_tab.title": "Hide \"Featured\" tab?",
"confirmations.logout.confirm": "Log out", "confirmations.logout.confirm": "Log out",
"confirmations.logout.message": "Are you sure you want to log out?", "confirmations.logout.message": "Are you sure you want to log out?",
"confirmations.logout.title": "Log out?", "confirmations.logout.title": "Log out?",
@ -535,6 +546,7 @@
"content_warning.hide": "Hide post", "content_warning.hide": "Hide post",
"content_warning.show": "Show anyway", "content_warning.show": "Show anyway",
"content_warning.show_more": "Show more", "content_warning.show_more": "Show more",
"content_warning.show_short": "Show",
"conversation.delete": "Delete conversation", "conversation.delete": "Delete conversation",
"conversation.mark_as_read": "Mark as read", "conversation.mark_as_read": "Mark as read",
"conversation.open": "View conversation", "conversation.open": "View conversation",
@ -575,6 +587,14 @@
"domain_pill.your_server": "Your digital home, where all of your posts live. Dont like this one? Transfer servers at any time and bring your followers too.", "domain_pill.your_server": "Your digital home, where all of your posts live. Dont like this one? Transfer servers at any time and bring your followers too.",
"domain_pill.your_username": "Your unique identifier on this server. Its possible to find users with the same username on different servers.", "domain_pill.your_username": "Your unique identifier on this server. Its possible to find users with the same username on different servers.",
"dropdown.empty": "Select an option", "dropdown.empty": "Select an option",
"email_subscriptions.email": "Email",
"email_subscriptions.form.action": "Subscribe",
"email_subscriptions.form.bottom": "Get posts in your inbox without creating a Mastodon account. Unsubscribe at any time. For more information, refer to the <a>Privacy Policy</a>.",
"email_subscriptions.form.title": "Sign up for email updates from {name}",
"email_subscriptions.submitted.lead": "Check your inbox for an email to finish signing up for email updates.",
"email_subscriptions.submitted.title": "One more step",
"email_subscriptions.validation.email.blocked": "Blocked email provider",
"email_subscriptions.validation.email.invalid": "Invalid email address",
"embed.instructions": "Embed this post on your website by copying the code below.", "embed.instructions": "Embed this post on your website by copying the code below.",
"embed.preview": "Here is what it will look like:", "embed.preview": "Here is what it will look like:",
"emoji_button.activity": "Activity", "emoji_button.activity": "Activity",
@ -592,7 +612,14 @@
"emoji_button.search_results": "Search results", "emoji_button.search_results": "Search results",
"emoji_button.symbols": "Symbols", "emoji_button.symbols": "Symbols",
"emoji_button.travel": "Travel & Places", "emoji_button.travel": "Travel & Places",
"empty_column.account_featured.other": "{acct} has not featured anything yet. Did you know that you can feature your hashtags you use the most, and even your friends accounts on your profile?", "empty_column.account_featured.other": "{acct} has not featured anything yet.",
"empty_column.account_featured_self.no_collections_button": "Create a collection",
"empty_column.account_featured_self.no_collections_hide_tab": "Hide this tab instead",
"empty_column.account_featured_self.pre_collections": "Stay tuned for Collections",
"empty_column.account_featured_self.pre_collections_desc": "Collections (coming in Mastodon 4.6) allow you to create your own curated lists of accounts to recommend to others.",
"empty_column.account_featured_self.showcase_accounts": "Showcase your favourite accounts",
"empty_column.account_featured_self.showcase_accounts_desc": "Collections are curated lists of accounts to help others discover more of the Fediverse.",
"empty_column.account_featured_unknown.other": "This account hasnt featured anything yet.",
"empty_column.account_hides_collections": "This user has chosen not to make this information available", "empty_column.account_hides_collections": "This user has chosen not to make this information available",
"empty_column.account_suspended": "Account suspended", "empty_column.account_suspended": "Account suspended",
"empty_column.account_timeline": "No posts here!", "empty_column.account_timeline": "No posts here!",
@ -632,6 +659,10 @@
"featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}", "featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}",
"featured_carousel.slide": "Post {current, number} of {max, number}", "featured_carousel.slide": "Post {current, number} of {max, number}",
"featured_tags.more_items": "+{count}", "featured_tags.more_items": "+{count}",
"featured_tags.suggestions": "Lately youve posted about {items}. Add these as featured hashtags?",
"featured_tags.suggestions.add": "Add",
"featured_tags.suggestions.added": "Manage your featured hashtags at any time under <link>Edit Profile > Featured hashtags</link>.",
"featured_tags.suggestions.dismiss": "No thanks",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.", "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
"filter_modal.added.context_mismatch_title": "Context mismatch!", "filter_modal.added.context_mismatch_title": "Context mismatch!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.", "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
@ -674,7 +705,9 @@
"follow_suggestions.who_to_follow": "Who to follow", "follow_suggestions.who_to_follow": "Who to follow",
"followed_tags": "Followed hashtags", "followed_tags": "Followed hashtags",
"followers.hide_other_followers": "This user has chosen not to make their other followers visible", "followers.hide_other_followers": "This user has chosen not to make their other followers visible",
"followers.title": "Following {name}",
"following.hide_other_following": "This user has chosen not to make the rest of who they follow visible", "following.hide_other_following": "This user has chosen not to make the rest of who they follow visible",
"following.title": "Followed by {name}",
"footer.about": "About", "footer.about": "About",
"footer.about_mastodon": "About Mastodon", "footer.about_mastodon": "About Mastodon",
"footer.about_server": "About {domain}", "footer.about_server": "About {domain}",
@ -686,6 +719,7 @@
"footer.source_code": "View source code", "footer.source_code": "View source code",
"footer.status": "Status", "footer.status": "Status",
"footer.terms_of_service": "Terms of service", "footer.terms_of_service": "Terms of service",
"form_error.blank": "Field cannot be blank.",
"form_field.optional": "(optional)", "form_field.optional": "(optional)",
"generic.saved": "Saved", "generic.saved": "Saved",
"getting_started.heading": "Getting started", "getting_started.heading": "Getting started",

View File

@ -45,6 +45,7 @@
"account.featured": "Featured", "account.featured": "Featured",
"account.featured.accounts": "Profiles", "account.featured.accounts": "Profiles",
"account.featured.collections": "Collections", "account.featured.collections": "Collections",
"account.featured.new_collection": "New collection",
"account.field_overflow": "Show full content", "account.field_overflow": "Show full content",
"account.filters.all": "All activity", "account.filters.all": "All activity",
"account.filters.boosts_toggle": "Show boosts", "account.filters.boosts_toggle": "Show boosts",
@ -350,7 +351,7 @@
"closed_registrations_modal.find_another_server": "Find another server", "closed_registrations_modal.find_another_server": "Find another server",
"closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!", "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
"closed_registrations_modal.title": "Signing up on Mastodon", "closed_registrations_modal.title": "Signing up on Mastodon",
"collection.share_modal.share_link_label": "Invite share link", "collection.share_modal.share_link_label": "Share link",
"collection.share_modal.share_via_post": "Post on Mastodon", "collection.share_modal.share_via_post": "Post on Mastodon",
"collection.share_modal.share_via_system": "Share to…", "collection.share_modal.share_via_system": "Share to…",
"collection.share_modal.title": "Share collection", "collection.share_modal.title": "Share collection",
@ -416,7 +417,6 @@
"column.about": "About", "column.about": "About",
"column.blocks": "Blocked users", "column.blocks": "Blocked users",
"column.bookmarks": "Bookmarks", "column.bookmarks": "Bookmarks",
"column.collections": "My collections",
"column.community": "Local timeline", "column.community": "Local timeline",
"column.create_list": "Create list", "column.create_list": "Create list",
"column.direct": "Private mentions", "column.direct": "Private mentions",
@ -432,7 +432,9 @@
"column.list_members": "Manage list members", "column.list_members": "Manage list members",
"column.lists": "Lists", "column.lists": "Lists",
"column.mutes": "Muted users", "column.mutes": "Muted users",
"column.my_collections": "My collections",
"column.notifications": "Notifications", "column.notifications": "Notifications",
"column.other_collections": "Collections by {name}",
"column.pins": "Pinned posts", "column.pins": "Pinned posts",
"column.public": "Federated timeline", "column.public": "Federated timeline",
"column_back_button.label": "Back", "column_back_button.label": "Back",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Follow and add to list", "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.message": "You need to be following {name} to add them to a list.",
"confirmations.follow_to_list.title": "Follow user?", "confirmations.follow_to_list.title": "Follow user?",
"confirmations.hide_featured_tab.confirm": "Hide tab",
"confirmations.hide_featured_tab.intro": "You can change this at any time under <i>Edit profile > Profile tab settings</i>.",
"confirmations.hide_featured_tab.message": "This will hide the tab for users on {serverName} and other servers running the latest version of Mastodon. Other displays may vary.",
"confirmations.hide_featured_tab.title": "Hide \"Featured\" tab?",
"confirmations.logout.confirm": "Log out", "confirmations.logout.confirm": "Log out",
"confirmations.logout.message": "Are you sure you want to log out?", "confirmations.logout.message": "Are you sure you want to log out?",
"confirmations.logout.title": "Log out?", "confirmations.logout.title": "Log out?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Search results", "emoji_button.search_results": "Search results",
"emoji_button.symbols": "Symbols", "emoji_button.symbols": "Symbols",
"emoji_button.travel": "Travel & Places", "emoji_button.travel": "Travel & Places",
"empty_column.account_featured.other": "{acct} has not featured anything yet. Did you know that you can feature your hashtags you use the most, and even your friends accounts on your profile?", "empty_column.account_featured.other": "{acct} has not featured anything yet.",
"empty_column.account_featured_other.no_collections_desc": "{acct} hasnt created any collections yet.",
"empty_column.account_featured_other.title": "Nothing to see here",
"empty_column.account_featured_self.no_collections": "No collections yet",
"empty_column.account_featured_self.no_collections_button": "Create a collection", "empty_column.account_featured_self.no_collections_button": "Create a collection",
"empty_column.account_featured_self.no_collections_hide_tab": "Hide this tab instead",
"empty_column.account_featured_self.pre_collections": "Stay tuned for Collections", "empty_column.account_featured_self.pre_collections": "Stay tuned for Collections",
"empty_column.account_featured_self.pre_collections_desc": "Collections (coming in Mastodon 4.6) allows you to create your own curated lists of accounts to recommend to others.", "empty_column.account_featured_self.pre_collections_desc": "Collections (coming in Mastodon 4.6) allow you to create your own curated lists of accounts to recommend to others.",
"empty_column.account_featured_unknown.no_collections_desc": "This account hasnt created any collections yet.", "empty_column.account_featured_self.showcase_accounts": "Showcase your favorite accounts",
"empty_column.account_featured_self.showcase_accounts_desc": "Collections are curated lists of accounts to help others discover more of the Fediverse.",
"empty_column.account_featured_unknown.other": "This account hasnt featured anything yet.", "empty_column.account_featured_unknown.other": "This account hasnt featured anything yet.",
"empty_column.account_hides_collections": "This user has chosen to not make this information available", "empty_column.account_hides_collections": "This user has chosen to not make this information available",
"empty_column.account_suspended": "Account suspended", "empty_column.account_suspended": "Account suspended",
@ -898,6 +903,7 @@
"navigation_panel.expand_followed_tags": "Expand followed hashtags menu", "navigation_panel.expand_followed_tags": "Expand followed hashtags menu",
"navigation_panel.expand_lists": "Expand list menu", "navigation_panel.expand_lists": "Expand list menu",
"not_signed_in_indicator.not_signed_in": "You need to login to access this resource.", "not_signed_in_indicator.not_signed_in": "You need to login to access this resource.",
"notification.added_to_collection": "{name} added you to a collection",
"notification.admin.report": "{name} reported {target}", "notification.admin.report": "{name} reported {target}",
"notification.admin.report_account": "{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}", "notification.admin.report_account": "{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}",
"notification.admin.report_account_other": "{name} reported {count, plural, one {one post} other {# posts}} from {target}", "notification.admin.report_account_other": "{name} reported {count, plural, one {one post} other {# posts}} from {target}",
@ -907,6 +913,7 @@
"notification.admin.sign_up.name_and_others": "{name} and {count, plural, one {# other} other {# others}} signed up", "notification.admin.sign_up.name_and_others": "{name} and {count, plural, one {# other} other {# others}} signed up",
"notification.annual_report.message": "Your {year} #Wrapstodon awaits! Unveil your year's highlights and memorable moments on Mastodon!", "notification.annual_report.message": "Your {year} #Wrapstodon awaits! Unveil your year's highlights and memorable moments on Mastodon!",
"notification.annual_report.view": "View #Wrapstodon", "notification.annual_report.view": "View #Wrapstodon",
"notification.collection_update": "{name} edited a collection youre in",
"notification.favourite": "{name} favorited your post", "notification.favourite": "{name} favorited your post",
"notification.favourite.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> favorited your post", "notification.favourite.name_and_others_with_link": "{name} and <a>{count, plural, one {# other} other {# others}}</a> favorited your post",
"notification.favourite_pm": "{name} favorited your private mention", "notification.favourite_pm": "{name} favorited your private mention",

View File

@ -45,6 +45,7 @@
"account.featured": "Destacados", "account.featured": "Destacados",
"account.featured.accounts": "Perfiles", "account.featured.accounts": "Perfiles",
"account.featured.collections": "Colecciones", "account.featured.collections": "Colecciones",
"account.featured.new_collection": "Nueva colección",
"account.field_overflow": "Mostrar contenido completo", "account.field_overflow": "Mostrar contenido completo",
"account.filters.all": "Toda la actividad", "account.filters.all": "Toda la actividad",
"account.filters.boosts_toggle": "Mostrar adhesiones", "account.filters.boosts_toggle": "Mostrar adhesiones",
@ -416,7 +417,6 @@
"column.about": "Información", "column.about": "Información",
"column.blocks": "Usuarios bloqueados", "column.blocks": "Usuarios bloqueados",
"column.bookmarks": "Marcadores", "column.bookmarks": "Marcadores",
"column.collections": "Mis colecciones",
"column.community": "Línea temporal local", "column.community": "Línea temporal local",
"column.create_list": "Crear lista", "column.create_list": "Crear lista",
"column.direct": "Menciones privadas", "column.direct": "Menciones privadas",
@ -432,7 +432,9 @@
"column.list_members": "Administrar miembros de la lista", "column.list_members": "Administrar miembros de la lista",
"column.lists": "Listas", "column.lists": "Listas",
"column.mutes": "Usuarios silenciados", "column.mutes": "Usuarios silenciados",
"column.my_collections": "Mis colecciones",
"column.notifications": "Notificaciones", "column.notifications": "Notificaciones",
"column.other_collections": "Colecciones de {name}",
"column.pins": "Mensajes fijados", "column.pins": "Mensajes fijados",
"column.public": "Línea temporal federada", "column.public": "Línea temporal federada",
"column_back_button.label": "Volver", "column_back_button.label": "Volver",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Seguir y agregar a la lista", "confirmations.follow_to_list.confirm": "Seguir y agregar a la lista",
"confirmations.follow_to_list.message": "Necesitás seguir a {name} para agregarle a una lista.", "confirmations.follow_to_list.message": "Necesitás seguir a {name} para agregarle a una lista.",
"confirmations.follow_to_list.title": "¿Querés seguirle?", "confirmations.follow_to_list.title": "¿Querés seguirle?",
"confirmations.hide_featured_tab.confirm": "Ocultar pestaña",
"confirmations.hide_featured_tab.intro": "Podés cambiar esto en cualquier momento en <i>Editar perfil > Configuración de pestaña de perfil</i>.",
"confirmations.hide_featured_tab.message": "Esto ocultará la pestaña para los usuarios en {serverName} y otros servidores que tengan instalada la última versión de Mastodon. Otras pantallas pueden variar.",
"confirmations.hide_featured_tab.title": "¿Ocultar la pestaña «Destacados»?",
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿De verdad querés cerrar la sesión?", "confirmations.logout.message": "¿De verdad querés cerrar la sesión?",
"confirmations.logout.title": "¿Cerrar sesión?", "confirmations.logout.title": "¿Cerrar sesión?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Resultados de búsqueda", "emoji_button.search_results": "Resultados de búsqueda",
"emoji_button.symbols": "Símbolos", "emoji_button.symbols": "Símbolos",
"emoji_button.travel": "Viajes y lugares", "emoji_button.travel": "Viajes y lugares",
"empty_column.account_featured.other": "{acct} todavía no destacó nada. ¿Sabías que en tu perfil podés destacar tus etiquetas que más usás e incluso las cuentas de tus contactos?", "empty_column.account_featured.other": "{acct} todavía no destacó nada.",
"empty_column.account_featured_other.no_collections_desc": "{acct} todavía no creó ninguna colección.",
"empty_column.account_featured_other.title": "No hay nada por acá",
"empty_column.account_featured_self.no_collections": "No hay colecciones aún",
"empty_column.account_featured_self.no_collections_button": "Crear colección", "empty_column.account_featured_self.no_collections_button": "Crear colección",
"empty_column.account_featured_self.no_collections_hide_tab": "En su lugar, ocultar esta pestaña",
"empty_column.account_featured_self.pre_collections": "Mantenete pendiente de las colecciones", "empty_column.account_featured_self.pre_collections": "Mantenete pendiente de las colecciones",
"empty_column.account_featured_self.pre_collections_desc": "Las colecciones (que vendrán en Mastodon 4.6) te permite crear tus propias listas de cuentas seleccionadas para recomendar a otras personas.", "empty_column.account_featured_self.pre_collections_desc": "Las colecciones (que vendrán en Mastodon 4.6) te permiten crear tus propias listas de cuentas seleccionadas para recomendar a otras personas.",
"empty_column.account_featured_unknown.no_collections_desc": "Esta cuenta todavía no creó ninguna colección.", "empty_column.account_featured_self.showcase_accounts": "Mostrá tus cuentas favoritas",
"empty_column.account_featured_self.showcase_accounts_desc": "Las colecciones son listas de cuentas seleccionadas para ayudar a otras personas a descubrir más del Fediverso.",
"empty_column.account_featured_unknown.other": "Esta cuenta todavía no destacó nada.", "empty_column.account_featured_unknown.other": "Esta cuenta todavía no destacó nada.",
"empty_column.account_hides_collections": "Este usuario eligió no publicar esta información", "empty_column.account_hides_collections": "Este usuario eligió no publicar esta información",
"empty_column.account_suspended": "Cuenta suspendida", "empty_column.account_suspended": "Cuenta suspendida",

View File

@ -45,6 +45,7 @@
"account.featured": "Destacado", "account.featured": "Destacado",
"account.featured.accounts": "Perfiles", "account.featured.accounts": "Perfiles",
"account.featured.collections": "Colecciones", "account.featured.collections": "Colecciones",
"account.featured.new_collection": "Nueva colección",
"account.field_overflow": "Mostrar contenido completo", "account.field_overflow": "Mostrar contenido completo",
"account.filters.all": "Toda la actividad", "account.filters.all": "Toda la actividad",
"account.filters.boosts_toggle": "Mostrar impulsos", "account.filters.boosts_toggle": "Mostrar impulsos",
@ -416,7 +417,6 @@
"column.about": "Acerca de", "column.about": "Acerca de",
"column.blocks": "Usuarios bloqueados", "column.blocks": "Usuarios bloqueados",
"column.bookmarks": "Marcadores", "column.bookmarks": "Marcadores",
"column.collections": "Mis colecciones",
"column.community": "Cronología local", "column.community": "Cronología local",
"column.create_list": "Crear lista", "column.create_list": "Crear lista",
"column.direct": "Menciones privadas", "column.direct": "Menciones privadas",
@ -432,7 +432,9 @@
"column.list_members": "Administrar miembros de la lista", "column.list_members": "Administrar miembros de la lista",
"column.lists": "Listas", "column.lists": "Listas",
"column.mutes": "Usuarios silenciados", "column.mutes": "Usuarios silenciados",
"column.my_collections": "Mis colecciones",
"column.notifications": "Notificaciones", "column.notifications": "Notificaciones",
"column.other_collections": "Colecciones de {name}",
"column.pins": "Publicaciones fijadas", "column.pins": "Publicaciones fijadas",
"column.public": "Cronología federada", "column.public": "Cronología federada",
"column_back_button.label": "Atrás", "column_back_button.label": "Atrás",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Seguir y agregar a la lista", "confirmations.follow_to_list.confirm": "Seguir y agregar a la lista",
"confirmations.follow_to_list.message": "Tienes que seguir a {name} para añadirlo a una lista.", "confirmations.follow_to_list.message": "Tienes que seguir a {name} para añadirlo a una lista.",
"confirmations.follow_to_list.title": "¿Seguir a usuario?", "confirmations.follow_to_list.title": "¿Seguir a usuario?",
"confirmations.hide_featured_tab.confirm": "Ocultar pestaña",
"confirmations.hide_featured_tab.intro": "Puedes cambiar esto en cualquier momento en <i>Editar perfil > Configuración de la pestaña Perfil</i>.",
"confirmations.hide_featured_tab.message": "Esto ocultará la pestaña para los usuarios de {serverName} y otros servidores que ejecuten la última versión de Mastodon. La visualización puede variar en otros casos.",
"confirmations.hide_featured_tab.title": "¿Deseas ocultar la pestaña \"Destacado\"?",
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿Estás seguro de que quieres cerrar la sesión?", "confirmations.logout.message": "¿Estás seguro de que quieres cerrar la sesión?",
"confirmations.logout.title": "¿Deseas cerrar sesión?", "confirmations.logout.title": "¿Deseas cerrar sesión?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Resultados de búsqueda", "emoji_button.search_results": "Resultados de búsqueda",
"emoji_button.symbols": "Símbolos", "emoji_button.symbols": "Símbolos",
"emoji_button.travel": "Viajes y lugares", "emoji_button.travel": "Viajes y lugares",
"empty_column.account_featured.other": "{acct} no ha destacado nada todavía. ¿Sabías que puedes destacar las etiquetas que más usas e incluso las cuentas de tus amigos en tu perfil?", "empty_column.account_featured.other": "{acct} no ha destacado nada todavía.",
"empty_column.account_featured_other.no_collections_desc": "{acct} aún no ha creado ninguna colección.",
"empty_column.account_featured_other.title": "Aquí no hay nada que ver",
"empty_column.account_featured_self.no_collections": "Aún no hay colecciones",
"empty_column.account_featured_self.no_collections_button": "Crear una colección", "empty_column.account_featured_self.no_collections_button": "Crear una colección",
"empty_column.account_featured_self.no_collections_hide_tab": "Ocultar esta pestaña",
"empty_column.account_featured_self.pre_collections": "No te pierdas las colecciones", "empty_column.account_featured_self.pre_collections": "No te pierdas las colecciones",
"empty_column.account_featured_self.pre_collections_desc": "Las colecciones (disponibles en Mastodon 4.6) te permiten crear tus propias listas seleccionadas de cuentas para recomendarlas a otras personas.", "empty_column.account_featured_self.pre_collections_desc": "Las colecciones (que llegarán en Mastodon 4.6) te permiten crear tus propias listas de cuentas seleccionadas para recomendarlas a otras personas.",
"empty_column.account_featured_unknown.no_collections_desc": "Esta cuenta aún no ha creado ninguna colección.", "empty_column.account_featured_self.showcase_accounts": "Destaca tus cuentas favoritas",
"empty_column.account_featured_self.showcase_accounts_desc": "Las colecciones son listas de cuentas seleccionadas que ayudan a otros a descubrir más del Fediverso.",
"empty_column.account_featured_unknown.other": "Esta cuenta no ha destacado nada todavía.", "empty_column.account_featured_unknown.other": "Esta cuenta no ha destacado nada todavía.",
"empty_column.account_hides_collections": "Este usuario ha elegido no hacer disponible esta información", "empty_column.account_hides_collections": "Este usuario ha elegido no hacer disponible esta información",
"empty_column.account_suspended": "Cuenta suspendida", "empty_column.account_suspended": "Cuenta suspendida",

View File

@ -45,6 +45,7 @@
"account.featured": "Destacado", "account.featured": "Destacado",
"account.featured.accounts": "Perfiles", "account.featured.accounts": "Perfiles",
"account.featured.collections": "Colecciones", "account.featured.collections": "Colecciones",
"account.featured.new_collection": "Nueva colección",
"account.field_overflow": "Mostrar contenido completo", "account.field_overflow": "Mostrar contenido completo",
"account.filters.all": "Toda la actividad", "account.filters.all": "Toda la actividad",
"account.filters.boosts_toggle": "Mostrar impulsos", "account.filters.boosts_toggle": "Mostrar impulsos",
@ -416,7 +417,6 @@
"column.about": "Acerca de", "column.about": "Acerca de",
"column.blocks": "Usuarios bloqueados", "column.blocks": "Usuarios bloqueados",
"column.bookmarks": "Marcadores", "column.bookmarks": "Marcadores",
"column.collections": "Mis colecciones",
"column.community": "Cronología local", "column.community": "Cronología local",
"column.create_list": "Crear lista", "column.create_list": "Crear lista",
"column.direct": "Menciones privadas", "column.direct": "Menciones privadas",
@ -432,7 +432,9 @@
"column.list_members": "Administrar miembros de la lista", "column.list_members": "Administrar miembros de la lista",
"column.lists": "Listas", "column.lists": "Listas",
"column.mutes": "Usuarios silenciados", "column.mutes": "Usuarios silenciados",
"column.my_collections": "Mis colecciones",
"column.notifications": "Notificaciones", "column.notifications": "Notificaciones",
"column.other_collections": "Colecciones de {name}",
"column.pins": "Publicaciones fijadas", "column.pins": "Publicaciones fijadas",
"column.public": "Cronología federada", "column.public": "Cronología federada",
"column_back_button.label": "Atrás", "column_back_button.label": "Atrás",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Seguir y añadir a la lista", "confirmations.follow_to_list.confirm": "Seguir y añadir a la lista",
"confirmations.follow_to_list.message": "Necesitas seguir a {name} para agregarlo a una lista.", "confirmations.follow_to_list.message": "Necesitas seguir a {name} para agregarlo a una lista.",
"confirmations.follow_to_list.title": "¿Seguir usuario?", "confirmations.follow_to_list.title": "¿Seguir usuario?",
"confirmations.hide_featured_tab.confirm": "Ocultar pestaña",
"confirmations.hide_featured_tab.intro": "Puedes cambiar esto en cualquier momento en <i>Editar perfil > Configuración de pestañas del perfil</i>.",
"confirmations.hide_featured_tab.message": "Esto esconderá la pestaña a los usuarios en {serverName} y otros servidores con la última versión de Mastodon. En otros lugares, puede variar.",
"confirmations.hide_featured_tab.title": "¿Ocultar la pestaña \"Destacado\"?",
"confirmations.logout.confirm": "Cerrar sesión", "confirmations.logout.confirm": "Cerrar sesión",
"confirmations.logout.message": "¿Seguro que quieres cerrar la sesión?", "confirmations.logout.message": "¿Seguro que quieres cerrar la sesión?",
"confirmations.logout.title": "¿Cerrar sesión?", "confirmations.logout.title": "¿Cerrar sesión?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Resultados de búsqueda", "emoji_button.search_results": "Resultados de búsqueda",
"emoji_button.symbols": "Símbolos", "emoji_button.symbols": "Símbolos",
"emoji_button.travel": "Viajes y lugares", "emoji_button.travel": "Viajes y lugares",
"empty_column.account_featured.other": "{acct} aún no ha destacado nada. ¿Sabías que puedes destacar las etiquetas que más usas e incluso las cuentas de tus amigos en tu perfil?", "empty_column.account_featured.other": "{acct} aún no ha destacado nada.",
"empty_column.account_featured_other.no_collections_desc": "{acct} aún no ha creado ninguna colección.",
"empty_column.account_featured_other.title": "Nada que ver aquí",
"empty_column.account_featured_self.no_collections": "Aún no hay colecciones",
"empty_column.account_featured_self.no_collections_button": "Crear una colección", "empty_column.account_featured_self.no_collections_button": "Crear una colección",
"empty_column.account_featured_self.no_collections_hide_tab": "Ocultar esta pestaña",
"empty_column.account_featured_self.pre_collections": "No te pierdas las Colecciones", "empty_column.account_featured_self.pre_collections": "No te pierdas las Colecciones",
"empty_column.account_featured_self.pre_collections_desc": "Las colecciones (disponibles en Mastodon 4.6) te permiten crear tus propias listas seleccionadas de cuentas para recomendarlas a otras personas.", "empty_column.account_featured_self.pre_collections_desc": "Las colecciones (disponibles a partir de Mastodon 4.6) te permiten crear tus propias listas de cuentas seleccionadas para recomendarlas a otros.",
"empty_column.account_featured_unknown.no_collections_desc": "Esta cuenta aún no ha creado ninguna colección.", "empty_column.account_featured_self.showcase_accounts": "Muestra tus cuentas favoritas",
"empty_column.account_featured_self.showcase_accounts_desc": "Las colecciones son listas de cuentas seleccionadas para ayudar a otros a descubrir más del Fediverso.",
"empty_column.account_featured_unknown.other": "Esta cuenta no ha destacado nada todavía.", "empty_column.account_featured_unknown.other": "Esta cuenta no ha destacado nada todavía.",
"empty_column.account_hides_collections": "Este usuario ha decidido no mostrar esta información", "empty_column.account_hides_collections": "Este usuario ha decidido no mostrar esta información",
"empty_column.account_suspended": "Cuenta suspendida", "empty_column.account_suspended": "Cuenta suspendida",

View File

@ -386,7 +386,6 @@
"column.about": "Teave", "column.about": "Teave",
"column.blocks": "Blokeeritud kasutajad", "column.blocks": "Blokeeritud kasutajad",
"column.bookmarks": "Järjehoidjad", "column.bookmarks": "Järjehoidjad",
"column.collections": "Minu kogumikud",
"column.community": "Kohalik ajajoon", "column.community": "Kohalik ajajoon",
"column.create_list": "Loo loend", "column.create_list": "Loo loend",
"column.direct": "Privaatsed mainimised", "column.direct": "Privaatsed mainimised",
@ -560,7 +559,6 @@
"emoji_button.search_results": "Otsitulemused", "emoji_button.search_results": "Otsitulemused",
"emoji_button.symbols": "Sümbolid", "emoji_button.symbols": "Sümbolid",
"emoji_button.travel": "Reisimine & kohad", "emoji_button.travel": "Reisimine & kohad",
"empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas teadsid, et oma profiilis saad esile tõsta rohkem kasutatud teemaviiteid või sõbra kasutajakontot?",
"empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks", "empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks",
"empty_column.account_suspended": "Konto kustutatud", "empty_column.account_suspended": "Konto kustutatud",
"empty_column.account_timeline": "Siin postitusi ei ole!", "empty_column.account_timeline": "Siin postitusi ei ole!",

View File

@ -355,7 +355,6 @@
"emoji_button.search_results": "Bilaketaren emaitzak", "emoji_button.search_results": "Bilaketaren emaitzak",
"emoji_button.symbols": "Sinboloak", "emoji_button.symbols": "Sinboloak",
"emoji_button.travel": "Bidaiak eta tokiak", "emoji_button.travel": "Bidaiak eta tokiak",
"empty_column.account_featured.other": "{acct}-ek ez du ezer nabarmendu oraindik. Ba al zenekien gehien erabiltzen dituzun traolak eta baita zure lagunen kontuak ere zure profilean nabarmendu ditzakezula?",
"empty_column.account_hides_collections": "Erabiltzaile honek informazio hau erabilgarri ez egotea aukeratu du.", "empty_column.account_hides_collections": "Erabiltzaile honek informazio hau erabilgarri ez egotea aukeratu du.",
"empty_column.account_suspended": "Kanporatutako kontua", "empty_column.account_suspended": "Kanporatutako kontua",
"empty_column.account_timeline": "Ez dago bidalketarik hemen!", "empty_column.account_timeline": "Ez dago bidalketarik hemen!",

View File

@ -355,7 +355,6 @@
"emoji_button.search_results": "نتایج جست‌وجو", "emoji_button.search_results": "نتایج جست‌وجو",
"emoji_button.symbols": "نمادها", "emoji_button.symbols": "نمادها",
"emoji_button.travel": "سفر و مکان", "emoji_button.travel": "سفر و مکان",
"empty_column.account_featured.other": "{acct} هنوز هیچ چیزی را پیشنهاد نکرده است. آیا می‌دانستید که می‌توانید برچسب‌هایی را که بیشتر استفاده می‌کنید و حتی حساب‌های کاربری دوستانتان را در نمایه خود پیشنهاد کنید؟",
"empty_column.account_hides_collections": "کاربر خواسته که این اطّلاعات در دسترس نباشند", "empty_column.account_hides_collections": "کاربر خواسته که این اطّلاعات در دسترس نباشند",
"empty_column.account_suspended": "حساب معلق شد", "empty_column.account_suspended": "حساب معلق شد",
"empty_column.account_timeline": "هیچ فرسته‌ای این‌جا نیست!", "empty_column.account_timeline": "هیچ فرسته‌ای این‌جا نیست!",

View File

@ -45,6 +45,7 @@
"account.featured": "Esittelyssä", "account.featured": "Esittelyssä",
"account.featured.accounts": "Profiilit", "account.featured.accounts": "Profiilit",
"account.featured.collections": "Kokoelmat", "account.featured.collections": "Kokoelmat",
"account.featured.new_collection": "Uusi kokoelma",
"account.field_overflow": "Näytä koko sisältö", "account.field_overflow": "Näytä koko sisältö",
"account.filters.all": "Kaikki toiminta", "account.filters.all": "Kaikki toiminta",
"account.filters.boosts_toggle": "Näytä tehostukset", "account.filters.boosts_toggle": "Näytä tehostukset",
@ -416,7 +417,6 @@
"column.about": "Tietoja", "column.about": "Tietoja",
"column.blocks": "Estetyt käyttäjät", "column.blocks": "Estetyt käyttäjät",
"column.bookmarks": "Kirjanmerkit", "column.bookmarks": "Kirjanmerkit",
"column.collections": "Omat kokoelmat",
"column.community": "Paikallinen aikajana", "column.community": "Paikallinen aikajana",
"column.create_list": "Luo lista", "column.create_list": "Luo lista",
"column.direct": "Yksityismaininnat", "column.direct": "Yksityismaininnat",
@ -432,7 +432,9 @@
"column.list_members": "Hallitse listan jäseniä", "column.list_members": "Hallitse listan jäseniä",
"column.lists": "Listat", "column.lists": "Listat",
"column.mutes": "Mykistetyt käyttäjät", "column.mutes": "Mykistetyt käyttäjät",
"column.my_collections": "Omat kokoelmat",
"column.notifications": "Ilmoitukset", "column.notifications": "Ilmoitukset",
"column.other_collections": "Käyttäjän {name} kokoelmat",
"column.pins": "Kiinnitetyt julkaisut", "column.pins": "Kiinnitetyt julkaisut",
"column.public": "Yleinen aikajana", "column.public": "Yleinen aikajana",
"column_back_button.label": "Takaisin", "column_back_button.label": "Takaisin",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Seuraa ja lisää listaan", "confirmations.follow_to_list.confirm": "Seuraa ja lisää listaan",
"confirmations.follow_to_list.message": "Sinun on seurattava käyttäjää {name}, jotta voit lisätä hänet listaan.", "confirmations.follow_to_list.message": "Sinun on seurattava käyttäjää {name}, jotta voit lisätä hänet listaan.",
"confirmations.follow_to_list.title": "Seurataanko käyttäjää?", "confirmations.follow_to_list.title": "Seurataanko käyttäjää?",
"confirmations.hide_featured_tab.confirm": "Piilota välilehti",
"confirmations.hide_featured_tab.intro": "Voit muuttaa tätä milloin tahansa kohdasta <i>Muokkaa profiilia > Profiilin välilehtien asetukset</i>.",
"confirmations.hide_featured_tab.message": "Tämä piilottaa välilehden palvelimen {serverName} käyttäjiltä ja muilta palvelimilta, jotka toimivat Mastodonin uusimmalla versiolla. Muut näkymät voivat vaihdella.",
"confirmations.hide_featured_tab.title": "Piilotetaanko Esittelyssä-välilehti?",
"confirmations.logout.confirm": "Kirjaudu ulos", "confirmations.logout.confirm": "Kirjaudu ulos",
"confirmations.logout.message": "Haluatko varmasti kirjautua ulos?", "confirmations.logout.message": "Haluatko varmasti kirjautua ulos?",
"confirmations.logout.title": "Kirjaudutaanko ulos?", "confirmations.logout.title": "Kirjaudutaanko ulos?",
@ -606,14 +612,13 @@
"emoji_button.search_results": "Hakutulokset", "emoji_button.search_results": "Hakutulokset",
"emoji_button.symbols": "Symbolit", "emoji_button.symbols": "Symbolit",
"emoji_button.travel": "Matkailu ja paikat", "emoji_button.travel": "Matkailu ja paikat",
"empty_column.account_featured.other": "{acct} ei esittele vielä mitään. Tiesitkö, että voit esitellä profiilissasi eniten käyttämiäsi aihetunnisteita ja jopa ystäviesi tilejä?", "empty_column.account_featured.other": "{acct} ei esittele vielä mitään.",
"empty_column.account_featured_other.no_collections_desc": "{acct} ei ole luonut vielä yhtään kokoelmaa.",
"empty_column.account_featured_other.title": "Täällä ei mitään nähtävää",
"empty_column.account_featured_self.no_collections": "Ei vielä kokoelmia",
"empty_column.account_featured_self.no_collections_button": "Luo kokoelma", "empty_column.account_featured_self.no_collections_button": "Luo kokoelma",
"empty_column.account_featured_self.no_collections_hide_tab": "Piilota tämä välilehti sen sijaan",
"empty_column.account_featured_self.pre_collections": "Ole valmiina kokoelmiin", "empty_column.account_featured_self.pre_collections": "Ole valmiina kokoelmiin",
"empty_column.account_featured_self.pre_collections_desc": "Kokoelmien (tulossa Mastodon 4.6:ssa) avulla voit luoda valikoidun listan muille suosittelemiasi tilejä.", "empty_column.account_featured_self.pre_collections_desc": "Kokoelmien (tulossa Mastodon 4.6:ssa) avulla voit luoda valikoidun listan muille suosittelemistasi tileistä.",
"empty_column.account_featured_unknown.no_collections_desc": "Tämä tili ei ole luonut vielä yhtään kokoelmaa.", "empty_column.account_featured_self.showcase_accounts": "Esittele suosikkitilejäsi",
"empty_column.account_featured_self.showcase_accounts_desc": "Kokoelmat ovat valikoituja listoja tileistä, jotka auttavat muita löytämään enemmän fediversumissa.",
"empty_column.account_featured_unknown.other": "Tämä tili ei esittele vielä mitään.", "empty_column.account_featured_unknown.other": "Tämä tili ei esittele vielä mitään.",
"empty_column.account_hides_collections": "Käyttäjä on päättänyt pitää nämä tiedot yksityisinä", "empty_column.account_hides_collections": "Käyttäjä on päättänyt pitää nämä tiedot yksityisinä",
"empty_column.account_suspended": "Tili jäädytetty", "empty_column.account_suspended": "Tili jäädytetty",

View File

@ -326,7 +326,6 @@
"column.about": "Um", "column.about": "Um",
"column.blocks": "Bannaðir brúkarar", "column.blocks": "Bannaðir brúkarar",
"column.bookmarks": "Bókamerki", "column.bookmarks": "Bókamerki",
"column.collections": "Míni søvn",
"column.community": "Lokal tíðarlinja", "column.community": "Lokal tíðarlinja",
"column.create_list": "Ger lista", "column.create_list": "Ger lista",
"column.direct": "Privatar umrøður", "column.direct": "Privatar umrøður",
@ -504,7 +503,6 @@
"emoji_button.search_results": "Leitiúrslit", "emoji_button.search_results": "Leitiúrslit",
"emoji_button.symbols": "Ímyndir", "emoji_button.symbols": "Ímyndir",
"emoji_button.travel": "Ferðing og støð", "emoji_button.travel": "Ferðing og støð",
"empty_column.account_featured.other": "{acct} hevur ikki tikið nakað fram enn. Visti tú, at tú kanst taka fram tey frámerki, tú brúkar mest, og sjálvt kontur hjá vinum tínum á vangan hjá tær?",
"empty_column.account_hides_collections": "Hesin brúkarin hevur valt, at hesar upplýsingarnar ikki skulu vera tøkar", "empty_column.account_hides_collections": "Hesin brúkarin hevur valt, at hesar upplýsingarnar ikki skulu vera tøkar",
"empty_column.account_suspended": "Kontan gjørd óvirkin", "empty_column.account_suspended": "Kontan gjørd óvirkin",
"empty_column.account_timeline": "Einki uppslag her!", "empty_column.account_timeline": "Einki uppslag her!",

View File

@ -45,6 +45,7 @@
"account.featured": "En vedette", "account.featured": "En vedette",
"account.featured.accounts": "Profils", "account.featured.accounts": "Profils",
"account.featured.collections": "Collections", "account.featured.collections": "Collections",
"account.featured.new_collection": "Nouvelle collection",
"account.field_overflow": "Voir tout", "account.field_overflow": "Voir tout",
"account.filters.all": "Toutes les activités", "account.filters.all": "Toutes les activités",
"account.filters.boosts_toggle": "Afficher les partages", "account.filters.boosts_toggle": "Afficher les partages",
@ -416,7 +417,6 @@
"column.about": "À propos", "column.about": "À propos",
"column.blocks": "Comptes bloqués", "column.blocks": "Comptes bloqués",
"column.bookmarks": "Signets", "column.bookmarks": "Signets",
"column.collections": "Mes collections",
"column.community": "Fil local", "column.community": "Fil local",
"column.create_list": "Créer une liste", "column.create_list": "Créer une liste",
"column.direct": "Mention privée", "column.direct": "Mention privée",
@ -502,6 +502,10 @@
"confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste", "confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste",
"confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.", "confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.",
"confirmations.follow_to_list.title": "Suivre l'utilisateur·rice ?", "confirmations.follow_to_list.title": "Suivre l'utilisateur·rice ?",
"confirmations.hide_featured_tab.confirm": "Cacher l'onglet",
"confirmations.hide_featured_tab.intro": "Vous pouvez modifier ce paramétrage à tout moment depuis <i>Modifier le profil > Paramètres de l'onglet du profil</i>.",
"confirmations.hide_featured_tab.message": "Cela masquera l'onglet pour les utilisateur·rice·s de {serverName} et des autres serveurs utilisant la dernière version de Mastodon. Pour les autres l'affichage peut varier.",
"confirmations.hide_featured_tab.title": "Cacher l'onglet « En vedette » ?",
"confirmations.logout.confirm": "Se déconnecter", "confirmations.logout.confirm": "Se déconnecter",
"confirmations.logout.message": "Voulez-vous vraiment vous déconnecter?", "confirmations.logout.message": "Voulez-vous vraiment vous déconnecter?",
"confirmations.logout.title": "Se déconnecter ?", "confirmations.logout.title": "Se déconnecter ?",
@ -606,14 +610,13 @@
"emoji_button.search_results": "Résultats", "emoji_button.search_results": "Résultats",
"emoji_button.symbols": "Symboles", "emoji_button.symbols": "Symboles",
"emoji_button.travel": "Voyage et lieux", "emoji_button.travel": "Voyage et lieux",
"empty_column.account_featured.other": "{acct} n'a pas encore mis de contenu en avant. Saviez-vous que vous pouviez mettre en avant les hashtags que vous utilisez le plus, et même les comptes de vos amis sur votre profil ?", "empty_column.account_featured.other": "{acct} n'a pas encore mis de contenu en avant.",
"empty_column.account_featured_other.no_collections_desc": "{acct} n'a pas encore créé de collection.",
"empty_column.account_featured_other.title": "Il n'y a rien à voir",
"empty_column.account_featured_self.no_collections": "Aucune collection pour le moment",
"empty_column.account_featured_self.no_collections_button": "Créer une collection", "empty_column.account_featured_self.no_collections_button": "Créer une collection",
"empty_column.account_featured_self.no_collections_hide_tab": "Cacher cet onglet",
"empty_column.account_featured_self.pre_collections": "Restez à l'écoute pour les collections", "empty_column.account_featured_self.pre_collections": "Restez à l'écoute pour les collections",
"empty_column.account_featured_self.pre_collections_desc": "Les collections (à venir dans Mastodon 4.6) vous permettent d'organiser vos propres listes de comptes à recommander aux autres.", "empty_column.account_featured_self.pre_collections_desc": "Les collections (à venir dans Mastodon 4.6) permettent d'organiser vos propres listes de comptes à recommander aux autres.",
"empty_column.account_featured_unknown.no_collections_desc": "Ce compte n'a pas encore créé de collection.", "empty_column.account_featured_self.showcase_accounts": "Présentez vos comptes préférés",
"empty_column.account_featured_self.showcase_accounts_desc": "Les collections sont des listes de comptes sélectionnés pour aider les autres à découvrir le Fédivers.",
"empty_column.account_featured_unknown.other": "Ce compte n'a mis aucun contenu en avant pour l'instant.", "empty_column.account_featured_unknown.other": "Ce compte n'a mis aucun contenu en avant pour l'instant.",
"empty_column.account_hides_collections": "Cet utilisateur·ice préfère ne pas rendre publiques ces informations", "empty_column.account_hides_collections": "Cet utilisateur·ice préfère ne pas rendre publiques ces informations",
"empty_column.account_suspended": "Compte suspendu", "empty_column.account_suspended": "Compte suspendu",

View File

@ -45,6 +45,7 @@
"account.featured": "En vedette", "account.featured": "En vedette",
"account.featured.accounts": "Profils", "account.featured.accounts": "Profils",
"account.featured.collections": "Collections", "account.featured.collections": "Collections",
"account.featured.new_collection": "Nouvelle collection",
"account.field_overflow": "Voir tout", "account.field_overflow": "Voir tout",
"account.filters.all": "Toutes les activités", "account.filters.all": "Toutes les activités",
"account.filters.boosts_toggle": "Afficher les partages", "account.filters.boosts_toggle": "Afficher les partages",
@ -416,7 +417,6 @@
"column.about": "À propos", "column.about": "À propos",
"column.blocks": "Comptes bloqués", "column.blocks": "Comptes bloqués",
"column.bookmarks": "Marque-pages", "column.bookmarks": "Marque-pages",
"column.collections": "Mes collections",
"column.community": "Fil local", "column.community": "Fil local",
"column.create_list": "Créer une liste", "column.create_list": "Créer une liste",
"column.direct": "Mentions privées", "column.direct": "Mentions privées",
@ -502,6 +502,10 @@
"confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste", "confirmations.follow_to_list.confirm": "Suivre et ajouter à la liste",
"confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.", "confirmations.follow_to_list.message": "Vous devez suivre {name} pour l'ajouter à une liste.",
"confirmations.follow_to_list.title": "Suivre l'utilisateur·rice ?", "confirmations.follow_to_list.title": "Suivre l'utilisateur·rice ?",
"confirmations.hide_featured_tab.confirm": "Cacher l'onglet",
"confirmations.hide_featured_tab.intro": "Vous pouvez modifier ce paramétrage à tout moment depuis <i>Modifier le profil > Paramètres de l'onglet du profil</i>.",
"confirmations.hide_featured_tab.message": "Cela masquera l'onglet pour les utilisateur·rice·s de {serverName} et des autres serveurs utilisant la dernière version de Mastodon. Pour les autres l'affichage peut varier.",
"confirmations.hide_featured_tab.title": "Cacher l'onglet « En vedette » ?",
"confirmations.logout.confirm": "Se déconnecter", "confirmations.logout.confirm": "Se déconnecter",
"confirmations.logout.message": "Voulez-vous vraiment vous déconnecter ?", "confirmations.logout.message": "Voulez-vous vraiment vous déconnecter ?",
"confirmations.logout.title": "Se déconnecter ?", "confirmations.logout.title": "Se déconnecter ?",
@ -606,14 +610,13 @@
"emoji_button.search_results": "Résultats de la recherche", "emoji_button.search_results": "Résultats de la recherche",
"emoji_button.symbols": "Symboles", "emoji_button.symbols": "Symboles",
"emoji_button.travel": "Voyage et lieux", "emoji_button.travel": "Voyage et lieux",
"empty_column.account_featured.other": "{acct} n'a pas encore mis de contenu en avant. Saviez-vous que vous pouviez mettre en avant les hashtags que vous utilisez le plus, et même les comptes de vos amis sur votre profil ?", "empty_column.account_featured.other": "{acct} n'a pas encore mis de contenu en avant.",
"empty_column.account_featured_other.no_collections_desc": "{acct} n'a pas encore créé de collection.",
"empty_column.account_featured_other.title": "Il n'y a rien à voir",
"empty_column.account_featured_self.no_collections": "Aucune collection pour le moment",
"empty_column.account_featured_self.no_collections_button": "Créer une collection", "empty_column.account_featured_self.no_collections_button": "Créer une collection",
"empty_column.account_featured_self.no_collections_hide_tab": "Cacher cet onglet",
"empty_column.account_featured_self.pre_collections": "Restez à l'écoute pour les collections", "empty_column.account_featured_self.pre_collections": "Restez à l'écoute pour les collections",
"empty_column.account_featured_self.pre_collections_desc": "Les collections (à venir dans Mastodon 4.6) vous permettent d'organiser vos propres listes de comptes à recommander aux autres.", "empty_column.account_featured_self.pre_collections_desc": "Les collections (à venir dans Mastodon 4.6) permettent d'organiser vos propres listes de comptes à recommander aux autres.",
"empty_column.account_featured_unknown.no_collections_desc": "Ce compte n'a pas encore créé de collection.", "empty_column.account_featured_self.showcase_accounts": "Présentez vos comptes préférés",
"empty_column.account_featured_self.showcase_accounts_desc": "Les collections sont des listes de comptes sélectionnés pour aider les autres à découvrir le Fédivers.",
"empty_column.account_featured_unknown.other": "Ce compte n'a mis aucun contenu en avant pour l'instant.", "empty_column.account_featured_unknown.other": "Ce compte n'a mis aucun contenu en avant pour l'instant.",
"empty_column.account_hides_collections": "Cet utilisateur·ice préfère ne pas rendre publiques ces informations", "empty_column.account_hides_collections": "Cet utilisateur·ice préfère ne pas rendre publiques ces informations",
"empty_column.account_suspended": "Compte suspendu", "empty_column.account_suspended": "Compte suspendu",

View File

@ -285,7 +285,6 @@
"emoji_button.search_results": "Sykresultaten", "emoji_button.search_results": "Sykresultaten",
"emoji_button.symbols": "Symboalen", "emoji_button.symbols": "Symboalen",
"emoji_button.travel": "Reizgje en lokaasjes", "emoji_button.travel": "Reizgje en lokaasjes",
"empty_column.account_featured.other": "{acct} hat noch neat útljochte. Wisten jo dat jo jo hashtags dyt jo it meast brûke, en sels de accounts fan dyn freonen fermelde kinne op jo profyl?",
"empty_column.account_hides_collections": "Dizze brûker hat derfoar keazen dizze ynformaasje net beskikber te meitsjen", "empty_column.account_hides_collections": "Dizze brûker hat derfoar keazen dizze ynformaasje net beskikber te meitsjen",
"empty_column.account_suspended": "Account beskoattele", "empty_column.account_suspended": "Account beskoattele",
"empty_column.account_timeline": "Hjir binne gjin berjochten!", "empty_column.account_timeline": "Hjir binne gjin berjochten!",

View File

@ -416,7 +416,6 @@
"column.about": "Maidir le", "column.about": "Maidir le",
"column.blocks": "Úsáideoirí blocáilte", "column.blocks": "Úsáideoirí blocáilte",
"column.bookmarks": "Leabharmharcanna", "column.bookmarks": "Leabharmharcanna",
"column.collections": "Mo bhailiúcháin",
"column.community": "Amlíne áitiúil", "column.community": "Amlíne áitiúil",
"column.create_list": "Cruthaigh liosta", "column.create_list": "Cruthaigh liosta",
"column.direct": "Luann príobháideach", "column.direct": "Luann príobháideach",
@ -581,7 +580,9 @@
"domain_pill.your_server": "Do theach digiteach, áit a bhfuil do phoist go léir ina gcónaí. Nach maith leat an ceann seo? Aistrigh freastalaithe am ar bith agus tabhair leat do leantóirí freisin.", "domain_pill.your_server": "Do theach digiteach, áit a bhfuil do phoist go léir ina gcónaí. Nach maith leat an ceann seo? Aistrigh freastalaithe am ar bith agus tabhair leat do leantóirí freisin.",
"domain_pill.your_username": "D'aitheantóir uathúil ar an bhfreastalaí seo. Is féidir teacht ar úsáideoirí leis an ainm úsáideora céanna ar fhreastalaithe éagsúla.", "domain_pill.your_username": "D'aitheantóir uathúil ar an bhfreastalaí seo. Is féidir teacht ar úsáideoirí leis an ainm úsáideora céanna ar fhreastalaithe éagsúla.",
"dropdown.empty": "Roghnaigh rogha", "dropdown.empty": "Roghnaigh rogha",
"email_subscriptions.email": "Ríomhphost",
"email_subscriptions.form.action": "Liostáil", "email_subscriptions.form.action": "Liostáil",
"email_subscriptions.form.bottom": "Faigh poist i do bhosca isteach gan cuntas Mastodon a chruthú. Díliostáil ag am ar bith. Le haghaidh tuilleadh eolais, féach ar an <a>Polasaí Príobháideachta</a>.",
"email_subscriptions.form.title": "Cláraigh le haghaidh nuashonruithe ríomhphoist ó {name}", "email_subscriptions.form.title": "Cláraigh le haghaidh nuashonruithe ríomhphoist ó {name}",
"email_subscriptions.submitted.lead": "Seiceáil do bhosca isteach le haghaidh ríomhphoist chun clárú le haghaidh nuashonruithe ríomhphoist a chríochnú.", "email_subscriptions.submitted.lead": "Seiceáil do bhosca isteach le haghaidh ríomhphoist chun clárú le haghaidh nuashonruithe ríomhphoist a chríochnú.",
"email_subscriptions.submitted.title": "Céim amháin eile", "email_subscriptions.submitted.title": "Céim amháin eile",
@ -604,14 +605,8 @@
"emoji_button.search_results": "Torthaí cuardaigh", "emoji_button.search_results": "Torthaí cuardaigh",
"emoji_button.symbols": "Comharthaí", "emoji_button.symbols": "Comharthaí",
"emoji_button.travel": "Taisteal ⁊ Áiteanna", "emoji_button.travel": "Taisteal ⁊ Áiteanna",
"empty_column.account_featured.other": "Níl aon rud feicthe ag {acct} go fóill. An raibh a fhios agat gur féidir leat na hashtags is mó a úsáideann tú, agus fiú cuntais do chairde, a chur ar do phróifíl?",
"empty_column.account_featured_other.no_collections_desc": "Níl aon bhailiúcháin cruthaithe ag {acct} go fóill.",
"empty_column.account_featured_other.title": "Níl aon rud le feiceáil anseo",
"empty_column.account_featured_self.no_collections": "Gan aon bhailiúcháin fós",
"empty_column.account_featured_self.no_collections_button": "Cruthaigh bailiúchán", "empty_column.account_featured_self.no_collections_button": "Cruthaigh bailiúchán",
"empty_column.account_featured_self.pre_collections": "Fan tiúnta le haghaidh bailiúcháin", "empty_column.account_featured_self.pre_collections": "Fan tiúnta le haghaidh bailiúcháin",
"empty_column.account_featured_self.pre_collections_desc": "Le bailiúcháin (atá ag teacht i Mastodon 4.6) is féidir leat do liostaí cuntas féin a chruthú le moladh do dhaoine eile.",
"empty_column.account_featured_unknown.no_collections_desc": "Níl aon bhailiúcháin cruthaithe ag an gcuntas seo go fóill.",
"empty_column.account_featured_unknown.other": "Níl aon rud le feiceáil ar an gcuntas seo go fóill.", "empty_column.account_featured_unknown.other": "Níl aon rud le feiceáil ar an gcuntas seo go fóill.",
"empty_column.account_hides_collections": "Roghnaigh an t-úsáideoir seo gan an fhaisnéis seo a chur ar fáil", "empty_column.account_hides_collections": "Roghnaigh an t-úsáideoir seo gan an fhaisnéis seo a chur ar fáil",
"empty_column.account_suspended": "Cuntas ar fionraí", "empty_column.account_suspended": "Cuntas ar fionraí",

View File

@ -416,7 +416,6 @@
"column.about": "Mu dhèidhinn", "column.about": "Mu dhèidhinn",
"column.blocks": "Cleachdaichean bacte", "column.blocks": "Cleachdaichean bacte",
"column.bookmarks": "Comharran-lìn", "column.bookmarks": "Comharran-lìn",
"column.collections": "Na cruinneachaidhean agam",
"column.community": "Loidhne-ama ionadail", "column.community": "Loidhne-ama ionadail",
"column.create_list": "Cruthaich liosta", "column.create_list": "Cruthaich liosta",
"column.direct": "Iomraidhean prìobhaideach", "column.direct": "Iomraidhean prìobhaideach",
@ -605,12 +604,7 @@
"emoji_button.search_results": "Toraidhean an luirg", "emoji_button.search_results": "Toraidhean an luirg",
"emoji_button.symbols": "Samhlaidhean", "emoji_button.symbols": "Samhlaidhean",
"emoji_button.travel": "Siubhal ⁊ àitichean", "emoji_button.travel": "Siubhal ⁊ àitichean",
"empty_column.account_featured.other": "Chan eil {acct} a brosnachadh dad fhathast. An robh fios agad gur urrainn dhut na tagaichean hais a chleachdas tu as trice agus fiù s cunntasan do charaidean a bhrosnachadh air a phròifil agad?",
"empty_column.account_featured_other.no_collections_desc": "Cha do chruthaich {acct} cruinneachadh sam bith fhathast.",
"empty_column.account_featured_other.title": "Chan eil dad ri fhaicinn an-seo",
"empty_column.account_featured_self.no_collections": "Chan eil cruinneachadh ann fhathast",
"empty_column.account_featured_self.no_collections_button": "Cruthaich cruinneachadh", "empty_column.account_featured_self.no_collections_button": "Cruthaich cruinneachadh",
"empty_column.account_featured_unknown.no_collections_desc": "Cha do chruthaich an cunntas seo cruinneachadh sam bith fhathast.",
"empty_column.account_featured_unknown.other": "Chan eil an cunntas seo a brosnachadh dad fhathast.", "empty_column.account_featured_unknown.other": "Chan eil an cunntas seo a brosnachadh dad fhathast.",
"empty_column.account_hides_collections": "Chuir an cleachdaiche seo roimhe nach eil am fiosrachadh seo ri fhaighinn", "empty_column.account_hides_collections": "Chuir an cleachdaiche seo roimhe nach eil am fiosrachadh seo ri fhaighinn",
"empty_column.account_suspended": "Chaidh an cunntas a chur à rèim", "empty_column.account_suspended": "Chaidh an cunntas a chur à rèim",

View File

@ -45,6 +45,7 @@
"account.featured": "Destacado", "account.featured": "Destacado",
"account.featured.accounts": "Perfís", "account.featured.accounts": "Perfís",
"account.featured.collections": "Coleccións", "account.featured.collections": "Coleccións",
"account.featured.new_collection": "Nova colección",
"account.field_overflow": "Mostrar contido completo", "account.field_overflow": "Mostrar contido completo",
"account.filters.all": "Toda actividade", "account.filters.all": "Toda actividade",
"account.filters.boosts_toggle": "Mostrar promocións", "account.filters.boosts_toggle": "Mostrar promocións",
@ -416,7 +417,6 @@
"column.about": "Sobre", "column.about": "Sobre",
"column.blocks": "Usuarias bloqueadas", "column.blocks": "Usuarias bloqueadas",
"column.bookmarks": "Marcadores", "column.bookmarks": "Marcadores",
"column.collections": "As miñas coleccións",
"column.community": "Cronoloxía local", "column.community": "Cronoloxía local",
"column.create_list": "Crear lista", "column.create_list": "Crear lista",
"column.direct": "Mencións privadas", "column.direct": "Mencións privadas",
@ -432,7 +432,9 @@
"column.list_members": "Xestionar membros da lista", "column.list_members": "Xestionar membros da lista",
"column.lists": "Listaxes", "column.lists": "Listaxes",
"column.mutes": "Usuarias acaladas", "column.mutes": "Usuarias acaladas",
"column.my_collections": "As miñas coleccións",
"column.notifications": "Notificacións", "column.notifications": "Notificacións",
"column.other_collections": "Coleccións de {name}",
"column.pins": "Publicacións fixadas", "column.pins": "Publicacións fixadas",
"column.public": "Cronoloxía federada", "column.public": "Cronoloxía federada",
"column_back_button.label": "Volver", "column_back_button.label": "Volver",
@ -502,6 +504,10 @@
"confirmations.follow_to_list.confirm": "Seguir e engadir á lista", "confirmations.follow_to_list.confirm": "Seguir e engadir á lista",
"confirmations.follow_to_list.message": "Tes que seguir a {name} para poder engadila a unha lista.", "confirmations.follow_to_list.message": "Tes que seguir a {name} para poder engadila a unha lista.",
"confirmations.follow_to_list.title": "Seguir á usuaria?", "confirmations.follow_to_list.title": "Seguir á usuaria?",
"confirmations.hide_featured_tab.confirm": "Ocultar pestana",
"confirmations.hide_featured_tab.intro": "Podes cambiar isto cando queiras nos axustes en <i>Editar perfil > pestana Perfil</i>.",
"confirmations.hide_featured_tab.message": "Vas ocultar a pestana para as usuarias de {serverName} e outros servidores que usen a última versión de Mastodon. Noutros lugares podería ser diferente.",
"confirmations.hide_featured_tab.title": "Ocultar a pestana «Destacado»?",
"confirmations.logout.confirm": "Pechar sesión", "confirmations.logout.confirm": "Pechar sesión",
"confirmations.logout.message": "Desexas pechar a sesión?", "confirmations.logout.message": "Desexas pechar a sesión?",
"confirmations.logout.title": "Pechar sesión?", "confirmations.logout.title": "Pechar sesión?",
@ -583,6 +589,7 @@
"dropdown.empty": "Escolle unha opción", "dropdown.empty": "Escolle unha opción",
"email_subscriptions.email": "Correo electrónico", "email_subscriptions.email": "Correo electrónico",
"email_subscriptions.form.action": "Subscribirse", "email_subscriptions.form.action": "Subscribirse",
"email_subscriptions.form.bottom": "Recibe publicacións no teu correo sen precisar crear unha conta de Mastodon. Date de baixa cando queiras. Máis información na <a>Directiva de privacidade</a>.",
"email_subscriptions.form.title": "Subscríbete para recibir actualizacións por correo de {name}", "email_subscriptions.form.title": "Subscríbete para recibir actualizacións por correo de {name}",
"email_subscriptions.submitted.lead": "Comproba a túa caixa de correo para completar a subscrición ás actualizacións por correo.", "email_subscriptions.submitted.lead": "Comproba a túa caixa de correo para completar a subscrición ás actualizacións por correo.",
"email_subscriptions.submitted.title": "Un último paso", "email_subscriptions.submitted.title": "Un último paso",
@ -605,14 +612,13 @@
"emoji_button.search_results": "Resultados da procura", "emoji_button.search_results": "Resultados da procura",
"emoji_button.symbols": "Símbolos", "emoji_button.symbols": "Símbolos",
"emoji_button.travel": "Viaxes e Lugares", "emoji_button.travel": "Viaxes e Lugares",
"empty_column.account_featured.other": "{acct} aínda non escolleu nada para destacar. Sabías que podes facer destacatar no teu perfil os cancelos que máis usas, incluso os perfís das túas amizades?", "empty_column.account_featured.other": "{acct} aínda non destacou nada.",
"empty_column.account_featured_other.no_collections_desc": "{acct} aínda non creou ningunha colección.",
"empty_column.account_featured_other.title": "Non hai nada que ver aquí",
"empty_column.account_featured_self.no_collections": "Aínda non tes coleccións",
"empty_column.account_featured_self.no_collections_button": "Crea unha colección", "empty_column.account_featured_self.no_collections_button": "Crea unha colección",
"empty_column.account_featured_self.no_collections_hide_tab": "Ocultar esta pestana entonces",
"empty_column.account_featured_self.pre_collections": "Van chegar as Coleccións", "empty_column.account_featured_self.pre_collections": "Van chegar as Coleccións",
"empty_column.account_featured_self.pre_collections_desc": "Coas Coleccións (a partir de Mastodon 4.6) podes crear as túas listas persoais de contas para recomendar a outras persoas.", "empty_column.account_featured_self.pre_collections_desc": "Coas Coleccións (a partir de Mastodon 4.6) podes crear as túas listas persoais de contas para recomendar a outras persoas.",
"empty_column.account_featured_unknown.no_collections_desc": "Esta conta aínda non creou ningunha colección.", "empty_column.account_featured_self.showcase_accounts": "Salienta as túas contas favoritas",
"empty_column.account_featured_self.showcase_accounts_desc": "As coleccións son listas de contas para que outras persoas as coñezan e desfruten aquí no Fediverso.",
"empty_column.account_featured_unknown.other": "Esta conta aínda non destacou nada.", "empty_column.account_featured_unknown.other": "Esta conta aínda non destacou nada.",
"empty_column.account_hides_collections": "A usuaria decideu non facer pública esta información", "empty_column.account_hides_collections": "A usuaria decideu non facer pública esta información",
"empty_column.account_suspended": "Conta suspendida", "empty_column.account_suspended": "Conta suspendida",

Some files were not shown because too many files have changed in this diff Show More