diff --git a/lib/account/models/user_label.dart b/lib/account/models/user_label.dart index 0334d1731..8b630a0ef 100644 --- a/lib/account/models/user_label.dart +++ b/lib/account/models/user_label.dart @@ -87,8 +87,29 @@ class UserLabel { } } + static Future> fetchAllUserLabels() async { + try { + final userLabelRows = await database.select(database.userLabels).get(); + return userLabelRows + .map((userLabel) => UserLabel( + id: userLabel.id.toString(), + username: userLabel.username, + label: userLabel.label, + )) + .toList(); + } catch (e) { + debugPrint(e.toString()); + return []; + } + } + /// Generates a username string that can be used to uniquely identify entries in the UserLabels table static String usernameFromParts(String username, String actorId) { return '$username@${fetchInstanceNameFromUrl(actorId)}'; } + + /// Splits a username generated by [usernameFromParts] back into name and instance + static ({String username, String instance}) partsFromUsername(String username) { + return (username: username.split('@')[0], instance: username.split('@')[1]); + } } diff --git a/lib/comment/cubit/create_comment_cubit.dart b/lib/comment/cubit/create_comment_cubit.dart index bd315f940..17b2c7a93 100644 --- a/lib/comment/cubit/create_comment_cubit.dart +++ b/lib/comment/cubit/create_comment_cubit.dart @@ -17,18 +17,27 @@ class CreateCommentCubit extends Cubit { emit(state.copyWith(status: CreateCommentStatus.initial, message: null)); } - Future uploadImage(String imageFile) async { + Future uploadImages(List imageFiles) async { Account? account = await fetchActiveProfileAccount(); if (account == null) return; PictrsApi pictrs = PictrsApi(account.instance!); + List urls = []; + emit(state.copyWith(status: CreateCommentStatus.imageUploadInProgress)); try { - PictrsUpload result = await pictrs.upload(filePath: imageFile, auth: account.jwt); - String url = "https://${account.instance!}/pictrs/image/${result.files[0].file}"; + for (String imageFile in imageFiles) { + PictrsUpload result = await pictrs.upload(filePath: imageFile, auth: account.jwt); + String url = "https://${account.instance!}/pictrs/image/${result.files[0].file}"; + + urls.add(url); + + // Add a delay between each upload to avoid possible rate limiting + await Future.wait(urls.map((url) => Future.delayed(const Duration(milliseconds: 500)))); + } - emit(state.copyWith(status: CreateCommentStatus.imageUploadSuccess, imageUrl: url)); + emit(state.copyWith(status: CreateCommentStatus.imageUploadSuccess, imageUrls: urls)); } catch (e) { emit(state.copyWith(status: CreateCommentStatus.imageUploadFailure, message: e.toString())); } diff --git a/lib/comment/cubit/create_comment_state.dart b/lib/comment/cubit/create_comment_state.dart index 80cac5ef9..54708075a 100644 --- a/lib/comment/cubit/create_comment_state.dart +++ b/lib/comment/cubit/create_comment_state.dart @@ -16,7 +16,7 @@ class CreateCommentState extends Equatable { const CreateCommentState({ this.status = CreateCommentStatus.initial, this.commentView, - this.imageUrl, + this.imageUrls, this.message, }); @@ -26,8 +26,8 @@ class CreateCommentState extends Equatable { /// The result of the created or edited comment final CommentView? commentView; - /// The url of the uploaded image - final String? imageUrl; + /// The urls of the uploaded images + final List? imageUrls; /// The info or error message to be displayed as a snackbar final String? message; @@ -35,17 +35,17 @@ class CreateCommentState extends Equatable { CreateCommentState copyWith({ required CreateCommentStatus status, CommentView? commentView, - String? imageUrl, + List? imageUrls, String? message, }) { return CreateCommentState( status: status, commentView: commentView ?? this.commentView, - imageUrl: imageUrl ?? this.imageUrl, + imageUrls: imageUrls ?? this.imageUrls, message: message ?? this.message, ); } @override - List get props => [status, commentView, imageUrl, message]; + List get props => [status, commentView, imageUrls, message]; } diff --git a/lib/comment/enums/comment_action.dart b/lib/comment/enums/comment_action.dart index 7627795d8..78672e506 100644 --- a/lib/comment/enums/comment_action.dart +++ b/lib/comment/enums/comment_action.dart @@ -6,6 +6,7 @@ enum CommentAction { save(permissionType: PermissionType.user), delete(permissionType: PermissionType.user), report(permissionType: PermissionType.user), + read(permissionType: PermissionType.user), // This is used for inbox items (replies/mentions) /// Moderator level post actions remove(permissionType: PermissionType.moderator), diff --git a/lib/comment/view/create_comment_page.dart b/lib/comment/view/create_comment_page.dart index b552b04d1..62a44d9aa 100644 --- a/lib/comment/view/create_comment_page.dart +++ b/lib/comment/view/create_comment_page.dart @@ -108,9 +108,12 @@ class _CreateCommentPageState extends State { /// The ID of the post we're responding to int? postId; - // The ID of the comment we're responding to + /// The ID of the comment we're responding to int? parentCommentId; + /// Contains the text that is currently being selected in the post/comment that we are replying to + String? replyViewSelection; + @override void initState() { super.initState(); @@ -256,7 +259,8 @@ class _CreateCommentPageState extends State { switch (state.status) { case CreateCommentStatus.imageUploadSuccess: - _bodyTextController.text = _bodyTextController.text.replaceRange(_bodyTextController.selection.end, _bodyTextController.selection.end, "![](${state.imageUrl})"); + String markdownImages = state.imageUrls?.map((url) => '![]($url)').join('\n\n') ?? ''; + _bodyTextController.text = _bodyTextController.text.replaceRange(_bodyTextController.selection.end, _bodyTextController.selection.end, markdownImages); break; case CreateCommentStatus.imageUploadFailure: showSnackbar(l10n.postUploadImageError, leadingIcon: Icons.warning_rounded, leadingIconColor: theme.colorScheme.errorContainer); @@ -328,6 +332,7 @@ class _CreateCommentPageState extends State { showExpandableButton: false, selectable: true, showReplyEditorButtons: true, + onSelectionChanged: (selection) => replyViewSelection = selection, ), ), ), @@ -354,6 +359,7 @@ class _CreateCommentPageState extends State { disableActions: true, selectable: true, showReplyEditorButtons: true, + onSelectionChanged: (selection) => replyViewSelection = selection, ), ), ), @@ -466,9 +472,10 @@ class _CreateCommentPageState extends State { customImageButtonAction: () async { if (state.status == CreateCommentStatus.imageUploadInProgress) return; - String imagePath = await selectImageToUpload(); - if (context.mounted) context.read().uploadImage(imagePath); + List imagesPath = await selectImagesToUpload(allowMultiple: true); + if (context.mounted) context.read().uploadImages(imagesPath); }, + getAlternativeSelection: () => replyViewSelection, ), ), ), diff --git a/lib/community/pages/create_post_page.dart b/lib/community/pages/create_post_page.dart index 07ef99f3f..2eb9c731c 100644 --- a/lib/community/pages/create_post_page.dart +++ b/lib/community/pages/create_post_page.dart @@ -174,7 +174,7 @@ class _CreatePostPageState extends State { if (widget.image != null) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - if (context.mounted) context.read().uploadImage(widget.image!.path, isPostImage: true); + if (context.mounted) context.read().uploadImages([widget.image!.path], isPostImage: true); }); } @@ -331,10 +331,11 @@ class _CreatePostPageState extends State { switch (state.status) { case CreatePostStatus.imageUploadSuccess: - _bodyTextController.text = _bodyTextController.text.replaceRange(_bodyTextController.selection.end, _bodyTextController.selection.end, "![](${state.imageUrl})"); + String markdownImages = state.imageUrls?.map((url) => '![]($url)').join('\n\n') ?? ''; + _bodyTextController.text = _bodyTextController.text.replaceRange(_bodyTextController.selection.end, _bodyTextController.selection.end, markdownImages); break; case CreatePostStatus.postImageUploadSuccess: - _urlTextController.text = state.imageUrl ?? ''; + _urlTextController.text = state.imageUrls?.first ?? ''; break; case CreatePostStatus.imageUploadFailure: case CreatePostStatus.postImageUploadFailure: @@ -460,8 +461,8 @@ class _CreatePostPageState extends State { onPressed: () async { if (state.status == CreatePostStatus.postImageUploadInProgress) return; - String imagePath = await selectImageToUpload(); - if (context.mounted) context.read().uploadImage(imagePath, isPostImage: true); + List imagesPath = await selectImagesToUpload(); + if (context.mounted) context.read().uploadImages(imagesPath, isPostImage: true); }, icon: state.status == CreatePostStatus.postImageUploadInProgress ? const SizedBox( @@ -604,8 +605,8 @@ class _CreatePostPageState extends State { customImageButtonAction: () async { if (state.status == CreatePostStatus.imageUploadInProgress) return; - String imagePath = await selectImageToUpload(); - if (context.mounted) context.read().uploadImage(imagePath, isPostImage: false); + List imagesPath = await selectImagesToUpload(allowMultiple: true); + if (context.mounted) context.read().uploadImages(imagesPath, isPostImage: false); }, ), ), diff --git a/lib/community/widgets/community_header.dart b/lib/community/widgets/community_header.dart index fddcc5a40..5255d5a6a 100644 --- a/lib/community/widgets/community_header.dart +++ b/lib/community/widgets/community_header.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; +import 'package:thunder/feed/bloc/feed_bloc.dart'; +import 'package:thunder/feed/utils/utils.dart'; import 'package:thunder/shared/avatars/community_avatar.dart'; import 'package:thunder/shared/full_name_widgets.dart'; @@ -29,6 +32,7 @@ class _CommunityHeaderState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final FeedBloc feedBloc = context.watch(); return Material( elevation: widget.showCommunitySidebar ? 5.0 : 0, @@ -115,7 +119,7 @@ class _CommunityHeaderState extends State { useDisplayName: false, ), const SizedBox(height: 8.0), - Row( + Wrap( children: [ IconText( icon: const Icon(Icons.people_rounded), @@ -126,6 +130,11 @@ class _CommunityHeaderState extends State { icon: const Icon(Icons.calendar_month_rounded), text: formatNumberToK(widget.getCommunityResponse.communityView.counts.usersActiveMonth), ), + const SizedBox(width: 8.0), + IconText( + icon: Icon(getSortIcon(feedBloc.state)), + text: getSortName(feedBloc.state), + ), ], ), ], diff --git a/lib/core/enums/full_name.dart b/lib/core/enums/full_name.dart index 09bcf9a3f..1b28530a8 100644 --- a/lib/core/enums/full_name.dart +++ b/lib/core/enums/full_name.dart @@ -109,6 +109,7 @@ Widget generateSampleUserFullNameWidget( NameThickness? instanceNameThickness, NameColor? instanceNameColor, TextStyle? textStyle, + bool? useDisplayName, }) => UserFullNameWidget( null, @@ -121,6 +122,7 @@ Widget generateSampleUserFullNameWidget( instanceNameThickness: instanceNameThickness, instanceNameColor: instanceNameColor, textStyle: textStyle, + useDisplayName: useDisplayName, ); String generateSampleCommunityFullName(FullNameSeparator separator, bool useDisplayName) => generateCommunityFullName( @@ -139,6 +141,7 @@ Widget generateSampleCommunityFullNameWidget( NameThickness? instanceNameThickness, NameColor? instanceNameColor, TextStyle? textStyle, + bool? useDisplayName, }) => CommunityFullNameWidget( null, @@ -151,6 +154,7 @@ Widget generateSampleCommunityFullNameWidget( instanceNameThickness: instanceNameThickness, instanceNameColor: instanceNameColor, textStyle: textStyle, + useDisplayName: useDisplayName, ); /// --- USERS --- diff --git a/lib/core/enums/local_settings.dart b/lib/core/enums/local_settings.dart index 1f8d3c299..05baf1f18 100644 --- a/lib/core/enums/local_settings.dart +++ b/lib/core/enums/local_settings.dart @@ -9,6 +9,7 @@ enum LocalSettingsCategories { floatingActionButton('floatingActionButton'), accessibility('Accessibility'), account('Account'), + userLabels('User Labels'), about('About'), debug('Debug'), theming('Theming'), @@ -253,6 +254,8 @@ enum LocalSettings { subCategory: LocalSettingsSubCategories.names, searchable: false), + userLabels(name: 'setting_user_labels', key: 'userLabels', category: LocalSettingsCategories.userLabels), + /// -------------------------- Gesture Related Settings -------------------------- // Sidebar Gesture Settings sidebarBottomNavBarSwipeGesture( @@ -476,6 +479,7 @@ extension LocalizationExt on AppLocalizations { 'videoAutoLoop': videoAutoLoop, 'videoAutoPlay': videoAutoPlay, 'videoDefaultPlaybackSpeed': videoDefaultPlaybackSpeed, + 'userLabels': userLabels, }; if (localizationMap.containsKey(key)) { diff --git a/lib/globals.dart b/lib/globals.dart index 79f1e8d7e..1ea292279 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1 +1 @@ -const String currentVersion = '0.5.0-1+58'; +const String currentVersion = '0.5.0-2+59'; diff --git a/lib/inbox/bloc/inbox_bloc.dart b/lib/inbox/bloc/inbox_bloc.dart index 5ecd3fb29..a1a2cb7ba 100644 --- a/lib/inbox/bloc/inbox_bloc.dart +++ b/lib/inbox/bloc/inbox_bloc.dart @@ -3,12 +3,15 @@ import 'package:equatable/equatable.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:thunder/account/models/account.dart'; +import 'package:thunder/comment/enums/comment_action.dart'; +import 'package:thunder/comment/utils/comment.dart'; import 'package:thunder/core/auth/helpers/fetch_account.dart'; import 'package:thunder/core/singletons/lemmy_client.dart'; +import 'package:thunder/inbox/enums/inbox_type.dart'; import 'package:thunder/utils/global_context.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; part 'inbox_event.dart'; part 'inbox_state.dart'; @@ -32,175 +35,199 @@ class InboxBloc extends Bloc { } void _init() { - on( - _getInboxEvent, - transformer: restartable(), - ); - on( - _markReplyAsReadEvent, - // Do not throttle mark as read because it's something - // a user might try to do in quick succession to multiple messages - // Do not use any transformer, because a throttleDroppable will only process the first request and restartable will only process the last. - ); - on( - _markMentionAsReadEvent, - // Do not throttle mark as read because it's something - // a user might try to do in quick succession to multiple messages - transformer: throttleDroppable(Duration.zero), - ); - on( - _createCommentEvent, - transformer: throttleDroppable(throttleDuration), - ); - on( - _markAllAsRead, - // Do not throttle mark as read because it's something - // a user might try to do in quick succession to multiple messages - transformer: throttleDroppable(Duration.zero), - ); + on(_getInboxEvent, transformer: restartable()); + on(_inboxItemActionEvent); + on(_markAllAsRead); } Future _getInboxEvent(GetInboxEvent event, emit) async { - int attemptCount = 0; int limit = 20; + Account? account = await fetchActiveProfileAccount(); + if (account?.jwt == null) { + return emit(state.copyWith( + status: InboxStatus.empty, + privateMessages: [], + mentions: [], + replies: [], + showUnreadOnly: !event.showAll, + inboxMentionPage: 1, + inboxReplyPage: 1, + inboxPrivateMessagePage: 1, + totalUnreadCount: 0, + repliesUnreadCount: 0, + mentionsUnreadCount: 0, + messagesUnreadCount: 0, + hasReachedInboxReplyEnd: true, + hasReachedInboxMentionEnd: true, + hasReachedInboxPrivateMessageEnd: true, + )); + } + try { - Object? exception; + LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; - Account? account = await fetchActiveProfileAccount(); + PrivateMessagesResponse? privateMessagesResponse; + GetPersonMentionsResponse? getPersonMentionsResponse; + GetRepliesResponse? getRepliesResponse; - while (attemptCount < 2) { - try { - LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + if (event.reset) { + emit(state.copyWith(status: InboxStatus.loading, errorMessage: '')); - if (event.reset) { - emit(state.copyWith(status: InboxStatus.loading, errorMessage: '')); - // Fetch all the things - PrivateMessagesResponse privateMessagesResponse = await lemmy.run( - GetPrivateMessages( + switch (event.inboxType) { + case InboxType.replies: + getRepliesResponse = await lemmy.run( + GetReplies( auth: account!.jwt!, unreadOnly: !event.showAll, limit: limit, + sort: event.commentSortType, page: 1, ), ); - - GetPersonMentionsResponse getPersonMentionsResponse = await lemmy.run( + break; + case InboxType.mentions: + getPersonMentionsResponse = await lemmy.run( GetPersonMentions( - auth: account.jwt!, + auth: account!.jwt!, unreadOnly: !event.showAll, - sort: CommentSortType.new_, + sort: event.commentSortType, limit: limit, page: 1, ), ); - - GetRepliesResponse getRepliesResponse = await lemmy.run( + break; + case InboxType.messages: + privateMessagesResponse = await lemmy.run( + GetPrivateMessages( + auth: account!.jwt!, + unreadOnly: !event.showAll, + limit: limit, + page: 1, + ), + ); + break; + case InboxType.all: + getRepliesResponse = await lemmy.run( GetReplies( - auth: account.jwt!, + auth: account!.jwt!, unreadOnly: !event.showAll, limit: limit, - sort: CommentSortType.new_, + sort: event.commentSortType, page: 1, ), ); - - GetUnreadCountResponse getUnreadCountResponse = await lemmy.run( - GetUnreadCount( + getPersonMentionsResponse = await lemmy.run( + GetPersonMentions( auth: account.jwt!, + unreadOnly: !event.showAll, + sort: event.commentSortType, + limit: limit, + page: 1, ), ); - - int totalUnreadCount = getUnreadCountResponse.privateMessages + getUnreadCountResponse.mentions + getUnreadCountResponse.replies; - - return emit( - state.copyWith( - status: InboxStatus.success, - privateMessages: cleanDeletedMessages(privateMessagesResponse.privateMessages), - mentions: cleanDeletedMentions(getPersonMentionsResponse.mentions), - replies: getRepliesResponse.replies.toList(), // Copy this list so that it is modifyable - showUnreadOnly: !event.showAll, - inboxMentionPage: 2, - inboxReplyPage: 2, - inboxPrivateMessagePage: 2, - totalUnreadCount: totalUnreadCount, - repliesUnreadCount: getUnreadCountResponse.replies, - mentionsUnreadCount: getUnreadCountResponse.mentions, - messagesUnreadCount: getUnreadCountResponse.privateMessages, - hasReachedInboxReplyEnd: getRepliesResponse.replies.isEmpty || getRepliesResponse.replies.length < limit, - hasReachedInboxMentionEnd: getPersonMentionsResponse.mentions.isEmpty || getPersonMentionsResponse.mentions.length < limit, - hasReachedInboxPrivateMessageEnd: privateMessagesResponse.privateMessages.isEmpty || privateMessagesResponse.privateMessages.length < limit, + privateMessagesResponse = await lemmy.run( + GetPrivateMessages( + auth: account.jwt!, + unreadOnly: !event.showAll, + limit: limit, + page: 1, ), ); - } + break; + default: + break; + } - // Prevent duplicate requests if we're done fetching - if (state.hasReachedInboxReplyEnd && state.hasReachedInboxMentionEnd && state.hasReachedInboxPrivateMessageEnd) return; - emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); + GetUnreadCountResponse getUnreadCountResponse = await lemmy.run(GetUnreadCount(auth: account!.jwt!)); + int totalUnreadCount = getUnreadCountResponse.privateMessages + getUnreadCountResponse.mentions + getUnreadCountResponse.replies; - // Fetch all the things - PrivateMessagesResponse privateMessagesResponse = await lemmy.run( - GetPrivateMessages( + return emit( + state.copyWith( + status: InboxStatus.success, + privateMessages: cleanDeletedMessages(privateMessagesResponse?.privateMessages ?? []), + mentions: cleanDeletedMentions(getPersonMentionsResponse?.mentions ?? []), + replies: getRepliesResponse?.replies.toList() ?? [], // Copy this list so that it is modifyable + showUnreadOnly: !event.showAll, + inboxMentionPage: 2, + inboxReplyPage: 2, + inboxPrivateMessagePage: 2, + totalUnreadCount: totalUnreadCount, + repliesUnreadCount: getUnreadCountResponse.replies, + mentionsUnreadCount: getUnreadCountResponse.mentions, + messagesUnreadCount: getUnreadCountResponse.privateMessages, + hasReachedInboxReplyEnd: getRepliesResponse?.replies.isEmpty == true || (getRepliesResponse?.replies.length ?? 0) < limit, + hasReachedInboxMentionEnd: getPersonMentionsResponse?.mentions.isEmpty == true || (getPersonMentionsResponse?.mentions.length ?? 0) < limit, + hasReachedInboxPrivateMessageEnd: privateMessagesResponse?.privateMessages.isEmpty == true || (privateMessagesResponse?.privateMessages.length ?? 0) < limit, + ), + ); + } + + // Prevent fetching if we're already fetching + if (state.status == InboxStatus.refreshing) return; + emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); + + switch (event.inboxType) { + case InboxType.replies: + if (state.hasReachedInboxReplyEnd) return; + + getRepliesResponse = await lemmy.run( + GetReplies( auth: account!.jwt!, - unreadOnly: !event.showAll, + unreadOnly: state.showUnreadOnly, limit: limit, - page: state.inboxPrivateMessagePage, + sort: event.commentSortType, + page: state.inboxReplyPage, ), ); + break; + case InboxType.mentions: + if (state.hasReachedInboxMentionEnd) return; - GetPersonMentionsResponse getPersonMentionsResponse = await lemmy.run( + getPersonMentionsResponse = await lemmy.run( GetPersonMentions( - auth: account.jwt!, - unreadOnly: !event.showAll, - sort: CommentSortType.new_, + auth: account!.jwt!, + unreadOnly: state.showUnreadOnly, + sort: event.commentSortType, limit: limit, page: state.inboxMentionPage, ), ); - - GetRepliesResponse getRepliesResponse = await lemmy.run( - GetReplies( - auth: account.jwt!, - unreadOnly: !event.showAll, + break; + case InboxType.messages: + if (state.hasReachedInboxPrivateMessageEnd) return; + privateMessagesResponse = await lemmy.run( + GetPrivateMessages( + auth: account!.jwt!, + unreadOnly: state.showUnreadOnly, limit: limit, - sort: CommentSortType.new_, - page: state.inboxReplyPage, - ), - ); - - List replies = List.from(state.replies)..addAll(getRepliesResponse.replies); - List mentions = List.from(state.mentions)..addAll(getPersonMentionsResponse.mentions); - List privateMessages = List.from(state.privateMessages)..addAll(privateMessagesResponse.privateMessages); - - return emit( - state.copyWith( - status: InboxStatus.success, - privateMessages: cleanDeletedMessages(privateMessages), - mentions: cleanDeletedMentions(mentions), - replies: replies, - showUnreadOnly: state.showUnreadOnly, - inboxMentionPage: state.inboxMentionPage + 1, - inboxReplyPage: state.inboxReplyPage + 1, - inboxPrivateMessagePage: state.inboxPrivateMessagePage + 1, - hasReachedInboxReplyEnd: getRepliesResponse.replies.isEmpty || getRepliesResponse.replies.length < limit, - hasReachedInboxMentionEnd: getPersonMentionsResponse.mentions.isEmpty || getPersonMentionsResponse.mentions.length < limit, - hasReachedInboxPrivateMessageEnd: privateMessagesResponse.privateMessages.isEmpty || privateMessagesResponse.privateMessages.length < limit, + page: state.inboxPrivateMessagePage, ), ); - } catch (e) { - exception = e; - attemptCount++; - } + break; + default: + break; } - emit(state.copyWith( - status: InboxStatus.failure, - errorMessage: exception.toString(), - totalUnreadCount: 0, - repliesUnreadCount: 0, - mentionsUnreadCount: 0, - messagesUnreadCount: 0, - )); + List replies = List.from(state.replies)..addAll(getRepliesResponse?.replies ?? []); + List mentions = List.from(state.mentions)..addAll(getPersonMentionsResponse?.mentions ?? []); + List privateMessages = List.from(state.privateMessages)..addAll(privateMessagesResponse?.privateMessages ?? []); + + return emit( + state.copyWith( + status: InboxStatus.success, + privateMessages: cleanDeletedMessages(privateMessages), + mentions: cleanDeletedMentions(mentions), + replies: replies, + showUnreadOnly: state.showUnreadOnly, + inboxMentionPage: state.inboxMentionPage + 1, + inboxReplyPage: state.inboxReplyPage + 1, + inboxPrivateMessagePage: state.inboxPrivateMessagePage + 1, + hasReachedInboxReplyEnd: getRepliesResponse?.replies.isEmpty == true || (getRepliesResponse?.replies.length ?? 0) < limit, + hasReachedInboxMentionEnd: getPersonMentionsResponse?.mentions.isEmpty == true || (getPersonMentionsResponse?.mentions.length ?? 0) < limit, + hasReachedInboxPrivateMessageEnd: privateMessagesResponse?.privateMessages.isEmpty == true || (privateMessagesResponse?.privateMessages.length ?? 0) < limit, + ), + ); } catch (e) { emit(state.copyWith( status: InboxStatus.failure, @@ -213,142 +240,228 @@ class InboxBloc extends Bloc { } } - Future _markReplyAsReadEvent(MarkReplyAsReadEvent event, emit) async { - try { - emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); + /// Handles comment related actions on a given item within the inbox + Future _inboxItemActionEvent(InboxItemActionEvent event, Emitter emit) async { + assert(!(event.commentReplyId == null && event.personMentionId == null)); + emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); - bool matchMarkedComment(CommentReplyView commentView) => commentView.commentReply.id == event.commentReplyId; + int existingIndex = -1; - // Optimistically remove the reply from the list - // or change the status (depending on whether we're showing all) - final CommentReplyView commentReplyView = state.replies.firstWhere(matchMarkedComment); - int index = state.replies.indexOf(commentReplyView); - if (event.showAll) { - state.replies[index] = commentReplyView.copyWith(commentReply: commentReplyView.commentReply.copyWith(read: event.read)); - } else if (event.read) { - state.replies.remove(commentReplyView); - } + CommentReplyView? existingCommentReplyView; + PersonMentionView? existingPersonMentionView; - Account? account = await fetchActiveProfileAccount(); - LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + if (event.commentReplyId != null) { + existingIndex = state.replies.indexWhere((element) => element.commentReply.id == event.commentReplyId); + existingCommentReplyView = state.replies[existingIndex]; + } else if (event.personMentionId != null) { + existingIndex = state.mentions.indexWhere((element) => element.personMention.id == event.personMentionId); + existingPersonMentionView = state.mentions[existingIndex]; + } - if (account?.jwt == null) { - return emit(state.copyWith(status: InboxStatus.success)); - } + if (existingCommentReplyView == null && existingPersonMentionView == null) return emit(state.copyWith(status: InboxStatus.failure)); + + /// Convert the reply or mention to a comment + CommentView? commentView; + + if (existingCommentReplyView != null) { + commentView = CommentView( + comment: existingCommentReplyView.comment, + creator: existingCommentReplyView.creator, + post: existingCommentReplyView.post, + community: existingCommentReplyView.community, + counts: existingCommentReplyView.counts, + creatorBannedFromCommunity: existingCommentReplyView.creatorBannedFromCommunity, + subscribed: existingCommentReplyView.subscribed, + saved: existingCommentReplyView.saved, + creatorBlocked: existingCommentReplyView.creatorBlocked, + myVote: existingCommentReplyView.myVote as int?, + ); + } else if (existingPersonMentionView != null) { + commentView = CommentView( + comment: existingPersonMentionView.comment, + creator: existingPersonMentionView.creator, + post: existingPersonMentionView.post, + community: existingPersonMentionView.community, + counts: existingPersonMentionView.counts, + creatorBannedFromCommunity: existingPersonMentionView.creatorBannedFromCommunity, + subscribed: existingPersonMentionView.subscribed, + saved: existingPersonMentionView.saved, + creatorBlocked: existingPersonMentionView.creatorBlocked, + myVote: existingPersonMentionView.myVote, + ); + } - CommentReplyResponse response = await lemmy.run(MarkCommentReplyAsRead( - auth: account!.jwt!, - commentReplyId: event.commentReplyId, - read: event.read, - )); + switch (event.action) { + case CommentAction.read: + try { + // Optimistically remove the reply from the list or change the status (depending on whether we're showing all) + if (existingCommentReplyView != null) { + if (!state.showUnreadOnly) { + state.replies[existingIndex] = existingCommentReplyView.copyWith(commentReply: existingCommentReplyView.commentReply.copyWith(read: event.value)); + } else if (event.value == true) { + state.replies.remove(existingCommentReplyView); + } + } else if (existingPersonMentionView != null) { + if (!state.showUnreadOnly) { + state.mentions[existingIndex] = existingPersonMentionView.copyWith(personMention: existingPersonMentionView.personMention.copyWith(read: event.value)); + } else if (event.value == true) { + state.mentions.remove(existingPersonMentionView); + } + } - if (response.commentReplyView.commentReply.read != event.read) { - return emit( - state.copyWith( - status: InboxStatus.failure, - errorMessage: event.read ? AppLocalizations.of(GlobalContext.context)!.errorMarkingReplyRead : AppLocalizations.of(GlobalContext.context)!.errorMarkingReplyUnread, - ), - ); - } + Account? account = await fetchActiveProfileAccount(); + LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; - GetUnreadCountResponse getUnreadCountResponse = await lemmy.run( - GetUnreadCount( - auth: account.jwt!, - ), - ); + if (account?.jwt == null) return emit(state.copyWith(status: InboxStatus.success)); - int totalUnreadCount = getUnreadCountResponse.privateMessages + getUnreadCountResponse.mentions + getUnreadCountResponse.replies; + if (existingCommentReplyView != null) { + await lemmy.run(MarkCommentReplyAsRead( + auth: account!.jwt!, + commentReplyId: event.commentReplyId!, + read: event.value, + )); + } else if (existingPersonMentionView != null) { + await lemmy.run(MarkPersonMentionAsRead( + auth: account!.jwt!, + personMentionId: event.personMentionId!, + read: event.value, + )); + } - return emit(state.copyWith( - status: InboxStatus.success, - replies: state.replies, - totalUnreadCount: totalUnreadCount, - repliesUnreadCount: getUnreadCountResponse.replies, - mentionsUnreadCount: getUnreadCountResponse.mentions, - messagesUnreadCount: getUnreadCountResponse.privateMessages, - inboxReplyMarkedAsRead: event.commentReplyId, - )); - } catch (e) { - return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); - } - } + GetUnreadCountResponse getUnreadCountResponse = await lemmy.run(GetUnreadCount(auth: account!.jwt!)); + int totalUnreadCount = getUnreadCountResponse.privateMessages + getUnreadCountResponse.mentions + getUnreadCountResponse.replies; + + return emit(state.copyWith( + status: InboxStatus.success, + totalUnreadCount: totalUnreadCount, + repliesUnreadCount: getUnreadCountResponse.replies, + mentionsUnreadCount: getUnreadCountResponse.mentions, + messagesUnreadCount: getUnreadCountResponse.privateMessages, + inboxReplyMarkedAsRead: event.commentReplyId, + )); + } catch (e) { + return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); + } + case CommentAction.vote: + try { + CommentView updatedCommentView = optimisticallyVoteComment(commentView!, event.value); - Future _markMentionAsReadEvent(MarkMentionAsReadEvent event, emit) async { - try { - emit(state.copyWith( - status: InboxStatus.loading, - privateMessages: state.privateMessages, - mentions: state.mentions, - replies: state.replies, - errorMessage: '', - )); + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView.copyWith(counts: updatedCommentView.counts, myVote: updatedCommentView.myVote); + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView.copyWith(counts: updatedCommentView.counts, myVote: updatedCommentView.myVote); + } - Account? account = await fetchActiveProfileAccount(); - LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + // Immediately set the status, and continue + emit(state.copyWith(status: InboxStatus.success)); + emit(state.copyWith(status: InboxStatus.refreshing)); - if (account?.jwt == null) { - return emit(state.copyWith(status: InboxStatus.success)); - } + await voteComment(commentView.comment.id, event.value).timeout(timeout, onTimeout: () { + // Restore the original comment if vote fails + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView; + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView; + } - await lemmy.run(MarkPersonMentionAsRead( - auth: account!.jwt!, - personMentionId: event.personMentionId, - read: event.read, - )); + throw Exception(AppLocalizations.of(GlobalContext.context)!.timeoutUpvoteComment); + }); - add(GetInboxEvent(showAll: !state.showUnreadOnly)); - } catch (e) { - return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); - } - } + return emit(state.copyWith(status: InboxStatus.success)); + } catch (e) { + return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); + } + case CommentAction.save: + try { + CommentView updatedCommentView = optimisticallySaveComment(commentView!, event.value); - Future _createCommentEvent(CreateInboxCommentReplyEvent event, Emitter emit) async { - try { - emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView.copyWith(saved: updatedCommentView.saved); + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView.copyWith(saved: updatedCommentView.saved); + } - Account? account = await fetchActiveProfileAccount(); - LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; + // Immediately set the status, and continue + emit(state.copyWith(status: InboxStatus.success)); + emit(state.copyWith(status: InboxStatus.refreshing)); - if (account?.jwt == null) { - return emit(state.copyWith(status: InboxStatus.failure, errorMessage: 'You are not logged in. Cannot create a comment')); - } + await saveComment(commentView.comment.id, event.value).timeout(timeout, onTimeout: () { + // Restore the original comment if saving fails + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView; + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView; + } - await lemmy.run(CreateComment( - auth: account!.jwt!, - content: event.content, - postId: event.postId, - parentId: event.parentCommentId, - )); + throw Exception(AppLocalizations.of(GlobalContext.context)!.timeoutSaveComment); + }); - add(GetInboxEvent(showAll: !state.showUnreadOnly)); - return emit(state.copyWith(status: InboxStatus.success)); - } catch (e) { - return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); + return emit(state.copyWith(status: InboxStatus.success)); + } catch (e) { + return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); + } + case CommentAction.delete: + try { + CommentView updatedCommentView = optimisticallyDeleteComment(commentView!, event.value); + + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView.copyWith(comment: updatedCommentView.comment); + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView.copyWith(comment: updatedCommentView.comment); + } + + // Immediately set the status, and continue + emit(state.copyWith(status: InboxStatus.success)); + emit(state.copyWith(status: InboxStatus.refreshing)); + + await deleteComment(commentView.comment.id, event.value).timeout(timeout, onTimeout: () { + // Restore the original comment if deleting fails + if (existingCommentReplyView != null) { + state.replies[existingIndex] = existingCommentReplyView; + } else if (existingPersonMentionView != null) { + state.mentions[existingIndex] = existingPersonMentionView; + } + + throw Exception(AppLocalizations.of(GlobalContext.context)!.timeoutErrorMessage); + }); + + return emit(state.copyWith(status: InboxStatus.success)); + } catch (e) { + return emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); + } + default: + return emit(state.copyWith(status: InboxStatus.failure, errorMessage: AppLocalizations.of(GlobalContext.context)!.unexpectedError)); } } Future _markAllAsRead(MarkAllAsReadEvent event, emit) async { try { - emit(state.copyWith( - status: InboxStatus.refreshing, - errorMessage: '', - )); + emit(state.copyWith(status: InboxStatus.refreshing, errorMessage: '')); + Account? account = await fetchActiveProfileAccount(); LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3; - if (account?.jwt == null) { - return emit(state.copyWith(status: InboxStatus.success)); - } - await lemmy.run(MarkAllAsRead( - auth: account!.jwt!, - )); + if (account?.jwt == null) return emit(state.copyWith(status: InboxStatus.success)); + await lemmy.run(MarkAllAsRead(auth: account!.jwt!)); - add(GetInboxEvent(reset: true, showAll: !state.showUnreadOnly)); - } catch (e) { - emit(state.copyWith( - status: InboxStatus.failure, - errorMessage: e.toString(), + // Update all the replies, mentions, and messages to be read locally + List updatedReplies = state.replies.map((commentReplyView) => commentReplyView.copyWith(commentReply: commentReplyView.commentReply.copyWith(read: true))).toList(); + List updatedMentions = state.mentions.map((personMentionView) => personMentionView.copyWith(personMention: personMentionView.personMention.copyWith(read: true))).toList(); + List updatedPrivateMessages = + state.privateMessages.map((privateMessageView) => privateMessageView.copyWith(privateMessage: privateMessageView.privateMessage.copyWith(read: true))).toList(); + + return emit(state.copyWith( + status: InboxStatus.success, + replies: updatedReplies, + mentions: updatedMentions, + privateMessages: updatedPrivateMessages, + totalUnreadCount: 0, + repliesUnreadCount: 0, + mentionsUnreadCount: 0, + messagesUnreadCount: 0, )); + } catch (e) { + emit(state.copyWith(status: InboxStatus.failure, errorMessage: e.toString())); } } diff --git a/lib/inbox/bloc/inbox_event.dart b/lib/inbox/bloc/inbox_event.dart index b8970b367..936e9be4d 100644 --- a/lib/inbox/bloc/inbox_event.dart +++ b/lib/inbox/bloc/inbox_event.dart @@ -8,33 +8,35 @@ abstract class InboxEvent extends Equatable { } class GetInboxEvent extends InboxEvent { + /// The inbox type to fetch from. If null, it will not fetch anything. If [reset] is true, it will only fetch the total unread counts + final InboxType? inboxType; + + /// If true, it will fetch read and unread messages final bool showAll; + + /// If true, it will reset the inbox and re-fetch everything depending on [inboxType] final bool reset; - const GetInboxEvent({this.showAll = false, this.reset = false}); -} + /// The comment sort type to use for replies/mentions + final CommentSortType commentSortType; -class MarkReplyAsReadEvent extends InboxEvent { - final int commentReplyId; - final bool read; - final bool showAll; - - const MarkReplyAsReadEvent({required this.commentReplyId, required this.read, required this.showAll}); + const GetInboxEvent({this.inboxType, this.showAll = false, this.reset = false, this.commentSortType = CommentSortType.new_}); } -class MarkMentionAsReadEvent extends InboxEvent { - final int personMentionId; - final bool read; +class InboxItemActionEvent extends InboxEvent { + /// The action to perform on the inbox item. This is generally a comment. + final CommentAction action; - const MarkMentionAsReadEvent({required this.personMentionId, required this.read}); -} + /// The id of the comment reply. Only one of [commentReplyId] or [personMentionId] should be set + final int? commentReplyId; + + /// The id of the person mention reply. Only one of [commentReplyId] or [personMentionId] should be set + final int? personMentionId; -class CreateInboxCommentReplyEvent extends InboxEvent { - final String content; - final int postId; - final int parentCommentId; + /// The value to pass to the action + final dynamic value; - const CreateInboxCommentReplyEvent({required this.content, required this.postId, required this.parentCommentId}); + const InboxItemActionEvent({required this.action, this.commentReplyId, this.personMentionId, this.value}); } class MarkAllAsReadEvent extends InboxEvent {} diff --git a/lib/inbox/enums/inbox_type.dart b/lib/inbox/enums/inbox_type.dart new file mode 100644 index 000000000..d64d3784f --- /dev/null +++ b/lib/inbox/enums/inbox_type.dart @@ -0,0 +1 @@ +enum InboxType { replies, mentions, messages, all } diff --git a/lib/inbox/pages/inbox_page.dart b/lib/inbox/pages/inbox_page.dart index a43ea5a85..aacc93e00 100644 --- a/lib/inbox/pages/inbox_page.dart +++ b/lib/inbox/pages/inbox_page.dart @@ -1,21 +1,23 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; -import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import "package:flutter_gen/gen_l10n/app_localizations.dart"; +import 'package:lemmy_api_client/v3.dart'; + import 'package:thunder/core/auth/bloc/auth_bloc.dart'; +import 'package:thunder/core/singletons/lemmy_client.dart'; import 'package:thunder/inbox/bloc/inbox_bloc.dart'; - -import 'package:thunder/inbox/widgets/inbox_categories_widget.dart'; +import 'package:thunder/inbox/enums/inbox_type.dart'; import 'package:thunder/inbox/widgets/inbox_mentions_view.dart'; import 'package:thunder/inbox/widgets/inbox_private_messages_view.dart'; import 'package:thunder/inbox/widgets/inbox_replies_view.dart'; -import 'package:thunder/post/bloc/post_bloc.dart'; +import 'package:thunder/shared/comment_sort_picker.dart'; import 'package:thunder/shared/dialogs.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:thunder/shared/snackbar.dart'; +import 'package:thunder/shared/thunder_popup_menu_item.dart'; -enum InboxType { replies, mentions, messages } - +/// A widget that displays the user's inbox replies, mentions, and private messages. class InboxPage extends StatefulWidget { const InboxPage({super.key}); @@ -23,168 +25,201 @@ class InboxPage extends StatefulWidget { State createState() => _InboxPageState(); } -class _InboxPageState extends State { +class _InboxPageState extends State with SingleTickerProviderStateMixin { + /// The controller for the tab bar used for switching between inbox types. + late TabController tabController; + + /// The global key for the nested scroll view. + final GlobalKey nestedScrollViewKey = GlobalKey(); + + /// Whether to show all inbox mentions, replies, and private messages or not bool showAll = false; - InboxType? inboxType = InboxType.replies; - final _scrollController = ScrollController(initialScrollOffset: 0); + + /// The current inbox sort type. This only applies to replies and mentions, since messages does not have a sort type + CommentSortType commentSortType = CommentSortType.new_; + + /// The current account id. If this changes, and the current view is active, reload the view + int? accountId; + + InboxType get inboxType => tabController.index == 0 + ? InboxType.replies + : tabController.index == 1 + ? InboxType.mentions + : InboxType.messages; @override void initState() { - _scrollController.addListener(_onScroll); super.initState(); + tabController = TabController(vsync: this, length: 3); + accountId = context.read().state.account?.userId; + + WidgetsBinding.instance.addPostFrameCallback( + (_) => nestedScrollViewKey.currentState!.innerController.addListener(() { + ScrollController controller = nestedScrollViewKey.currentState!.innerController; + + if (controller.position.pixels >= controller.position.maxScrollExtent * 0.7 && context.read().state.status == InboxStatus.success) { + context.read().add(GetInboxEvent(inboxType: inboxType)); + } + }), + ); + + context.read().add(GetInboxEvent(inboxType: inboxType, reset: true)); } @override void dispose() { - _scrollController.dispose(); + tabController.dispose(); super.dispose(); } - void _onScroll() { - if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent * 0.7) { - context.read().add(const GetInboxEvent()); - } + /// Displays the sort options bottom sheet for comments, since replies and mentions are technically comments + void showSortBottomSheet() { + final AppLocalizations l10n = AppLocalizations.of(context)!; + + showModalBottomSheet( + showDragHandle: true, + context: context, + builder: (builderContext) => CommentSortPicker( + title: l10n.sortOptions, + onSelect: (selected) async { + setState(() => commentSortType = selected.payload); + context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: showAll, commentSortType: selected.payload)); + }, + previouslySelected: commentSortType, + minimumVersion: LemmyClient.instance.version, + ), + ); } @override Widget build(BuildContext context) { - final theme = Theme.of(context); - final AppLocalizations l10n = AppLocalizations.of(context)!; + final l10n = AppLocalizations.of(context)!; return Scaffold( - appBar: AppBar( - toolbarHeight: 80.0, - centerTitle: false, - title: AutoSizeText(l10n.inbox, style: theme.textTheme.titleLarge), - actions: [ - IconButton( - icon: Icon( - Icons.checklist, - semanticLabel: l10n.readAll, - ), - onPressed: () async { - await showThunderDialog( - context: context, - title: l10n.confirmMarkAllAsReadTitle, - contentText: l10n.confirmMarkAllAsReadBody, - onSecondaryButtonPressed: (dialogContext) { - Navigator.of(dialogContext).pop(); - }, - secondaryButtonText: l10n.cancel, - onPrimaryButtonPressed: (dialogContext, _) { - Navigator.of(dialogContext).pop(); - context.read().add(MarkAllAsReadEvent()); - }, - primaryButtonText: l10n.markAllAsRead, - ); - }, - ), - IconButton( - icon: Icon( - Icons.refresh_rounded, - semanticLabel: l10n.refresh, - ), - onPressed: () { - context.read().add(GetInboxEvent(reset: true, showAll: showAll)); - }, - ), - FilterChip( - shape: const StadiumBorder(), - visualDensity: VisualDensity.compact, - label: Text(l10n.showAll), - selected: showAll, - onSelected: (bool selected) { - setState(() => showAll = !showAll); - context.read().add(GetInboxEvent(reset: true, showAll: selected)); - }, - ), - const SizedBox(width: 16.0), - ], - bottom: PreferredSize( - preferredSize: const Size.fromHeight(45.0), - child: BlocBuilder( - builder: (context, state) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Center( - child: InboxCategoryWidget( - inboxType: inboxType, - onSelected: (InboxType? selected) { - _scrollController.animateTo(0, duration: const Duration(milliseconds: 150), curve: Curves.easeInOut); - setState(() { - inboxType = selected; - }); - }, - unreadCounts: { - InboxType.replies: state.repliesUnreadCount, - InboxType.mentions: state.mentionsUnreadCount, - InboxType.messages: state.messagesUnreadCount, - }, + body: BlocConsumer( + listener: (context, state) { + if (state.status == InboxStatus.initial || state.status == InboxStatus.loading || state.status == InboxStatus.empty) { + nestedScrollViewKey.currentState?.innerController.jumpTo(0); + + int? newAccountId = context.read().state.account?.userId; + + if (newAccountId != accountId) { + accountId = newAccountId; + context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: showAll)); + } + } + + if (state.errorMessage?.isNotEmpty == true) { + showSnackbar( + state.errorMessage!, + trailingIcon: Icons.refresh_rounded, + trailingAction: () => context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: showAll)), + ); + } + }, + builder: (context, state) { + return NestedScrollView( + key: nestedScrollViewKey, + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: SliverAppBar( + pinned: true, + centerTitle: false, + toolbarHeight: 70.0, + forceElevated: innerBoxIsScrolled, + title: Text(l10n.inbox), + actions: [ + IconButton( + icon: Icon(Icons.refresh_rounded, semanticLabel: l10n.refresh), + onPressed: () => context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: showAll)), + ), + IconButton(onPressed: () => showSortBottomSheet(), icon: Icon(Icons.sort, semanticLabel: l10n.sortBy)), + PopupMenuButton( + onOpened: () => HapticFeedback.mediumImpact(), + itemBuilder: (context) => [ + ThunderPopupMenuItem( + onTap: () async { + HapticFeedback.mediumImpact(); + await showThunderDialog( + context: context, + title: l10n.confirmMarkAllAsReadTitle, + contentText: l10n.confirmMarkAllAsReadBody, + onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(), + secondaryButtonText: l10n.cancel, + onPrimaryButtonPressed: (dialogContext, _) { + Navigator.of(dialogContext).pop(); + context.read().add(MarkAllAsReadEvent()); + }, + primaryButtonText: l10n.markAllAsRead, + ); + }, + icon: Icons.checklist, + title: l10n.markAllAsRead, + ), + ThunderPopupMenuItem( + onTap: () async { + HapticFeedback.mediumImpact(); + context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: !showAll)); + setState(() => showAll = !showAll); + }, + icon: showAll ? Icons.mark_as_unread : Icons.all_inbox_rounded, + title: showAll ? l10n.showUnreadOnly : l10n.showAll, + ), + ], + ), + ], + bottom: TabBar( + controller: tabController, + onTap: (index) { + context.read().add(GetInboxEvent(inboxType: inboxType, reset: true, showAll: showAll)); + }, + tabs: [ + Tab( + child: Wrap( + spacing: 4.0, + children: [ + Text(l10n.reply(10)), + if (state.repliesUnreadCount > 0) Badge(label: Text(state.repliesUnreadCount > 99 ? '99+' : state.repliesUnreadCount.toString())), + ], + ), + ), + Tab( + child: Wrap( + spacing: 4.0, + children: [ + Text(l10n.mention(10)), + if (state.mentionsUnreadCount > 0) Badge(label: Text(state.mentionsUnreadCount > 99 ? '99+' : state.mentionsUnreadCount.toString())), + ], + ), + ), + Tab( + child: Wrap( + spacing: 4.0, + children: [ + Text(l10n.message(10)), + if (state.messagesUnreadCount > 0) Badge(label: Text(state.messagesUnreadCount > 99 ? '99+' : state.messagesUnreadCount.toString())), + ], + ), + ), + ], + ), ), ), - ); + ]; }, - ), - ), - ), - body: BlocProvider( - create: (context) => PostBloc(), - child: RefreshIndicator( - onRefresh: () async { - context.read().add(GetInboxEvent(reset: true, showAll: showAll)); - }, - child: SingleChildScrollView( - controller: _scrollController, - physics: const AlwaysScrollableScrollPhysics(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + body: TabBarView( + controller: tabController, + physics: const NeverScrollableScrollPhysics(), children: [ - BlocBuilder( - builder: (context, InboxState state) { - if (context.read().state.isLoggedIn == false) { - return Align(alignment: Alignment.topCenter, child: Text(l10n.loginToSeeInbox, style: theme.textTheme.titleMedium)); - } - - switch (state.status) { - case InboxStatus.initial: - context.read().add(const GetInboxEvent(reset: true)); - case InboxStatus.loading: - return const Padding( - padding: EdgeInsets.all(24.0), - child: Align( - alignment: Alignment.center, - child: SizedBox( - width: 40, - height: 40, - child: CircularProgressIndicator(), - ), - ), - ); - case InboxStatus.refreshing: - case InboxStatus.success: - case InboxStatus.failure: - if (state.errorMessage?.isNotEmpty == true) { - showSnackbar( - state.errorMessage!, - trailingIcon: Icons.refresh_rounded, - trailingAction: () => context.read().add(GetInboxEvent(reset: true, showAll: showAll)), - ); - } - - if (inboxType == InboxType.mentions) return InboxMentionsView(mentions: state.mentions); - if (inboxType == InboxType.messages) return InboxPrivateMessagesView(privateMessages: state.privateMessages); - if (inboxType == InboxType.replies) return InboxRepliesView(replies: state.replies, showAll: showAll); - case InboxStatus.empty: - return Center(child: Text(l10n.emptyInbox)); - } - - return Container(); - }, - ), + InboxRepliesView(replies: state.replies), + InboxMentionsView(mentions: state.mentions), + InboxPrivateMessagesView(privateMessages: state.privateMessages), ], ), - ), - ), + ); + }, ), ); } diff --git a/lib/inbox/widgets/inbox_categories_widget.dart b/lib/inbox/widgets/inbox_categories_widget.dart deleted file mode 100644 index 1b4e96c9d..000000000 --- a/lib/inbox/widgets/inbox_categories_widget.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:thunder/inbox/inbox.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - -class InboxCategory { - final InboxType type; - final String title; - final IconData icon; - - InboxCategory({required this.type, required this.title, required this.icon}); -} - -class InboxCategoryWidget extends StatelessWidget { - const InboxCategoryWidget({ - super.key, - required this.onSelected, - required this.inboxType, - required this.unreadCounts, - }); - final ValueChanged onSelected; - final InboxType? inboxType; - final Map unreadCounts; - - @override - Widget build(BuildContext context) { - List inboxCategories = [ - InboxCategory( - type: InboxType.replies, - title: AppLocalizations.of(context)!.reply(10), - icon: Icons.comment_bank_rounded, - ), - InboxCategory( - type: InboxType.mentions, - title: AppLocalizations.of(context)!.mention(10), - icon: Icons.comment_bank_rounded, - ), - InboxCategory( - type: InboxType.messages, - title: AppLocalizations.of(context)!.message(10), - icon: Icons.message_rounded, - ), - ]; - return Wrap( - spacing: 5.0, - children: inboxCategories.map((InboxCategory inboxCategory) { - return ChoiceChip( - showCheckmark: false, - label: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(inboxCategory.title), - if ((unreadCounts[inboxCategory.type] ?? 0) > 0) ...[ - const SizedBox(width: 5), - Badge(label: Text((unreadCounts[inboxCategory.type] ?? 0) > 99 ? '99+' : unreadCounts[inboxCategory.type]?.toString() ?? '')), - ], - ], - ), - selected: inboxType == inboxCategory.type, - onSelected: (selected) => onSelected(inboxCategory.type), - ); - }).toList(), - ); - } -} diff --git a/lib/inbox/widgets/inbox_mentions_view.dart b/lib/inbox/widgets/inbox_mentions_view.dart index a226193ef..0ae6f0f7b 100644 --- a/lib/inbox/widgets/inbox_mentions_view.dart +++ b/lib/inbox/widgets/inbox_mentions_view.dart @@ -1,181 +1,94 @@ -// Dart imports -import 'dart:io'; - // Flutter imports import 'package:flutter/material.dart'; // Package imports import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; -import 'package:swipeable_page_route/swipeable_page_route.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // Project imports -import 'package:thunder/account/bloc/account_bloc.dart'; -import 'package:thunder/comment/utils/navigate_comment.dart'; +import 'package:thunder/comment/enums/comment_action.dart'; import 'package:thunder/core/auth/bloc/auth_bloc.dart'; -import 'package:thunder/core/models/post_view_media.dart'; -import 'package:thunder/feed/utils/utils.dart'; import 'package:thunder/feed/view/feed_page.dart'; import 'package:thunder/inbox/bloc/inbox_bloc.dart'; -import 'package:thunder/post/bloc/post_bloc.dart'; -import 'package:thunder/post/pages/legacy_post_page.dart'; -import 'package:thunder/shared/common_markdown_body.dart'; -import 'package:thunder/shared/full_name_widgets.dart'; -import 'package:thunder/shared/text/scalable_text.dart'; -import 'package:thunder/thunder/bloc/thunder_bloc.dart'; -import 'package:thunder/utils/date_time.dart'; -import 'package:thunder/utils/instance.dart'; -import 'package:thunder/utils/swipe.dart'; +import 'package:thunder/shared/comment_reference.dart'; +import 'package:thunder/shared/divider.dart'; -class InboxMentionsView extends StatelessWidget { +extension on PersonMentionView { + CommentView toCommentView() { + return CommentView( + comment: comment, + creator: creator, + post: post, + community: community, + counts: counts, + creatorBannedFromCommunity: creatorBannedFromCommunity, + subscribed: subscribed, + saved: saved, + creatorBlocked: creatorBlocked, + ); + } +} + +class InboxMentionsView extends StatefulWidget { final List mentions; const InboxMentionsView({super.key, this.mentions = const []}); @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final AppLocalizations l10n = AppLocalizations.of(context)!; - final ThunderState thunderState = context.read().state; - - if (mentions.isEmpty) { - return Align(alignment: Alignment.topCenter, heightFactor: (MediaQuery.of(context).size.height / 27), child: const Text('No mentions')); - } + State createState() => _InboxMentionsViewState(); +} - return ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: mentions.length, - itemBuilder: (context, index) { - return Card( - clipBehavior: Clip.hardEdge, - child: InkWell( - onTap: () async { - AccountBloc accountBloc = context.read(); - AuthBloc authBloc = context.read(); - ThunderBloc thunderBloc = context.read(); +class _InboxMentionsViewState extends State { + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + final state = context.read().state; - final ThunderState state = context.read().state; - final bool reduceAnimations = state.reduceAnimations; + return Builder(builder: (context) { + return CustomScrollView( + key: PageStorageKey(l10n.mention(10)), + slivers: [ + SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)), + if (state.status == InboxStatus.loading) + const SliverFillRemaining( + hasScrollBody: false, + child: Center(child: CircularProgressIndicator()), + ), + if (widget.mentions.isEmpty) + SliverFillRemaining( + hasScrollBody: false, + child: Center(child: Text(l10n.noMentions)), + ), + SliverList.builder( + itemCount: widget.mentions.length, + itemBuilder: (context, index) { + PersonMentionView personMentionView = widget.mentions[index]; + PersonMention personMention = personMentionView.personMention; - // To to specific post for now, in the future, will be best to scroll to the position of the comment - await Navigator.of(context).push( - SwipeablePageRoute( - transitionDuration: reduceAnimations ? const Duration(milliseconds: 100) : null, - backGestureDetectionStartOffset: Platform.isAndroid ? 45 : 0, - backGestureDetectionWidth: 45, - canSwipe: Platform.isIOS || state.enableFullScreenSwipeNavigationGesture, - canOnlySwipeFromEdge: disableFullPageSwipe(isUserLoggedIn: authBloc.state.isLoggedIn, state: thunderBloc.state, isPostPage: true) || !state.enableFullScreenSwipeNavigationGesture, - builder: (context) => MultiBlocProvider( - providers: [ - BlocProvider.value(value: accountBloc), - BlocProvider.value(value: authBloc), - BlocProvider.value(value: thunderBloc), - BlocProvider(create: (context) => PostBloc()), - ], - child: PostPage( - selectedCommentPath: mentions[index].comment.path, - selectedCommentId: mentions[index].comment.id, - postId: mentions[index].post.id, - onPostUpdated: (PostViewMedia postViewMedia) => {}), - ), - ), - ); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Column( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - UserFullNameWidget( - context, - mentions[index].creator.name, - mentions[index].creator.displayName, - fetchInstanceNameFromUrl(mentions[index].creator.actorId), - ), - Text(formatTimeToString(dateTime: mentions[index].comment.published.toIso8601String())) - ], - ), - Row( - children: [ - ExcludeSemantics( - child: ScalableText( - l10n.in_, - fontScale: thunderState.contentFontSizeScale, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.textTheme.bodyMedium?.color?.withOpacity(0.4), - ), - ), - ), - const SizedBox(width: 5.0), - GestureDetector( - child: CommunityFullNameWidget( - context, - mentions[index].community.name, - mentions[index].community.title, - fetchInstanceNameFromUrl(mentions[index].community.actorId), - ), - onTap: () => onTapCommunityName(context, mentions[index].community.id), + CommentReference( + comment: personMentionView.toCommentView(), + isOwnComment: personMentionView.creator.id == context.read().state.account?.userId, + child: IconButton( + onPressed: () => context.read().add(InboxItemActionEvent(action: CommentAction.read, personMentionId: personMention.id, value: !personMention.read)), + icon: Icon( + Icons.check, + semanticLabel: l10n.markAsRead, + color: personMention.read ? Colors.green : null, ), - ], + visualDensity: VisualDensity.compact, + ), ), - const SizedBox(height: 10), - CommonMarkdownBody(body: mentions[index].comment.content), - const Divider(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (mentions[index].personMention.read == false) - IconButton( - onPressed: () { - context.read().add(MarkMentionAsReadEvent(personMentionId: mentions[index].personMention.id, read: true)); - }, - icon: const Icon( - Icons.check, - semanticLabel: 'Mark as read', - ), - visualDensity: VisualDensity.compact, - ), - IconButton( - onPressed: () async => navigateToCreateCommentPage( - context, - parentCommentView: CommentView( - comment: mentions[index].comment, - creator: mentions[index].creator, - post: mentions[index].post, - community: mentions[index].community, - counts: mentions[index].counts, - creatorBannedFromCommunity: mentions[index].creatorBannedFromCommunity, - subscribed: mentions[index].subscribed, - saved: mentions[index].saved, - creatorBlocked: mentions[index].creatorBlocked, - ), - onCommentSuccess: (commentView, userChanged) { - // TODO: Handle - }, - ), - icon: const Icon( - Icons.reply_rounded, - semanticLabel: 'Reply', - ), - visualDensity: VisualDensity.compact, - ), - ], - ) + if (index != widget.mentions.length - 1) const ThunderDivider(sliver: false, padding: false), ], - ), - ), + ); + }, ), - ); - }, - ); - } - - void onTapCommunityName(BuildContext context, int communityId) { - navigateToFeedPage(context, feedType: FeedType.community, communityId: communityId); + if (state.hasReachedInboxMentionEnd && widget.mentions.isNotEmpty) const SliverToBoxAdapter(child: FeedReachedEnd()), + ], + ); + }); } } diff --git a/lib/inbox/widgets/inbox_private_messages_view.dart b/lib/inbox/widgets/inbox_private_messages_view.dart index 0dcd95b74..ec8a60e24 100644 --- a/lib/inbox/widgets/inbox_private_messages_view.dart +++ b/lib/inbox/widgets/inbox_private_messages_view.dart @@ -1,63 +1,88 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:lemmy_api_client/v3.dart'; +import 'package:thunder/feed/view/feed_page.dart'; +import 'package:thunder/inbox/bloc/inbox_bloc.dart'; import 'package:thunder/shared/common_markdown_body.dart'; import 'package:thunder/utils/date_time.dart'; -class InboxPrivateMessagesView extends StatelessWidget { +class InboxPrivateMessagesView extends StatefulWidget { final List privateMessages; const InboxPrivateMessagesView({super.key, this.privateMessages = const []}); + @override + State createState() => _InboxPrivateMessagesViewState(); +} + +class _InboxPrivateMessagesViewState extends State { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; final theme = Theme.of(context); + final state = context.read().state; - if (privateMessages.isEmpty) { - return Align(alignment: Alignment.topCenter, heightFactor: (MediaQuery.of(context).size.height / 27), child: const Text('No messages')); - } - - return ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: privateMessages.length, - itemBuilder: (context, index) { - return Card( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - privateMessages[index].creator.name, - style: theme.textTheme.titleSmall?.copyWith(color: Colors.greenAccent), - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 8.0), - child: Icon(Icons.arrow_forward_rounded, size: 14), - ), - Text( - privateMessages[index].recipient.name, - style: theme.textTheme.titleSmall?.copyWith(color: Colors.greenAccent), - ), - ], - ), - Text(formatTimeToString(dateTime: privateMessages[index].privateMessage.published.toIso8601String())) - ], - ), - const SizedBox(height: 10), - CommonMarkdownBody(body: privateMessages[index].privateMessage.content), - ], + return Builder(builder: (context) { + return CustomScrollView( + key: PageStorageKey(l10n.message(10)), + slivers: [ + SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)), + if (state.status == InboxStatus.loading) + const SliverFillRemaining( + hasScrollBody: false, + child: Center(child: CircularProgressIndicator()), ), + if (widget.privateMessages.isEmpty) + SliverFillRemaining( + hasScrollBody: false, + child: Center(child: Text(l10n.noMessages)), + ), + SliverList.builder( + itemCount: widget.privateMessages.length, + itemBuilder: (context, index) { + return Card( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.privateMessages[index].creator.name, + style: theme.textTheme.titleSmall?.copyWith(color: Colors.greenAccent), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), + child: Icon(Icons.arrow_forward_rounded, size: 14), + ), + Text( + widget.privateMessages[index].recipient.name, + style: theme.textTheme.titleSmall?.copyWith(color: Colors.greenAccent), + ), + ], + ), + Text(formatTimeToString(dateTime: widget.privateMessages[index].privateMessage.published.toIso8601String())) + ], + ), + const SizedBox(height: 10), + CommonMarkdownBody(body: widget.privateMessages[index].privateMessage.content), + ], + ), + ), + ); + }, ), - ); - }, - ); + if (state.hasReachedInboxMentionEnd && widget.privateMessages.isNotEmpty) const SliverToBoxAdapter(child: FeedReachedEnd()), + ], + ); + }); } } diff --git a/lib/inbox/widgets/inbox_replies_view.dart b/lib/inbox/widgets/inbox_replies_view.dart index b1ebec5ad..375100e4c 100644 --- a/lib/inbox/widgets/inbox_replies_view.dart +++ b/lib/inbox/widgets/inbox_replies_view.dart @@ -4,16 +4,16 @@ import 'package:flutter/material.dart'; // Package imports import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; // Project imports +import 'package:thunder/comment/enums/comment_action.dart'; import 'package:thunder/comment/utils/navigate_comment.dart'; import 'package:thunder/core/auth/bloc/auth_bloc.dart'; -import 'package:thunder/feed/utils/utils.dart'; import 'package:thunder/feed/view/feed_page.dart'; import 'package:thunder/inbox/bloc/inbox_bloc.dart'; -import 'package:thunder/post/bloc/post_bloc.dart'; -import 'package:thunder/post/utils/comment_action_helpers.dart'; import 'package:thunder/shared/comment_reference.dart'; +import 'package:thunder/shared/divider.dart'; extension on CommentReplyView { CommentView toCommentView() { @@ -27,90 +27,90 @@ extension on CommentReplyView { subscribed: subscribed, saved: saved, creatorBlocked: creatorBlocked, + myVote: myVote as int?, ); } } class InboxRepliesView extends StatefulWidget { final List replies; - final bool showAll; - const InboxRepliesView({super.key, this.replies = const [], required this.showAll}); + const InboxRepliesView({super.key, this.replies = const []}); @override State createState() => _InboxRepliesViewState(); } class _InboxRepliesViewState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - if (widget.replies.isEmpty) { - return Align(alignment: Alignment.topCenter, heightFactor: (MediaQuery.of(context).size.height / 27), child: Text(l10n.noReplies)); - } + final l10n = AppLocalizations.of(context)!; + final state = context.read().state; - return ListView.builder( - padding: EdgeInsets.zero, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: widget.replies.length, - itemBuilder: (context, index) { - return Column( - children: [ - Divider( - height: 1.0, - thickness: 1.0, - color: ElevationOverlay.applySurfaceTint( - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surfaceTint, - 10, - ), + return Builder(builder: (context) { + return CustomScrollView( + key: PageStorageKey(l10n.reply(10)), + slivers: [ + SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)), + if (state.status == InboxStatus.loading) + const SliverFillRemaining( + hasScrollBody: false, + child: Center(child: CircularProgressIndicator()), ), - CommentReference( - comment: widget.replies[index].toCommentView(), - onVoteAction: (int commentId, int voteType) => context.read().add(VoteCommentEvent(commentId: commentId, score: voteType)), - onSaveAction: (int commentId, bool save) => context.read().add(SaveCommentEvent(commentId: commentId, save: save)), - onDeleteAction: (int commentId, bool deleted) => context.read().add(DeleteCommentEvent(deleted: deleted, commentId: commentId)), - onReportAction: (int commentId) { - showReportCommentActionBottomSheet( - context, - commentId: commentId, - ); - }, - onReplyEditAction: (CommentView commentView, bool isEdit) async => navigateToCreateCommentPage( - context, - commentView: isEdit ? commentView : null, - parentCommentView: isEdit ? null : commentView, - onCommentSuccess: (commentView, userChanged) { - if (!userChanged) { - context.read().add(UpdateCommentEvent(commentView: commentView, isEdit: isEdit)); - } - }, - ), - isOwnComment: widget.replies[index].creator.id == context.read().state.account?.userId, - child: IconButton( - onPressed: () { - context.read().add(MarkReplyAsReadEvent(commentReplyId: widget.replies[index].commentReply.id, read: !widget.replies[index].commentReply.read, showAll: widget.showAll)); - }, - icon: Icon( - Icons.check, - semanticLabel: l10n.markAsRead, - color: widget.replies[index].commentReply.read ? Colors.green : null, - ), - visualDensity: VisualDensity.compact, - ), + if (widget.replies.isEmpty) + SliverFillRemaining( + hasScrollBody: false, + child: Center(child: Text(l10n.noReplies)), ), - ], - ); - }, - ); - } + SliverList.builder( + itemCount: widget.replies.length, + itemBuilder: (context, index) { + CommentReplyView commentReplyView = widget.replies[index]; + CommentReply commentReply = commentReplyView.commentReply; - void onTapCommunityName(BuildContext context, int communityId) { - navigateToFeedPage(context, feedType: FeedType.community, communityId: communityId); + return Column( + children: [ + CommentReference( + comment: commentReplyView.toCommentView(), + isOwnComment: commentReplyView.creator.id == context.read().state.account?.userId, + onVoteAction: (int commentId, int voteType) => context.read().add( + InboxItemActionEvent( + action: CommentAction.vote, + commentReplyId: commentReply.id, + value: switch (voteType) { + 1 => commentReplyView.myVote == 1 ? 0 : 1, + -1 => commentReplyView.myVote == -1 ? 0 : -1, + _ => 0, + }, + ), + ), + onSaveAction: (int commentId, bool save) => context.read().add(InboxItemActionEvent(action: CommentAction.save, commentReplyId: commentReply.id, value: save)), + onDeleteAction: (int commentId, bool deleted) => context.read().add(InboxItemActionEvent(action: CommentAction.delete, commentReplyId: commentReply.id, value: deleted)), + onReplyEditAction: (CommentView commentView, bool isEdit) { + return navigateToCreateCommentPage( + context, + commentView: isEdit ? commentView : null, + parentCommentView: isEdit ? null : commentView, + ); + }, + child: IconButton( + onPressed: () => context.read().add(InboxItemActionEvent(action: CommentAction.read, commentReplyId: commentReply.id, value: !commentReply.read)), + icon: Icon( + Icons.check, + semanticLabel: l10n.markAsRead, + color: commentReply.read ? Colors.green : null, + ), + visualDensity: VisualDensity.compact, + ), + ), + if (index != widget.replies.length - 1) const ThunderDivider(sliver: false, padding: false), + ], + ); + }, + ), + if (state.hasReachedInboxReplyEnd && widget.replies.isNotEmpty) const SliverToBoxAdapter(child: FeedReachedEnd()), + ], + ); + }); } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8d22c518c..fae4659d1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -429,7 +429,7 @@ "@confirmMarkAllAsReadBody": { "description": "The body of the confirm mark all as read dialog" }, - "confirmMarkAllAsReadTitle": "Mark All As Read?", + "confirmMarkAllAsReadTitle": "Mark all as read?", "@confirmMarkAllAsReadTitle": { "description": "The title of the confirm mark all as read dialog" }, @@ -645,6 +645,10 @@ "@deleteLocalPreferencesDescription": { "description": "Description for confirmation action to delete local preferences" }, + "deleteUserLabelConfirmation": "Are you sure you want to delete the label?", + "@deleteUserLabelConfirmation": { + "description": "Confirmation message for deleting a label" + }, "deletedByCreator": "deleted by creator", "@deletedByCreator": { "description": "Placeholder text for a comment deleted by the creator. Be sure to keep this lowercase." @@ -1197,7 +1201,7 @@ "@manageMedia": { "description": "Setting name and page title for media management" }, - "markAllAsRead": "Mark All As Read", + "markAllAsRead": "Mark all as read", "@markAllAsRead": { "description": "The mark all as read action" }, @@ -1393,6 +1397,14 @@ "@noMatrixUserSet": { "description": "Message for no matrix user set" }, + "noMentions": "No mentions", + "@noMentions": { + "description": "Label for when there are no person mentions in the list" + }, + "noMessages": "No messages", + "@noMessages": { + "description": "Label for when there are no messages in the inbox" + }, "noPostsFound": "No posts found.", "@noPostsFound": {}, "noProfileBioSet": "No profile bio set", @@ -1415,6 +1427,10 @@ }, "noUserBlocks": "No blocked users.", "@noUserBlocks": {}, + "noUserLabels": "You have not created any user labels yet", + "@noUserLabels": { + "description": "Placeholder message for empty list of labels" + }, "noUsersFound": "No users found.", "@noUsersFound": {}, "none": "None", @@ -2003,7 +2019,7 @@ "@shareUserLinkLocal": { "description": "Menu item for sharing a local user link" }, - "showAll": "Show All", + "showAll": "Show all", "@showAll": {}, "showBotAccounts": "Show Bot Accounts", "@showBotAccounts": { @@ -2107,6 +2123,10 @@ "@showThumbnailPreviewOnRight": { "description": "Toggle to show thumbnails on the right side." }, + "showUnreadOnly": "Show unread only", + "@showUnreadOnly": { + "description": "Show unread replies/mentions/messages only" + }, "showUpdateChangelogs": "Show Update Changelogs", "@showUpdateChangelogs": { "description": "Setting for showing changelogs after updates" @@ -2551,6 +2571,14 @@ "@userLabelHint": { "description": "Hint text for adding a new user label" }, + "userLabels": "User Labels", + "@userLabels": { + "description": "Heading for user labels settings" + }, + "userLabelsSettingsPageDescription": "You can add, modify, or remove labels associated with users.", + "@userLabelsSettingsPageDescription": { + "description": "Description text for user labels settings page" + }, "userNameColor": "User Name Color", "@userNameColor": { "description": "Setting for username color" diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 8ea50aa6c..2ff9f0b38 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -465,7 +465,7 @@ "@instanceHasAlreadyBenAdded": {}, "internetOrInstanceIssues": "Es posible que no esté conectado a Internet o que tu instancia no esté disponible actualmente.", "@internetOrInstanceIssues": {}, - "keywordFilterDescription": "Filtra los mensajes que contienen palabras clave en el título o en el cuerpo", + "keywordFilterDescription": "Filtra los mensajes que contienen palabras clave en el título, cuerpo o URL", "@keywordFilterDescription": { "description": "Description of keyword filter settings" }, @@ -1453,7 +1453,7 @@ "@unpinFromCommunity": { "description": "Setting for unpinning a post from a community (moderator action)" }, - "lockPost": "Bloquear tema", + "lockPost": "Bloquear Publicación", "@lockPost": { "description": "Action for locking a post (moderator action)" }, @@ -1561,7 +1561,7 @@ "@markPostAsReadOnScroll": { "description": "Toggle to mark posts as read as you scroll past them in the feed." }, - "moderator": "Moderador", + "moderator": "{count, plural, zero {Moderator} one {Moderator} other {Moderators}}", "@moderator": { "description": "Role name for moderator" }, @@ -2375,7 +2375,7 @@ "@accountBirthday": { "description": "User descriptor for accounts created on today's date" }, - "xYearsOld": "{count, plural, zero {{x} años} one {{x} año} other {{x} años}}", + "xYearsOld": "{count, plural, zero {{x} año} one {{x} años} other {{x} años}}", "@xYearsOld": { "description": "An account descriptor representing how old an account is" }, @@ -2483,7 +2483,7 @@ "@countLocalSubscribers": { "description": "Number of local subscribers" }, - "changePasswordWarning": "Para cambiar su contraseña, será direccionado a su instancia principal. \\n\\n¿Seguro que desea continuar?", + "changePasswordWarning": "Para cambiar tu contraseña, será redirigido al sitio de tu instancia. \n\n¿Está seguro de que deseas continuar?", "@changePasswordWarning": { "description": "Warning label for changing password" }, @@ -2574,5 +2574,65 @@ "userLabelHint": "Este es mi usuario favorito", "@userLabelHint": { "description": "Hint text for adding a new user label" + }, + "databaseNotExportedSuccessfully": "La base de datos no fue exportada exitosamente o la operación fue cancelada.", + "@databaseNotExportedSuccessfully": { + "description": "Message shown to the user when the db was unsuccessfully exported" + }, + "email": "Correo Electrónico", + "@email": { + "description": "Label for email input" + }, + "exportSettingsSubtitle": "Las configuraciones incluyen todas las preferencias que ha configurado en Thunder.", + "@exportSettingsSubtitle": { + "description": "Subtitle of setting for exporting settings" + }, + "guestModeFeedSettings": "Configuración de Feed Modo Invitado", + "@guestModeFeedSettings": { + "description": "Feed settings for guest accounts" + }, + "hostInstance": "Instancia de Host", + "@hostInstance": { + "description": "The instance hosting a community" + }, + "imageDimensionTimeout": "Tiempo de Espera de Dimensión de Imagen", + "@imageDimensionTimeout": { + "description": "Setting for how long to wait for the image dimensions to be fetched" + }, + "matrixUser": "Usuario de Matrix", + "@matrixUser": { + "description": "Label for Matrix user id" + }, + "databaseNotImportedSuccessfully": "La base de datos no fue importada exitosamente o la operación fue cancelada.", + "@databaseNotImportedSuccessfully": { + "description": "Message shown to the user when the db was unsuccessfully imported" + }, + "exportDatabaseSubtitle": "La base de datos contiene información acerca de cuentas, favoritos, subscripciones anónimas y etiquetas de usuario.", + "@exportDatabaseSubtitle": { + "description": "Subtitle of setting for exporting the db" + }, + "displayName": "Nombre para mostrar", + "@displayName": { + "description": "Label for display name" + }, + "guestModeFeedSettingsLabel": "Las siguientes configuraciones aplican solo para cuentas de invitado. Para ajustar las configuraciones del feed de su cuenta, vaya a Configuración de Cuenta.", + "@guestModeFeedSettingsLabel": { + "description": "Description for guest mode feed settings" + }, + "exportDatabase": "Exportar Base de Datos", + "@exportDatabase": { + "description": "Name of the setting for exporting the db" + }, + "showCommunityDisplayNames": "Mostrar nombres para mostrar de la comunidad", + "@showCommunityDisplayNames": { + "description": "Setting for showing community display names" + }, + "noMessages": "Sin mensajes", + "@noMessages": { + "description": "Label for when there are no messages in the inbox" + }, + "noMentions": "Sin menciones", + "@noMentions": { + "description": "Label for when there are no person mentions in the list" } } diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb index 272913e36..236415ece 100644 --- a/lib/l10n/app_tr.arb +++ b/lib/l10n/app_tr.arb @@ -1,1710 +1,2640 @@ -{ - "about": "Hakkında", - "@about": { - "description": "Ayarlar kategorisi hakkında." - }, - "accessibility": "Erişilebilirlik", - "@accessibility": {}, - "accessibilityProfilesDescription": "Erişilebilirlik profilleri, belirli bir erişilebilirlik gereksinimini karşılamak için aynı anda birkaç ayarın uygulanmasını sağlar.", - "@accessibilityProfilesDescription": {}, - "account": "{count, plural, zero {Account} one {Account} other {Accounts} } ", - "@account": { - "description": "Kullanıcı hesabını açıklar" - }, - "accountSettings": "Hesap Ayarları", - "@accountSettings": {}, - "actions": "Eylemler", - "@actions": {}, - "active": "Aktif", - "@active": {}, - "add": "Ekle", - "@add": {}, - "addAccount": "Hesap Ekle", - "@addAccount": {}, - "addAccountToSeeProfile": "Hesabınızı görmek için giriş yapın.", - "@addAccountToSeeProfile": {}, - "addAnonymousInstance": "Anonim Örnek Ekle", - "@addAnonymousInstance": {}, - "addKeywordFilter": "Anahtar Kelime Ekle", - "@addKeywordFilter": { - "description": "Anahtar kelime eklemek için metin alanı için ipucu" - }, - "addToFavorites": "Favorilere ekle", - "@addToFavorites": { - "description": "Çekmecedeki bir topluluğu favorilere ekleme eylemi" - }, - "addedCommunityToSubscriptions": "Topluluğa abone olundu", - "@addedCommunityToSubscriptions": {}, - "addedInstanceMod": "Oluşum Modu eklendi", - "@addedInstanceMod": { - "description": "Bir örneğe mod eklemek için moderatör eylemi için kısa açıklama" - }, - "addedModToCommunity": "Topluluğa Mod Eklendi", - "@addedModToCommunity": { - "description": "Bir topluluğa mod eklemek için moderatör eylemi için kısa açıklama" - }, - "admin": "Yönetici", - "@admin": { - "description": "Yönetici için rol adı" - }, - "advanced": "Gelişmiş", - "@advanced": { - "description": "Gelişmiş ayarlar için başlık" - }, - "all": "Tümü", - "@all": {}, - "allPosts": "Tüm Gönderiler", - "@allPosts": {}, - "allowOpenSupportedLinks": "Uygulamanın desteklenen bağlantıları açmasına izin verin.", - "@allowOpenSupportedLinks": { - "description": "Uygulamanın desteklenen bağlantıları açmasına izin verme seçeneği." - }, - "alreadyPostedTo": "Zaten şuraya gönderildi:", - "@alreadyPostedTo": {}, - "andXMore": "ve {count} daha fazlası", - "@andXMore": {}, - "animations": "Animasyonlar", - "@animations": {}, - "anonymous": "Anonim", - "@anonymous": {}, - "appLanguage": "Uygulama Dili", - "@appLanguage": { - "description": "Ayarlarda dil seçimi." - }, - "appearance": "Görünüm", - "@appearance": { - "description": "Ayarlar -> Görünümdeki Görünümün Başlığı" - }, - "applied": "Uygulandı", - "@applied": {}, - "apply": "Uygula", - "@apply": {}, - "back": "Geri", - "@back": {}, - "backButton": "Geri düğmesi", - "@backButton": {}, - "backToTop": "Başa Dön", - "@backToTop": {}, - "backgroundCheckWarning": "Bildirim kontrollerinin ek pil tüketeceğini unutmayın", - "@backgroundCheckWarning": { - "description": "Bildirimleri etkinleştirme uyarısı" - }, - "bannedUser": "Yasaklı Kullanıcı", - "@bannedUser": { - "description": "Bir kullanıcıyı yasaklamak için moderatör eylemi için kısa açıklama" - }, - "bannedUserFromCommunity": "Topluluktan Yasaklı Kullanıcı", - "@bannedUserFromCommunity": { - "description": "Bir kullanıcıyı bir topluluktan yasaklamak için moderatör eylemi için kısa açıklama" - }, - "base": "Matrah", - "@base": { - "description": "Temel yazı tipi ölçeği açıklaması" - }, - "blockCommunity": "Topluluğu Engelle", - "@blockCommunity": {}, - "blockInstance": "Örneği Engelle", - "@blockInstance": {}, - "blockUser": "Kullanıcıyı Engelle", - "@blockUser": {}, - "blockedCommunities": "Engellenen Topluluklar", - "@blockedCommunities": {}, - "blockedInstances": "Engellenen Örnekler", - "@blockedInstances": {}, - "blockedUsers": "Engellenen Kullanıcılar", - "@blockedUsers": {}, - "browserMode": "Bağlantı işleme", - "@browserMode": { - "description": "Bağlantı işleme ayarı için başlık (dahili olarak browserMode olarak adlandırılır)" - }, - "browsingAnonymously": "Şu anda {instance }' a anonim olarak göz atıyorsunuz.", - "@browsingAnonymously": {}, - "cancel": "İptal", - "@cancel": {}, - "cannotReportOwnComment": "Kendi yorumunuz için rapor gönderemezsiniz.", - "@cannotReportOwnComment": {}, - "cantBlockAdmin": "Bir örnek yöneticisini engelleyemezsiniz.", - "@cantBlockAdmin": {}, - "cantBlockYourself": "Kendinizi engelleyemezsiniz.", - "@cantBlockYourself": {}, - "cardPostCardMetadataItems": "Kart Görünümü Meta Verileri", - "@cardPostCardMetadataItems": { - "description": "Kartpostal kartı meta veri öğeleri için ayar adı" - }, - "cardView": "Kart Görünümü", - "@cardView": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki kart görünümü seçeneği etiketi" - }, - "cardViewDescription": "Ayarları yapmak için kart görünümünü etkinleştirin", - "@cardViewDescription": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki kart görünümü alt kategorisi için açıklama" - }, - "cardViewSettings": "Kart Görünümü Ayarları", - "@cardViewSettings": { - "description": "Ayar -> Görünüm -> Gönderiler'de Alt Kategori" - }, - "changeAccountSettingsFor": "Için hesap ayarlarını değiştirin", - "@changeAccountSettingsFor": { - "description": "Hesap ayarlarını değiştirirken profil oluşturucu penceresine gitme" - }, - "changeSort": "Sıralamayı Değiştir", - "@changeSort": {}, - "clearCache": "Önbelleği Temizle ({cacheSize})", - "@clearCache": { - "description": "İletişim kutusunda önbelleği temizlemek için eylem etiketi" - }, - "clearDatabase": "Veritabanını Temizle", - "@clearDatabase": { - "description": "İletişim kutusunda yerel veritabanını temizlemek için eylem etiketi" - }, - "clearPreferences": "Tercihleri Temizle", - "@clearPreferences": { - "description": "İletişim kutusundaki yerel kullanıcı tercihlerini temizlemek için eylem etiketi" - }, - "clearSearch": "Aramayı Temizle", - "@clearSearch": {}, - "clearedCache": "Önbellek başarıyla temizlendi.", - "@clearedCache": { - "description": "Önbelleğin başarıyla temizlendiğini belirten mesaj" - }, - "clearedDatabase": "Yerel veritabanı temizlendi. Yeni değişikliklerin etkili olması için Thunder'ı yeniden başlat.", - "@clearedDatabase": {}, - "clearedUserPreferences": "Tüm kullanıcı tercihleri temizlendi", - "@clearedUserPreferences": {}, - "close": "Kapat", - "@close": {}, - "collapseCommentPreview": "Yorum Önizlemesini Daralt", - "@collapseCommentPreview": { - "description": "Ayar -> Görünüm -> Yorumlar bölümündeki daraltma düğmesi için anlamsal etiket" - }, - "collapseInformation": "Bilgileri Daralt", - "@collapseInformation": { - "description": "Bilgileri daraltma eylemini açıklar - FAB ayarlarında kullanılır" - }, - "collapseParentCommentBodyOnGesture": "Daraltıldığında Üst Yorumu Gizle", - "@collapseParentCommentBodyOnGesture": { - "description": "Jestte üst yorum gövdesini daraltmak için geçiş yapın." - }, - "collapsePost": "Gönderiyi daralt", - "@collapsePost": {}, - "collapsePostPreview": "Gönderi Önizlemesini Daralt", - "@collapsePostPreview": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki daraltma düğmesi için anlamsal etiket" - }, - "collapseSpoiler": "Spoiler'ı Daralt", - "@collapseSpoiler": { - "description": "Çöken spoyler etiketi" - }, - "combineCommentScores": "Yorum Puanlarını Birleştir", - "@combineCommentScores": { - "description": "Yorum puanlarını birleştirmek için geçiş yapın." - }, - "combineCommentScoresLabel": "Yorum Puanlarını Birleştir", - "@combineCommentScoresLabel": { - "description": "Yorum puanlarını birleştirme ayarı etiketi" - }, - "combineNavAndFab": "FAB ve Navigasyon Düğmelerini Birleştirin", - "@combineNavAndFab": {}, - "combineNavAndFabDescription": "Gezinme düğmeleri arasında Kayan Eylem Düğmesi gösterilecektir.", - "@combineNavAndFabDescription": {}, - "comment": "Yorum", - "@comment": {}, - "commentBehaviourSettings": "Yorumlar", - "@commentBehaviourSettings": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "commentFontScale": "Yorum İçeriği Yazı Tipi Ölçeği", - "@commentFontScale": { - "description": "Yorum yazı tipi ölçeği için ayar" - }, - "commentPreview": "Verilen ayarlarla yorumların önizlemesini göster", - "@commentPreview": { - "description": "Ayar -> Görünüm -> Yorumlar bölümündeki yorum önizlemesi için açıklama" - }, - "commentReported": "Yorum incelenmek üzere işaretlendi.", - "@commentReported": {}, - "commentSavedAsDraft": "Yorum taslak olarak kaydedildi", - "@commentSavedAsDraft": {}, - "commentShowUserInstance": "Kullanıcı Örneğini Göster", - "@commentShowUserInstance": { - "description": "Ayarlar, kullanıcı örneğini yorumlarda görünen adlarının yanında görüntülemek için geçiş yapar" - }, - "commentSortType": "Yorum Sıralama Türü", - "@commentSortType": {}, - "commentSwipeActions": "Yorum Kaydırma Eylemleri", - "@commentSwipeActions": { - "description": "Yorum kaydırma eylemleri için ayar" - }, - "commentSwipeGesturesHint": "Bunun yerine düğmeler mi kullanmak istiyorsunuz? Bunları genel ayarlardaki yorumlar bölümünde etkinleştirin.", - "@commentSwipeGesturesHint": {}, - "comments": "Yorumlar", - "@comments": {}, - "communities": "Topluluklar", - "@communities": {}, - "community": "Topluluk", - "@community": {}, - "communityFormat": "Topluluk Biçimi", - "@communityFormat": { - "description": "Topluluğun tam adı biçimini ayarlama" - }, - "compactPostCardMetadataItems": "Kompakt Görünüm Meta Verileri", - "@compactPostCardMetadataItems": { - "description": "Kompakt kartpostal meta veri öğeleri için ayarın adı" - }, - "compactView": "Kompakt Görünüm", - "@compactView": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki kompakt görünüm seçeneği etiketi" - }, - "compactViewDescription": "Ayarları yapmak için kompakt görünümü etkinleştirin", - "@compactViewDescription": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki kompakt görünüm alt kategorisinin açıklaması" - }, - "compactViewSettings": "Kompakt Görünüm Ayarları", - "@compactViewSettings": { - "description": "Ayar -> Görünüm -> Gönderiler'de Alt Kategori" - }, - "condensed": "Yoğunlaştırılmış", - "@condensed": { - "description": "Yoğunlaştırılmış gönderi gövdesi görünüm türü" - }, - "confirm": "Onayla", - "@confirm": { - "description": "İletişim kutusundaki onaylama eylemi etiketi" - }, - "confirmLogOutBody": "Çıkış yapmak istediğinizden emin misiniz?", - "@confirmLogOutBody": { - "description": "Onay oturumu kapatma iletişim kutusunun gövdesi" - }, - "confirmLogOutTitle": "Çıkış Yap?", - "@confirmLogOutTitle": { - "description": "Oturumu kapatmayı onayla iletişim kutusunun başlığı" - }, - "confirmMarkAllAsReadBody": "Tüm mesajları okundu olarak işaretlemek istediğinizden emin misiniz?", - "@confirmMarkAllAsReadBody": { - "description": "Onay kutusunun gövdesi tümünü okundu olarak işaretler" - }, - "confirmMarkAllAsReadTitle": "Tümü Okundu Olarak İşaretlensin", - "@confirmMarkAllAsReadTitle": { - "description": "Onayla başlığının tümü okundu olarak işaretle iletişim kutusu" - }, - "confirmResetCommentPreferences": "Bu, tüm yorum tercihlerini sıfırlayacaktır. Devam etmek istediğinizden emin misiniz?", - "@confirmResetCommentPreferences": { - "description": "Ayar -> Görünüm -> Yorumlar bölümündeki tercihleri sıfırlama iletişim kutusunun açıklaması" - }, - "confirmResetPostPreferences": "Bu, tüm gönderi tercihlerini sıfırlayacaktır. Devam etmek istediğinizden emin misiniz?", - "@confirmResetPostPreferences": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki sıfırlama tercihleri iletişim kutusunun açıklaması" - }, - "controversial": "Tartışmalı", - "@controversial": {}, - "copiedToClipboard": "Panoya kopyalandı", - "@copiedToClipboard": {}, - "copy": "Kopyala", - "@copy": {}, - "copyText": "Metni Kopyala", - "@copyText": {}, - "couldNotDetermineCommentDelete": "Hata: Yorumu silmek için gönderi belirlenemedi.", - "@couldNotDetermineCommentDelete": {}, - "couldNotDeterminePostComment": "Hata: Yorum yapılacak gönderi belirlenemedi.", - "@couldNotDeterminePostComment": {}, - "couldntCreateReport": "Yorum raporunuz şu anda gönderilemedi. Lütfen daha sonra tekrar deneyin", - "@couldntCreateReport": {}, - "countSubscribers": "{count} abone", - "@countSubscribers": {}, - "countUsers": "{count} kullanıcı", - "@countUsers": { - "description": "Belirli sayıda kullanıcıyı tanımlar" - }, - "createAccount": "Hesap Oluştur", - "@createAccount": {}, - "createComment": "Yorum Oluştur", - "@createComment": {}, - "createNewCrossPost": "Yeni çapraz gönderi oluştur", - "@createNewCrossPost": {}, - "createPost": "Gönderi Oluştur", - "@createPost": {}, - "creator": "Yaratıcı", - "@creator": { - "description": "Yaratıcı aramaları filtreler." - }, - "crossPostedFrom": "cross - posted from: {postUrl}", - "@crossPostedFrom": { - "description": "Metin tabanlı çapraz gönderi için ilk başlık" - }, - "crossPostedTo": "Çapraz gönderim yeri", - "@crossPostedTo": {}, - "currentLongPress": "Şu anda uzun basma olarak ayarlanmış", - "@currentLongPress": {}, - "currentSinglePress": "Şu anda tek basım olarak ayarlanmış", - "@currentSinglePress": {}, - "customizeSwipeActions": "Kaydırma eylemlerini özelleştirme (değiştirmek için dokunun)", - "@customizeSwipeActions": {}, - "dangerZone": "Tehlikeli Bölge", - "@dangerZone": { - "description": "Eylemlerin tehlikeli olabileceği bir bölümü açıklar (örneğin, hesabı kalıcı olarak silmek)" - }, - "dark": "Koyu", - "@dark": { - "description": "Koyu temayı kullanarak açıklar" - }, - "dateFormat": "Tarih Formatı", - "@dateFormat": { - "description": "Tarih formatı ayarı" - }, - "debug": "Hata ayıklama", - "@debug": { - "description": "Hata ayıklama ayarları kategorisi." - }, - "debugDescription": "Aşağıdaki hata ayıklama ayarları yalnızca sorun giderme amacıyla kullanılmalıdır.", - "@debugDescription": { - "description": "Hata ayıklama ayarları sayfası açıklaması" - }, - "defaultCommentSortType": "Varsayılan Yorum Sıralama Türü", - "@defaultCommentSortType": { - "description": "Varsayılan yorum sıralama türü için ayar" - }, - "defaultFeedSortType": "Varsayılan Akış Sıralama Türü", - "@defaultFeedSortType": { - "description": "Akış için varsayılan sıralama türü." - }, - "defaultFeedType": "Varsayılan Akış Türü", - "@defaultFeedType": { - "description": "Akış için varsayılan kayıt türü." - }, - "delete": "Sil", - "@delete": {}, - "deleteAccount": "Hesabı Sil", - "@deleteAccount": { - "description": "Hesabı silme eylemi etiketi" - }, - "deleteAccountDescription": "Hesabınızı kalıcı olarak silmek için örnek sitenize yönlendirileceksiniz. \n\nDevam etmek istediğinizden emin misiniz?", - "@deleteAccountDescription": { - "description": "Hesabı silmek için onay işlemi açıklaması" - }, - "deleteLocalDatabase": "Yerel Veritabanını Sil", - "@deleteLocalDatabase": { - "description": "Yerel veritabanını silmek için eylem etiketi" - }, - "deleteLocalDatabaseDescription": "Bu işlem yerel veritabanını kaldıracak ve tüm hesaplarınızın oturumunu kapatacaktır.\n\nDevam etmek istediğinizden emin misiniz?", - "@deleteLocalDatabaseDescription": { - "description": "Yerel veritabanını silmek için onay işlemi açıklaması" - }, - "deleteLocalPreferences": "Yerel Tercihleri Sil", - "@deleteLocalPreferences": { - "description": "Yerel tercihleri silmek için eylem etiketi" - }, - "deleteLocalPreferencesDescription": "Bu, Thunder'daki tüm kullanıcı tercihlerini ve ayarlarını temizler.\n\nDevam etmek istiyor musunuz?", - "@deleteLocalPreferencesDescription": { - "description": "Yerel tercihleri silmek için onay işlemi açıklaması" - }, - "detailedReason": "Sebep: {reason}", - "@detailedReason": { - "description": "Moderasyon eyleminin nedeni" - }, - "dimReadPosts": "Dım Okuma Gönderileri", - "@dimReadPosts": { - "description": "Okunan gönderiler üzerindeki etkinin açıklaması." - }, - "disable": "Devre Dışı Bırak", - "@disable": { - "description": "Bir şeyi devre dışı bırakma eylemi" - }, - "dismissRead": "Okumayı Reddet", - "@dismissRead": {}, - "displayUserScore": "Kullanıcı Puanlarını (Karma) görüntüleyin.", - "@displayUserScore": { - "description": "Kullanıcı puanlarını veya karmayı görüntüleme seçeneği." - }, - "downloadingMedia": "Paylaşmak için medya indiriliyor…", - "@downloadingMedia": {}, - "downvote": "Olumsuz oy", - "@downvote": {}, - "downvotesDisabled": "Bu durumda olumsuz oylar kapatılır.", - "@downvotesDisabled": {}, - "edit": "Düzenle", - "@edit": {}, - "editComment": "Yorumu Düzenle", - "@editComment": {}, - "editPost": "Gönderiyi Düzenle", - "@editPost": { - "description": "Bir gönderiyi düzenlerken başlık." - }, - "empty": "Boş", - "@empty": {}, - "emptyInbox": "Gelen Kutusunu Boşalt", - "@emptyInbox": {}, - "emptyUri": "Bağlantı boş. Devam etmek için lütfen geçerli bir dinamik bağlantı sağlayın.", - "@emptyUri": { - "description": "Boş bağlantı için hata mesajı." - }, - "enableCommentNavigation": "Yorum Navigasyonunu Etkinleştir", - "@enableCommentNavigation": { - "description": "Yorum navigasyonunu etkinleştirme ayarı" - }, - "enableFeedFab": "Akışlarda Kayan Düğmeyi Etkinleştir", - "@enableFeedFab": { - "description": "Akış için Kayan Eylem Düğmesini etkinleştirin" - }, - "enableFloatingButtonOnFeeds": "Akışlarda Kayan Düğmeyi Etkinleştir", - "@enableFloatingButtonOnFeeds": { - "description": "Beslemelerde kayan düğmeyi etkinleştirme ayarı" - }, - "enableFloatingButtonOnPosts": "Gönderilerde Kayan Düğmeyi Etkinleştir", - "@enableFloatingButtonOnPosts": { - "description": "Gönderilerde kayan düğmeyi etkinleştirme ayarı" - }, - "enableInboxNotifications": "Gelen Kutusu Bildirimlerini Etkinleştir (Deneysel)", - "@enableInboxNotifications": { - "description": "Gelen kutusu bildirimleri için ad ayarlama" - }, - "enablePostFab": "Gönderilerde Kayan Düğmeyi Etkinleştir", - "@enablePostFab": { - "description": "Gönderi için Kayan Eylem Düğmesini etkinleştirin" - }, - "endSearch": "Aramayı Sonlandır", - "@endSearch": {}, - "errorDownloadingMedia": "Paylaşılacak medya dosyası indirilemedi: {errorMessage}", - "@errorDownloadingMedia": {}, - "exceptionProcessingUri": "Bağlantı işlenirken bir hata oluştu. Bu, oturumunuzda kullanılamayabilir.", - "@exceptionProcessingUri": { - "description": "Bağlantı işleme sırasında belirtilmeyen bir hata." - }, - "expandCommentPreview": "Yorum Önizlemesini Genişlet", - "@expandCommentPreview": { - "description": "Ayar -> Görünüm -> Yorumlar bölümündeki genişletme düğmesi için anlamsal etiket" - }, - "expandInformation": "Bilgileri Genişlet", - "@expandInformation": { - "description": "FAB ayarlarında kullanılan bilgileri genişletme eylemini açıklar" - }, - "expandOptions": "Seçenekleri genişlet", - "@expandOptions": {}, - "expandPost": "Gönderiyi genişlet", - "@expandPost": {}, - "expandPostPreview": "Gönderi Önizlemesini Genişlet", - "@expandPostPreview": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki genişletme düğmesi için anlamsal etiket" - }, - "expandSpoiler": "Spoiler'ı Genişlet", - "@expandSpoiler": { - "description": "Genişletme spoyleri etiketi" - }, - "expanded": "Genişletilmiş", - "@expanded": { - "description": "Genişletilmiş gönderi gövdesi görünüm türü" - }, - "exploreInstance": "Örneği keşfedin", - "@exploreInstance": { - "description": "Bir örneği keşfetme düğmesi" - }, - "extraLarge": "Ekstra Büyük", - "@extraLarge": { - "description": "Ekstra büyük yazı tipi ölçeği açıklaması" - }, - "failedToBlock": "Engellenemedi: {errorMessage}", - "@failedToBlock": {}, - "failedToLoadBlocks": "Bloklar yüklenemedi: {errorMessage}", - "@failedToLoadBlocks": {}, - "failedToUnblock": "Engel kaldırılamadı: {errorMessage}", - "@failedToUnblock": {}, - "favorites": "Favoriler", - "@favorites": { - "description": "Çekmecedeki favori topluluklar" - }, - "featuredPost": "Öne Çıkan Gönderi", - "@featuredPost": { - "description": "Bir gönderiyi öne çıkarmak için moderatör eylemi için kısa açıklama" - }, - "feed": "Akış", - "@feed": {}, - "feedBehaviourSettings": "Akış", - "@feedBehaviourSettings": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "feedTypeAndSorts": "Varsayılan Akış Türü ve Sıralama", - "@feedTypeAndSorts": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "fetchAccountError": "Hesap belirlenemedi", - "@fetchAccountError": {}, - "filteringBy": "{entity} tarafından filtreleniyor", - "@filteringBy": { - "description": "Aramalarda bir topluluğa veya yaratıcıya göre filtreleme." - }, - "filters": "Filtreler", - "@filters": { - "description": "Çeşitli filtreleri tanımlamak için kategori (kullanıcı, topluluk, örnek engelleme)" - }, - "floatingActionButton": "Kayan Eylem Düğmesi", - "@floatingActionButton": { - "description": "Kayan Eylem Düğmesi ayarları kategorisi." - }, - "floatingActionButtonInformation": "Thunder, birkaç hareketi destekleyen tamamen özelleştirilebilir bir FAB deneyimine sahiptir.\n- Ek FAB eylemlerini ortaya çıkarmak için yukarı kaydırın\n- FAB'I gizlemek veya ortaya çıkarmak için aşağı/yukarı kaydırın\n\nFAB için ana ve ikincil eylemleri özelleştirmek için aşağıdaki işlemlerden birine uzun basın.", - "@floatingActionButtonInformation": { - "description": "Ayarlar sayfasındaki FAB AÇIKLAMASI" - }, - "floatingActionButtonLongPressDescription": "fAB'IN uzun basma eylemini gösterir.", - "@floatingActionButtonLongPressDescription": { - "description": "FAB'IN uzun basma eyleminin açıklaması" - }, - "floatingActionButtonSinglePressDescription": "fAB'IN tek basma eylemini gösterir.", - "@floatingActionButtonSinglePressDescription": { - "description": "FAB'IN tek basma eyleminin açıklaması" - }, - "fonts": "Yazı Tipleri", - "@fonts": { - "description": "Yazı tipleri için ayarlar kategorisi" - }, - "forward": "İleri", - "@forward": { - "description": "İleri gitmek için etiket (örneğin, bir tarayıcıda)" - }, - "fullScreenNavigationSwipeDescription": "Soldan sağa hareketler devre dışı bırakıldığında geri dönmek için herhangi bir yere kaydırın", - "@fullScreenNavigationSwipeDescription": {}, - "fullscreenSwipeGestures": "Tam Ekran Kaydırma Hareketleri", - "@fullscreenSwipeGestures": { - "description": "Tam ekran kaydırma hareketleri için ayar" - }, - "general": "Genel", - "@general": { - "description": "Genel ayarlar kategorisi." - }, - "generalSettings": "Genel Ayarlar", - "@generalSettings": { - "description": "Ayarlar'da Alt Kategori" - }, - "gestures": "Hareketler", - "@gestures": {}, - "gettingStarted": "Başlarken", - "@gettingStarted": {}, - "hidCommunity": "HID Topluluğu", - "@hidCommunity": { - "description": "Bir topluluğu gizlemek için moderatör eylemi için kısa açıklama" - }, - "hideNsfwPostsFromFeed": "NSFW Gönderilerini Akıştan Gizle", - "@hideNsfwPostsFromFeed": { - "description": "NSFW gönderilerini akıştan gizlemek için geçiş yapın." - }, - "hideNsfwPreviews": "NSFW Önizlemelerini Bulanıklaştır", - "@hideNsfwPreviews": { - "description": "NSFW önizlemelerini bulanıklaştırmak için geçiş yapın." - }, - "hidePassword": "Parolayı Gizle", - "@hidePassword": {}, - "hideTopBarOnScroll": "Kaydırmada Üst Çubuğu Gizle", - "@hideTopBarOnScroll": { - "description": "Ayarlar kaydırmada üst çubuğu gizlemek için geçiş yapar" - }, - "hot": "Sıcak", - "@hot": {}, - "image": "Resim", - "@image": {}, - "imageCachingMode": "Görüntü Önbelleğe Alma Modu", - "@imageCachingMode": { - "description": "Görüntü önbelleğe alma modu ile ilgili ayar başlığı" - }, - "imageCachingModeAggressive": "Görüntüleri agresif bir şekilde önbelleğe alır (daha fazla bellek kullanır)", - "@imageCachingModeAggressive": { - "description": "Agresif görüntü önbelleğe alma modu için uzun açıklama" - }, - "imageCachingModeAggressiveShort": "Agresif", - "@imageCachingModeAggressiveShort": { - "description": "Agresif görüntü önbelleğe alma modu için kısa açıklama" - }, - "imageCachingModeRelaxed": "Görüntü önbelleklerinin süresinin dolmasına izin verin (daha az bellek kullanır, ancak görüntülerin daha sık yeniden yüklenmesine neden olur)", - "@imageCachingModeRelaxed": { - "description": "Rahat görüntü önbelleğe alma modu için uzun açıklama" - }, - "imageCachingModeRelaxedShort": "Rahat", - "@imageCachingModeRelaxedShort": { - "description": "Rahat görüntü önbelleğe alma modu için kısa açıklama" - }, - "importExportSettings": "Ayarları İçe/Dışa Aktar", - "@importExportSettings": { - "description": "İthalat ve ihracat ile ilgili ayarlar için kategori." - }, - "importSettings": "Ayarları İçe Aktar", - "@importSettings": { - "description": "Uygulama ayarlarını içe aktarma eylemi." - }, - "inReplyTo": "{community} içindeki {post} yazısına yanıt olarak", - "@inReplyTo": {}, - "inbox": "Gelen Kutusu", - "@inbox": {}, - "includeCommunity": "Topluluğu Dahil Et", - "@includeCommunity": {}, - "includeExternalLink": "Harici Bağlantıyı Dahil Et", - "@includeExternalLink": {}, - "includeImage": "Resmi Dahil Et", - "@includeImage": {}, - "includePostLink": "Gönderi Bağlantısını Dahil Et", - "@includePostLink": {}, - "includeText": "Metni Dahil Et", - "@includeText": {}, - "includeTitle": "Başlığı Dahil Et", - "@includeTitle": {}, - "information": "Bilgi", - "@information": { - "description": "Bilgi başlığı - FAB ayarları sayfasında kullanılır" - }, - "instance": "{count, plural, zero {Instance} one {Instance} other {Instances}} ", - "@instance": { - "description": "Bir örnek için yönlendirme" - }, - "instanceHasAlreadyBenAdded": "{instance} zaten eklendi.", - "@instanceHasAlreadyBenAdded": {}, - "internetOrInstanceIssues": "İnternete bağlı olmayabilirsin veya oturumun şu anda kullanılamıyor olabilir.", - "@internetOrInstanceIssues": {}, - "keywordFilterDescription": "Başlık veya gövdede herhangi bir anahtar kelime içeren gönderileri filtreler", - "@keywordFilterDescription": { - "description": "Anahtar kelime filtresi ayarlarının açıklaması" - }, - "keywordFilters": "Anahtar Kelime Filtreleri", - "@keywordFilters": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "language": "Dil", - "@language": { - "description": "Bir gönderi oluştururken veya düzenlerken etiketleyin." - }, - "languageNotAllowed": "Gönderdiğiniz topluluk, seçtiğiniz dilde gönderilere izin vermiyor. Başka bir dil deneyin.", - "@languageNotAllowed": { - "description": "Language_not_allowed Lemmy istisnası için hata mesajı" - }, - "large": "Büyük", - "@large": { - "description": "Büyük yazı tipi ölçeği için açıklama" - }, - "leftLongSwipe": "Sola Uzun Kaydırma", - "@leftLongSwipe": { - "description": "Sola uzun kaydırma ayarı" - }, - "leftShortSwipe": "Sola Kısa Kaydırma", - "@leftShortSwipe": { - "description": "Sola kısa kaydırma ayarı" - }, - "light": "Açık", - "@light": { - "description": "Işık temasını kullanarak açıklar" - }, - "link": "{count, plural, zero {Link} one {Link} other {Links}} ", - "@link": {}, - "linkActions": "Bağlantı Eylemleri", - "@linkActions": {}, - "linkHandlingCustomTabs": "Uygulama içi gömülü sistem tarayıcısında aç", - "@linkHandlingCustomTabs": { - "description": "Özel sekmeler için açıklama bağlantı işleme" - }, - "linkHandlingCustomTabsShort": "Uygulama içi gömülü", - "@linkHandlingCustomTabsShort": { - "description": "Özel sekmeler bağlantı yönetimi için kısa açıklama" - }, - "linkHandlingExternal": "Sistem tarayıcısında harici olarak aç", - "@linkHandlingExternal": { - "description": "Harici bağlantı kullanımı için açıklama" - }, - "linkHandlingExternalShort": "Harici", - "@linkHandlingExternalShort": { - "description": "Harici bağlantı kullanımı için kısa açıklama" - }, - "linkHandlingInApp": "Thunder'ın yerleşik tarayıcısını kullanın", - "@linkHandlingInApp": { - "description": "Uygulama içi bağlantı kullanımı için açıklama" - }, - "linkHandlingInAppShort": "Uygulama içi", - "@linkHandlingInAppShort": { - "description": "Uygulama içi bağlantı kullanımı için kısa açıklama" - }, - "linksBehaviourSettings": "Bağlantılar", - "@linksBehaviourSettings": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "loadMorePlural": "{count} yanıt daha yükle…", - "@loadMorePlural": {}, - "loadMoreSingular": "{count} yanıt daha yükle…", - "@loadMoreSingular": {}, - "local": "Yerel", - "@local": {}, - "localPosts": "Yerel Gönderiler", - "@localPosts": {}, - "lockPost": "Yazıyı Kilitle", - "@lockPost": { - "description": "Bir gönderiyi kilitleme eylemi (moderatör eylemi)" - }, - "lockedPost": "Kilitli Gönderi", - "@lockedPost": { - "description": "Bir gönderiyi kilitlemek için moderatör eylemi için kısa açıklama" - }, - "logOut": "Çıkış yap", - "@logOut": {}, - "login": "Oturum aç", - "@login": {}, - "loginFailed": "Giriş yapılamadı. Lütfen tekrar deneyin:({errorMessage})", - "@loginFailed": {}, - "loginSucceeded": "Giriş yapıldı.", - "@loginSucceeded": {}, - "loginToPerformAction": "Bu görevi gerçekleştirmek için oturum açmış olmanız gerekir.", - "@loginToPerformAction": {}, - "loginToSeeInbox": "Gelen kutunuzu görmek için oturum açın", - "@loginToSeeInbox": {}, - "malformedUri": "Sağladığınız bağlantı desteklenmeyen bir formatta. Lütfen geçerli bir bağlantı olduğundan emin olun.", - "@malformedUri": { - "description": "Desteklenmeyen bağlantı biçimiyle ilgili hata." - }, - "manageAccounts": "Hesapları Yönet", - "@manageAccounts": {}, - "markAllAsRead": "Tümünü Okundu Olarak İşaretle", - "@markAllAsRead": { - "description": "Tümünü okundu olarak işaretle eylemi" - }, - "markPostAsReadOnMediaView": "Medyayı Görüntüledikten Sonra Okundu Olarak İşaretle", - "@markPostAsReadOnMediaView": { - "description": "Medyayı görüntüledikten sonra gönderileri okundu olarak işaretlemek için geçiş yapın." - }, - "markPostAsReadOnScroll": "Kaydırmada Okundu Olarak İşaretle", - "@markPostAsReadOnScroll": { - "description": "Akışta ilerlerken gönderileri okundu olarak işaretlemek için geçiş yapın." - }, - "medium": "Orta", - "@medium": { - "description": "Orta yazı tipi ölçeği için açıklama" - }, - "mention": "{count, plural, zero {Bahsetme} one {Bahsetme} other {Bahsetme}}", - "@mention": {}, - "message": "{count, plural, zero {Mesaj} one {Mesaj} other {Mesajlar}}", - "@message": {}, - "metadataFontScale": "Meta Veri Yazı Tipi Ölçeği", - "@metadataFontScale": { - "description": "Meta veri yazı tipi ölçeğini ayarlama" - }, - "missingErrorMessage": "Hata mesajı yok", - "@missingErrorMessage": {}, - "modAdd": "Örnek Moderatörleri Ekle/Kaldır", - "@modAdd": { - "description": "Örnek moderatörleri ekleyen eylemler için modlog filtresi açıklaması" - }, - "modAddCommunity": "Topluluklara Moderatör Ekle/Kaldır", - "@modAddCommunity": { - "description": "Topluluklara moderatör ekleyen eylemler için modlog filtresi açıklaması" - }, - "modBan": "Örnek Moderatörlerini Yasakla/Yasağı Kaldır", - "@modBan": { - "description": "Örnek moderatörlerini yasaklayan eylemler için modlog filtresi açıklaması" - }, - "modBanFromCommunity": "Kullanıcıları Topluluklardan Yasakla/Yasağı Kaldır", - "@modBanFromCommunity": { - "description": "Kullanıcıları topluluklardan yasaklayan eylemler için modlog filtresi açıklaması" - }, - "modFeaturePost": "Özellikli/Özelliksiz Gönderiler", - "@modFeaturePost": { - "description": "Gönderileri içeren eylemler için modlog filtresi açıklaması" - }, - "modLockPost": "Gönderileri Kilitle/Kilidi Aç", - "@modLockPost": { - "description": "Gönderileri kilitleyen eylemler için modlog filtresi açıklaması" - }, - "modRemoveComment": "Yorumları Kaldır/Geri Yükle", - "@modRemoveComment": { - "description": "Yorumları kaldıran eylemler için modlog filtresi açıklaması" - }, - "modRemoveCommunity": "Toplulukları Kaldır/Geri Yükle", - "@modRemoveCommunity": { - "description": "Toplulukları kaldıran eylemler için modlog filtresi açıklaması" - }, - "modRemovePost": "Gönderileri Kaldır/Geri Yükle", - "@modRemovePost": { - "description": "Gönderileri kaldıran eylemler için modlog filtresi açıklaması" - }, - "modTransferCommunity": "Aktaran Topluluklar", - "@modTransferCommunity": { - "description": "Moderatörleri başka bir topluluğa aktaran eylemler için modlog filtresi açıklaması" - }, - "moderatedCommunities": "Yönetilen Topluluklar", - "@moderatedCommunities": { - "description": "Geçerli kullanıcı tarafından yönetilen toplulukların bir listesini açıklar." - }, - "moderator": "Moderatör", - "@moderator": { - "description": "Moderatör için rol adı" - }, - "moderatorActions": "Moderatör Eylemleri", - "@moderatorActions": { - "description": "Moderatörlerin gönderilerde gerçekleştirecekleri eylemler" - }, - "modlog": "Modlog", - "@modlog": { - "description": "Modlog görüntüleme seçeneği." - }, - "mostComments": "En Çok Yorumlananlar", - "@mostComments": {}, - "mustBeLoggedInComment": "Yorum yapmak için giriş yapmalısınız", - "@mustBeLoggedInComment": {}, - "mustBeLoggedInPost": "Gönderi oluşturmak için giriş yapmanız gerekir", - "@mustBeLoggedInPost": {}, - "navbarDoubleTapGestures": "Navbar Çift Dokunma Hareketleri", - "@navbarDoubleTapGestures": { - "description": "Navbar çift dokunma hareketleri için ayar" - }, - "navbarSwipeGestures": "Navbar Kaydırma Hareketleri", - "@navbarSwipeGestures": { - "description": "Gezinme çubuğu kaydırma hareketleri için ayar" - }, - "navigateDown": "Sonraki yorum", - "@navigateDown": {}, - "navigateUp": "Önceki yorum", - "@navigateUp": {}, - "navigation": "Navigasyon", - "@navigation": {}, - "nestedCommentIndicatorColor": "İç İçe Yorum Gösterge Rengi", - "@nestedCommentIndicatorColor": { - "description": "İç içe yorum gösterge rengi ayarı" - }, - "nestedCommentIndicatorStyle": "İç İçe Yorum Gösterge Stili", - "@nestedCommentIndicatorStyle": { - "description": "İç içe yorum gösterge stili için ayar" - }, - "newComments": "Yeni Yorumlar", - "@newComments": {}, - "newPost": "Yeni Gönderi", - "@newPost": { - "description": "Yeni gönderi için ayar" - }, - "new_": "Yeni", - "@new_": {}, - "noComments": "Ah. Hiç yorum yok.", - "@noComments": {}, - "noCommentsFound": "Yorum bulunamadı.", - "@noCommentsFound": {}, - "noCommunitiesFound": "Hiçbir topluluk bulunamadı.", - "@noCommunitiesFound": {}, - "noCommunityBlocks": "Engellenen topluluk yok.", - "@noCommunityBlocks": {}, - "noFavoritedCommunities": "Favori topluluk yok", - "@noFavoritedCommunities": { - "description": "Çekmecede favori topluluk yok mesajı" - }, - "noInstanceBlocks": "Engellenen örnek yok.", - "@noInstanceBlocks": {}, - "noItems": "Öğe yok", - "@noItems": { - "description": "Öğe olmadığında genel metin" - }, - "noKeywordFilters": "Anahtar kelime filtresi eklenmedi", - "@noKeywordFilters": { - "description": "Anahtar kelime eklenmemiş mesajı" - }, - "noLanguage": "Dil yok", - "@noLanguage": { - "description": "Bir gönderi dili seçerken dil yok girdisi" - }, - "noPostsFound": "Gönderi bulunamadı.", - "@noPostsFound": {}, - "noResultsFound": "Sonuç bulunamadı.", - "@noResultsFound": {}, - "noSubscriptions": "Abonelik Yok", - "@noSubscriptions": { - "description": "Çekmecede abonelik olmaması için mesaj" - }, - "noUserBlocks": "Engellenen kullanıcı yok.", - "@noUserBlocks": {}, - "noUsersFound": "Kullanıcı bulunamadı.", - "@noUsersFound": {}, - "notValidLemmyInstance": "{instance} geçerli bir Lemmy örneği gibi görünmüyor", - "@notValidLemmyInstance": {}, - "notValidUrl": "Geçerli bir URL değil", - "@notValidUrl": {}, - "nothingToShare": "Paylaşılacak bir şey yok", - "@nothingToShare": {}, - "notifications": "{count, plural, zero {Bildirim} one {Bildirimler} other {Bildirimler}}", - "@notifications": { - "description": "Bildirimler için mesaj biçimlendirme sayılır." - }, - "notificationsBehaviourSettings": "Bildirimler", - "@notificationsBehaviourSettings": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "notificationsNotAllowed": "Sistem ayarlarında Thunder için bildirimlere izin verilmez", - "@notificationsNotAllowed": { - "description": "Uygulama için bildirimlere artık ne zaman izin verildiğine ilişkin açıklama" - }, - "notificationsWarningDialog": "Bildirimler, tüm cihazlarda doğru çalışmayabilen deneysel bir özelliktir.\n\n· Kontroller her ~15 dakikada bir gerçekleşecek ve ek pil tüketecektir.\n\n· Başarılı bildirim olasılığını artırmak için pil optimizasyonlarını devre dışı bırakın.\n\nDaha fazla bilgi için aşağıdaki sayfaya bakın.", - "@notificationsWarningDialog": { - "description": "Bildirimler özelliği için uyarı iletişim kutusunun içeriği" - }, - "nsfwWarning": "NSFW - Göstermek için dokunun", - "@nsfwWarning": { - "description": "NSFW gönderileri için uyarı" - }, - "off": "kapalı", - "@off": {}, - "ok": "Tamam", - "@ok": {}, - "old": "Eski", - "@old": {}, - "on": "açık", - "@on": {}, - "onlyModsCanPostInCommunity": "Bu toplulukta yalnızca moderatörler gönderi paylaşabilir", - "@onlyModsCanPostInCommunity": {}, - "open": "Açık", - "@open": {}, - "openAccountSwitcher": "Hesap değiştiriciyi açın", - "@openAccountSwitcher": {}, - "openByDefault": "Varsayılan olarak aç", - "@openByDefault": { - "description": "Açılan bağlantıların varsayılan davranışını gösterir." - }, - "openInBrowser": "Tarayıcıda Aç", - "@openInBrowser": {}, - "openInstance": "Örneği Aç", - "@openInstance": {}, - "openLinksInExternalBrowser": "Bağlantıları Harici Tarayıcıda Aç", - "@openLinksInExternalBrowser": { - "description": "Bağlantıları harici bir tarayıcıda açmak için geçiş yapın." - }, - "openLinksInReaderMode": "Bağlantıları Okuyucu Modunda Aç", - "@openLinksInReaderMode": { - "description": "Mevcut olduğunda bağlantıları okuyucu modunda açmak için geçiş yapın." - }, - "openSettings": "Ayarları Aç", - "@openSettings": { - "description": "Kullanıcının sistem ayarlarını açmasını iste" - }, - "overview": "Genel Bakış", - "@overview": {}, - "password": "Parola", - "@password": {}, - "pending": "Beklemede", - "@pending": { - "description": "'Beklemede' için abonelik durumunu açıklar" - }, - "permissionDenied": "İzin Verilmedi", - "@permissionDenied": { - "description": "Kullanıcı işletim sistemi izinlerini reddettiğinde hata başlığı" - }, - "permissionDeniedMessage": "Thunder, reddedilen bu görüntüyü kaydetmek için bazı izinler gerektirir.", - "@permissionDeniedMessage": { - "description": "Kullanıcı işletim sistemi izinlerini reddettiğinde hata açıklaması" - }, - "pinToCommunity": "Topluluğa Sabitle", - "@pinToCommunity": { - "description": "Bir gönderiyi bir topluluğa sabitleme ayarı (moderatör eylemi)" - }, - "placeholderText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - "@placeholderText": { - "description": "Herhangi bir önizleme için yer tutucu metin. Bu, https://www.lipsum.com/ adresinden alınmıştır." - }, - "postBehaviourSettings": "Gönderiler", - "@postBehaviourSettings": { - "description": "Ayar -> Genel bölümündeki Alt Kategori" - }, - "postBody": "Gönderi Gövdesi", - "@postBody": {}, - "postBodySettings": "Gönderi Gövde Ayarları", - "@postBodySettings": { - "description": "Gönderi gövdesi ayarları için ayarlar başlığı" - }, - "postBodySettingsDescription": "Bu ayarlar, gönderi gövdesinin görüntülenmesini etkiler", - "@postBodySettingsDescription": { - "description": "Gönderi gövdesi ayarlarının açıklaması" - }, - "postBodyViewType": "Gönderi Gövdesi Görünüm Türü", - "@postBodyViewType": { - "description": "Gönderi gövdesi görünüm türü ayarı için ad ayarlama" - }, - "postContentFontScale": "Gönderi İçeriği Yazı Tipi Ölçeği", - "@postContentFontScale": { - "description": "Gönderi içeriği yazı tipi ölçeği ayarı" - }, - "postCreatedSuccessfully": "Gönderi başarıyla oluşturuldu!", - "@postCreatedSuccessfully": { - "description": "Kullanıcıya gönderisinin başarıyla oluşturulduğunu bildirme" - }, - "postLocked": "Gönderi kilitlendi. Yanıtlara izin verilmez.", - "@postLocked": {}, - "postMetadataInstructions": "İstediğiniz bilgileri sürükleyip bırakarak meta veri bilgilerini özelleştirebilirsiniz", - "@postMetadataInstructions": { - "description": "Gönderi meta veri bilgilerinin nasıl özelleştirileceğine ilişkin talimatlar. Bu, Görünüm -> Gönderi ayarları sayfasında bulunur" - }, - "postNSFW": "NSFW olarak işaretle", - "@postNSFW": {}, - "postPreview": "Verilen ayarlarla gönderinin önizlemesini göster", - "@postPreview": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki son önizleme açıklaması" - }, - "postSavedAsDraft": "Gönderi taslak olarak kaydedildi", - "@postSavedAsDraft": {}, - "postSwipeActions": "Kaydırma Sonrası Eylemler", - "@postSwipeActions": { - "description": "Kaydırma sonrası eylemler için ayar" - }, - "postSwipeGesturesHint": "Bunun yerine düğmeler mi kullanmak istiyorsunuz? Genel ayarlarda kartpostallarda görünen düğmeleri değiştirin.", - "@postSwipeGesturesHint": {}, - "postTitle": "Unvanı", - "@postTitle": {}, - "postTitleFontScale": "Gönderi Başlığı Yazı Tipi Ölçeği", - "@postTitleFontScale": { - "description": "Yazı başlığı yazı tipi ölçeği ayarı" - }, - "postTogglePreview": "Önizlemeyi Aç/Kapat", - "@postTogglePreview": {}, - "postURL": "URL", - "@postURL": {}, - "postUploadImageError": "Resim yüklenemedi", - "@postUploadImageError": {}, - "postViewType": "Gönderi Görüntüleme Türü", - "@postViewType": { - "description": "Ayar -> Görünüm -> Gönderiler bölümündeki son görünüm türü (kompakt/kart) etiketi" - }, - "posts": "Gönderiler", - "@posts": {}, - "preview": "Ön izleme", - "@preview": {}, - "profileAppliedSuccessfully": "{profile} başarıyla uygulandı!", - "@profileAppliedSuccessfully": {}, - "profiles": "Profiller", - "@profiles": {}, - "pureBlack": "Saf Siyah", - "@pureBlack": { - "description": "Saf siyah temayı kullanarak açıklar" - }, - "purgedComment": "Temizlenmiş Yorum", - "@purgedComment": { - "description": "Bir yorumu temizlemek için moderatör eylemi için kısa açıklama" - }, - "purgedCommunity": "Temizlenen Topluluk", - "@purgedCommunity": { - "description": "Bir topluluğu temizlemek için moderatör eylemi için kısa açıklama" - }, - "purgedPerson": "Tasfiye Edilen Kişi", - "@purgedPerson": { - "description": "Bir kişiyi temizlemek için moderatör eylemi için kısa açıklama" - }, - "purgedPost": "Temizlenmiş Gönderi", - "@purgedPost": { - "description": "Bir gönderiyi temizlemek için moderatör eylemi için kısa açıklama" - }, - "reachedTheBottom": "Hımmm. Dibe ulaşmışsın gibi görünüyor.", - "@reachedTheBottom": {}, - "readAll": "Tümünü Oku", - "@readAll": {}, - "reason": "Nedeni", - "@reason": { - "description": "Denetleme eyleminin nedeni (örneğin, gönderiyi kaldırma)" - }, - "reduceAnimations": "Animasyonları Azaltın", - "@reduceAnimations": { - "description": "Animasyonları azaltmak için geçiş yapın." - }, - "reducesAnimations": "Thunder içinde kullanılan animasyonları azaltır", - "@reducesAnimations": {}, - "refresh": "Yenile", - "@refresh": {}, - "refreshContent": "İçeriği Yenile", - "@refreshContent": {}, - "removalReason": "Çıkarma Nedeni", - "@removalReason": { - "description": "Bir gönderiyi kaldırma iletişim kutusunun başlığı" - }, - "remove": "Kaldır", - "@remove": {}, - "removeAccount": "Hesabı Kaldır", - "@removeAccount": {}, - "removeFromFavorites": "Favorilerden kaldır", - "@removeFromFavorites": { - "description": "Çekmecedeki bir topluluğu favorilerden kaldırma işlemi" - }, - "removeInstance": "Örneği kaldır", - "@removeInstance": {}, - "removeKeyword": "\"{ keyword }\" kaldırılsın mı?", - "@removeKeyword": { - "description": "Bir anahtar kelimeyi kaldırmanın açıklaması" - }, - "removeKeywordFilter": "Anahtar Kelimeyi Kaldır", - "@removeKeywordFilter": { - "description": "Anahtar kelimeyi kaldırmak için dialig başlığı" - }, - "removePost": "Gönderiyi Kaldır", - "@removePost": { - "description": "Bir gönderiyi kaldırma eylemi (moderatör eylemi)" - }, - "removedComment": "Silinen Yorum", - "@removedComment": { - "description": "Bir yorumu kaldırmak için moderatör eylemi için kısa açıklama" - }, - "removedCommunity": "Kaldırılan Topluluk", - "@removedCommunity": { - "description": "Bir topluluğu kaldırmak için moderatör eylemi için kısa açıklama" - }, - "removedCommunityFromSubscriptions": "Topluluk aboneliğinden çıkıldı", - "@removedCommunityFromSubscriptions": {}, - "removedInstanceMod": "Oluşum Modu Kaldırıldı", - "@removedInstanceMod": { - "description": "Mod'u bir oluşumdan kaldırmak için moderatör eylemi için kısa açıklama" - }, - "removedModFromCommunity": "Mod Topluluktan Kaldırıldı", - "@removedModFromCommunity": { - "description": "Bir modun bir topluluktan kaldırılması için moderatör eylemi için kısa açıklama" - }, - "removedPost": "Gönderi Kaldırıldı", - "@removedPost": { - "description": "Bir gönderiyi kaldırmak için moderatör eylemi için kısa açıklama" - }, - "reply": "{count, plural, zero {Yanıt} one {Yanıt} other {Yanıt}}", - "@reply": {}, - "replyNotSupported": "Bu görünümden yanıt vermek şu anda desteklenmiyor", - "@replyNotSupported": {}, - "replyToPost": "Gönderiyi Yanıtla", - "@replyToPost": {}, - "replyingTo": "{author} adlı kişiye yanıt veriliyor", - "@replyingTo": {}, - "report": "Rapor", - "@report": {}, - "reportComment": "Yorumu Bildir", - "@reportComment": {}, - "reset": "Sıfırla", - "@reset": { - "description": "Ayar -> Görünüm -> Gönderiler/Yorumlar bölümündeki sıfırlama düğmesi etiketi" - }, - "resetCommentPreferences": "Yorum tercihlerini sıfırla", - "@resetCommentPreferences": { - "description": "Yorum görünümü ayarlarındaki düğme için anlamsal etiket." - }, - "resetPostPreferences": "Gönderi tercihlerini sıfırla", - "@resetPostPreferences": { - "description": "Görünüm sonrası ayarlarında düğme için anlamsal etiket." - }, - "resetPreferences": "Tercihleri Sıfırla", - "@resetPreferences": {}, - "resetPreferencesAndData": "Tercihleri ve Verileri Sıfırla", - "@resetPreferencesAndData": { - "description": "Tüm kullanıcı tercihlerini ve verilerini sıfırlamak için hata ayıklama ayarı başlığı." - }, - "restore": "Geri Yükle", - "@restore": {}, - "restorePost": "Gönderiyi Geri Yükle", - "@restorePost": { - "description": "Bir gönderiyi geri yükleme eylemi (moderatör eylemi)" - }, - "restoredComment": "Geri Yüklenen Yorum", - "@restoredComment": { - "description": "Bir yorumu geri yüklemek için moderatör eylemi için kısa açıklama" - }, - "restoredCommentFromDraft": "Taslaktan geri yüklenen yorum", - "@restoredCommentFromDraft": {}, - "restoredCommunity": "Geri Yüklenen Topluluk", - "@restoredCommunity": { - "description": "Bir topluluğu geri yüklemek için moderatör eylemi için kısa açıklama" - }, - "restoredPost": "Geri Yüklenen Gönderi", - "@restoredPost": { - "description": "Bir gönderiyi geri yüklemek için moderatör eylemi için kısa açıklama" - }, - "restoredPostFromDraft": "Taslaktaki gönderi geri yüklendi", - "@restoredPostFromDraft": {}, - "retry": "Tekrar dene", - "@retry": {}, - "rightLongSwipe": "Sağa Uzun Kaydırma", - "@rightLongSwipe": { - "description": "Sağa uzun kaydırmayı ayarlama" - }, - "rightShortSwipe": "Sağa Kısa Kaydırma", - "@rightShortSwipe": { - "description": "Sağa kısa kaydırma ayarı" - }, - "save": "Kaydet", - "@save": {}, - "saveSettings": "Ayarları Kaydet", - "@saveSettings": { - "description": "Uygulama ayarlarını kaydetme eylemi." - }, - "saved": "Kaydedildi", - "@saved": {}, - "scaled": "Ölçekli", - "@scaled": {}, - "scrapeMissingLinkPreviews": "Scrape Eksik Bağlantı Önizlemeleri", - "@scrapeMissingLinkPreviews": { - "description": "Eksik harici bağlantı önizlemelerini sıyırmak için geçiş yapın." - }, - "scrapeMissingPreviews": "Etkinleştirmenin bir performans isabeti olacaktır.", - "@scrapeMissingPreviews": { - "description": "Bağlantı hurdaya çıkarmayı etkinleştirirken potansiyel performans etkisine dikkat edin." - }, - "screenReaderProfile": "Ekran Okuyucu Profili", - "@screenReaderProfile": {}, - "screenReaderProfileDescription": "Genel öğeleri azaltarak ve potansiyel olarak çakışan hareketleri kaldırarak Thunder'ı ekran okuyucular için optimize eder.", - "@screenReaderProfileDescription": {}, - "search": "Ara", - "@search": {}, - "searchByText": "Metne göre ara", - "@searchByText": {}, - "searchByUrl": "URL'ye göre ara", - "@searchByUrl": {}, - "searchComments": "Yorumlarda Ara", - "@searchComments": {}, - "searchCommentsFederatedWith": "{instance} ile federe edilmiş yorumları arayın", - "@searchCommentsFederatedWith": {}, - "searchCommunitiesFederatedWith": "{instance} ile birleştirilmiş toplulukları arayın", - "@searchCommunitiesFederatedWith": {}, - "searchInstance": "{instance} ara", - "@searchInstance": {}, - "searchInstancesFederatedWith": "{instance} ile federe edilmiş örnekleri ara", - "@searchInstancesFederatedWith": { - "description": "Örnekleri aramak için yer tutucu metin" - }, - "searchPostSearchType": "Gönderi Arama Türünü Seçin", - "@searchPostSearchType": {}, - "searchPostsFederatedWith": "{instance} ile federe edilmiş gönderileri arayın", - "@searchPostsFederatedWith": {}, - "searchTerm": "Arama terimi", - "@searchTerm": {}, - "searchUsersFederatedWith": "{instance} ile federe edilmiş kullanıcıları ara", - "@searchUsersFederatedWith": {}, - "selectAccountToPostAs": "Gönderilecek hesabı şu şekilde seçin:", - "@selectAccountToPostAs": { - "description": "Gönderirken hesap seçici için başlık" - }, - "selectCommunity": "Bir topluluk seçin", - "@selectCommunity": {}, - "selectFeedType": "Akış Türünü Seçin", - "@selectFeedType": {}, - "selectLanguage": "Dil seçin", - "@selectLanguage": { - "description": "Gönderi oluşturma sayfasında bir dil seçme istemi" - }, - "selectSearchType": "Arama Türünü Seçin", - "@selectSearchType": {}, - "sensitiveContentWarning": "Hassas içerik içerebilir. Göstermek için dokun.", - "@sensitiveContentWarning": { - "description": "Hassas içerik için uyarı (örneğin, modlog'dan)" - }, - "serverErrorComments": "Daha fazla yorum alınırken bir sunucu hatasıyla karşılaşıldı: {message}", - "@serverErrorComments": {}, - "setAction": "Eylem Ayarla", - "@setAction": { - "description": "Ayarlanacak eylem için etiket (kısa basma, uzun basma)" - }, - "setLongPress": "Uzun basma eylemi olarak ayarla", - "@setLongPress": {}, - "setShortPress": "Kısa basın eylemi olarak ayarla", - "@setShortPress": {}, - "settingTypeNotSupported": "{settingType} türündeki ayarlar henüz desteklenmiyor.", - "@settingTypeNotSupported": {}, - "settings": "Ayarlar", - "@settings": {}, - "settingsFeedCards": "Bu ayarlar ana akıştaki kartlar için geçerlidir, gönderiler açılırken eylemler her zaman kullanılabilir.", - "@settingsFeedCards": { - "description": "Ana beslemedeki kartların görüntülenmesini kontrol etme ayarları." - }, - "share": "Paylaş", - "@share": {}, - "shareLink": "Bağlantıyı Paylaş", - "@shareLink": {}, - "shareMedia": "Medyayı Paylaş", - "@shareMedia": {}, - "sharePost": "Gönderiyi Paylaş", - "@sharePost": {}, - "showAll": "Tümünü Göster", - "@showAll": {}, - "showBotAccounts": "Bot Hesaplarını Göster", - "@showBotAccounts": { - "description": "Bot hesaplarını gösterme seçeneği." - }, - "showCommentActionButtons": "Yorum Eylem Düğmelerini Göster", - "@showCommentActionButtons": { - "description": "Yorum eylem düğmelerini göstermek için geçiş yapın." - }, - "showCrossPosts": "Çapraz Gönderileri Göster", - "@showCrossPosts": { - "description": "Çapraz gönderileri göstermek için geçiş yapın." - }, - "showEdgeToEdgeImages": "Kenardan Kenara Görüntüleri Göster", - "@showEdgeToEdgeImages": { - "description": "Kenardan kenara görüntüleri görüntülemek için geçiş yapın." - }, - "showFullDate": "Tam Tarihi Göster", - "@showFullDate": { - "description": "Gönderilerde tam tarihi göstermek için aç/kapat" - }, - "showFullDateDescription": "Gönderilerde tam tarihi göster", - "@showFullDateDescription": { - "description": "Gönderilerde tam tarihi göstermek için açıklama" - }, - "showFullHeightImages": "Tam Yükseklikte Resimleri Göster", - "@showFullHeightImages": { - "description": "Tam boy görüntüleri görüntülemek için geçiş yapın." - }, - "showInAppUpdateNotifications": "Yeni GitHub Sürümlerinden Haberdar Olun", - "@showInAppUpdateNotifications": { - "description": "Yeni GitHub sürümlerinden haberdar olmak için geçiş yapın." - }, - "showLess": "Daha az göster", - "@showLess": {}, - "showMore": "Daha fazla göster", - "@showMore": {}, - "showPassword": "Parolayı Göster", - "@showPassword": {}, - "showPostAuthor": "Gönderi Yazarını Göster", - "@showPostAuthor": { - "description": "Gönderi yazarını göstermek için aç/kapat." - }, - "showPostCommunityIcons": "Topluluk Simgelerini Göster", - "@showPostCommunityIcons": { - "description": "Topluluk simgelerini göstermek için geçiş yapın." - }, - "showPostSaveAction": "Kaydet Düğmesini Göster", - "@showPostSaveAction": { - "description": "Kaydet düğmesini göstermek için geçiş yapın." - }, - "showPostTextContentPreview": "Metin Önizlemesini Göster", - "@showPostTextContentPreview": { - "description": "Metin önizlemelerini göstermek için geçiş yapın." - }, - "showPostTitleFirst": "Önce Başlığı Göster", - "@showPostTitleFirst": { - "description": "Önce yazı başlığını göstermek için geçiş yapın." - }, - "showPostVoteActions": "Oy Düğmelerini Göster", - "@showPostVoteActions": { - "description": "Oy düğmelerini göstermek için geçiş yapın." - }, - "showReadPosts": "Okunan Gönderileri Göster", - "@showReadPosts": { - "description": "Akıştaki okunmuş yayınları gösterme ayarı." - }, - "showScoreCounters": "Kullanıcı Puanlarını Göster", - "@showScoreCounters": { - "description": "Kullanıcı puanlarını görüntülemek için geçiş yapın." - }, - "showScores": "Gönderi/Yorum Puanlarını Göster", - "@showScores": { - "description": "Yazılarda ve yorumlarda puanları gösterme seçeneği." - }, - "showTextPostIndicator": "Metin Gönderisi Göstergesini Göster", - "@showTextPostIndicator": { - "description": "Metin gönderisi göstergesini göstermek için geçiş yapın." - }, - "showThumbnailPreviewOnRight": "Küçük Resimleri Sağda Göster", - "@showThumbnailPreviewOnRight": { - "description": "Küçük resimleri sağ tarafta göstermek için geçiş yapın." - }, - "showUserDisplayNames": "Kullanıcı Görünen Adlarını Göster", - "@showUserDisplayNames": { - "description": "Kullanıcı görünen adlarını göstermek için geçiş yapın." - }, - "showUserInstance": "Kullanıcı Örneğini Göster", - "@showUserInstance": { - "description": "Kullanıcı örneğini göstermek için geçiş yapın." - }, - "sidebar": "Kenar çubuğu", - "@sidebar": {}, - "sidebarBottomNavDoubleTapDescription": "Kenar çubuğunu açmak için alt gezinmeye iki kez dokunun", - "@sidebarBottomNavDoubleTapDescription": {}, - "sidebarBottomNavSwipeDescription": "Kenar çubuğunu açmak için alt gezinmeyi kaydırın", - "@sidebarBottomNavSwipeDescription": {}, - "small": "Küçük", - "@small": { - "description": "Küçük yazı tipi ölçeği için açıklama" - }, - "somethingWentWrong": "Ne yazık ki bir hata oluştu!", - "@somethingWentWrong": {}, - "sortBy": "Sıralama Ölçütü", - "@sortBy": {}, - "sortByTop": "En üste göre sırala", - "@sortByTop": {}, - "sortOptions": "Sıralama Seçenekleri", - "@sortOptions": {}, - "spoiler": "Spoiler", - "@spoiler": { - "description": "Spoyler için yer tutucu etiket" - }, - "submit": "Gönder", - "@submit": {}, - "subscribe": "Abone ol", - "@subscribe": {}, - "subscribeToCommunity": "Topluluğa Abone Ol", - "@subscribeToCommunity": { - "description": "Bir topluluğa abone olma eylemi" - }, - "subscribed": "Abone olundu", - "@subscribed": {}, - "subscriptions": "Abonelikler", - "@subscriptions": {}, - "successfullyBlocked": "Engellendi.", - "@successfullyBlocked": {}, - "successfullyBlockedCommunity": "Engellenen {communityName}", - "@successfullyBlockedCommunity": {}, - "successfullyBlockedUser": "Engellenen {username}", - "@successfullyBlockedUser": { - "description": "Bir kullanıcıyı başarıyla engelleme bildirimi" - }, - "successfullyUnblocked": "Engeli kaldırıldı.", - "@successfullyUnblocked": {}, - "successfullyUnblockedCommunity": "{communityName} adlı topluluğun engelini kaldırdı", - "@successfullyUnblockedCommunity": {}, - "successfullyUnblockedUser": "{username} adlı kullanıcının engeli kaldırıldı", - "@successfullyUnblockedUser": { - "description": "Bir kullanıcının engelini başarıyla kaldırmak için bildirim" - }, - "suggestedTitle": "Önerilen başlık", - "@suggestedTitle": { - "description": "Gönderi için önerilen başlık." - }, - "system": "Sistem", - "@system": { - "description": "Tema için sistem ayarlarını kullanmayı açıklar" - }, - "tabletMode": "Tablet Modu (2 sütunlu görünüm)", - "@tabletMode": { - "description": "2 sütunlu tablet modunu etkinleştirmek için geçiş yapın." - }, - "tapToExit": "Çıkmak için tekrar BACK'e basın", - "@tapToExit": {}, - "tappableAuthorCommunity": "Dokunulabilir Yazarlar ve Topluluklar", - "@tappableAuthorCommunity": { - "description": "Yazarları ve toplulukları dokunulabilir hale getirmek için geçiş yapın." - }, - "text": "Metin", - "@text": {}, - "theme": "Tema", - "@theme": { - "description": "Tema için ayar" - }, - "themeAccentColor": "Vurgu Renkleri", - "@themeAccentColor": { - "description": "Tema vurgu rengi ayarı" - }, - "theming": "Tema", - "@theming": { - "description": "Ayarlar'da Tema Başlığı -> Görünüm -> Tema" - }, - "timeoutComments": "Hata: Yorumlar alınmaya çalışılırken zaman aşımı", - "@timeoutComments": {}, - "timeoutErrorMessage": "Yanıt bekleyen bir zaman aşımı vardı.", - "@timeoutErrorMessage": {}, - "timeoutSaveComment": "Hata: Bir yorumu kaydetmeye çalışırken zaman aşımı", - "@timeoutSaveComment": {}, - "timeoutSavingPost": "Hata: Gönderiyi kaydetmeye çalışırken zaman aşımı.", - "@timeoutSavingPost": {}, - "timeoutUpvoteComment": "Hata: Yoruma oy vermeye çalışırken zaman aşımı", - "@timeoutUpvoteComment": {}, - "timeoutVotingPost": "Hata: Gönderiye oy vermeye çalışırken zaman aşımı.", - "@timeoutVotingPost": {}, - "toggelRead": "Okumayı Aç/Kapat", - "@toggelRead": {}, - "top": "Üst", - "@top": {}, - "topAll": "Tüm zamanların en iyisi", - "@topAll": {}, - "topDay": "Bugünün En İyileri", - "@topDay": {}, - "topHour": "Son Bir Saatteki En Yüksek", - "@topHour": {}, - "topMonth": "En İyi Ay", - "@topMonth": {}, - "topNineMonths": "Son 9 Ayın Zirvesi", - "@topNineMonths": { - "description": "Son 9 ayda en üst için sıralama modu" - }, - "topSixHour": "Son 6 Saatteki Zirve", - "@topSixHour": {}, - "topSixMonths": "Son 6 Ayın Zirvesi", - "@topSixMonths": { - "description": "Son 6 ayda en üst için sıralama modu" - }, - "topThreeMonths": "Son 3 Ayın Zirvesi", - "@topThreeMonths": { - "description": "Son 3 aydaki en iyi için sıralama modu" - }, - "topTwelveHour": "Son 12 Saatteki Zirve", - "@topTwelveHour": {}, - "topWeek": "En İyi Hafta", - "@topWeek": {}, - "topYear": "En İyi Yıl", - "@topYear": {}, - "totp": "TOTP (isteğe bağlı)", - "@totp": {}, - "transferredModToCommunity": "Aktarılan Topluluk", - "@transferredModToCommunity": { - "description": "Bir topluluğu aktarmak için moderatör eylemi için kısa açıklama" - }, - "translationsMayNotBeComplete": "Çevirilerin tamamlanamayabileceğini lütfen unutmayın", - "@translationsMayNotBeComplete": { - "description": "Ayarlarda bir dil seçerken çevirilerin tamamlanamayabileceği uyarısı" - }, - "trendingCommunities": "Popüler Topluluklar", - "@trendingCommunities": {}, - "trySearchingFor": "Şunu aramayı deneyin...", - "@trySearchingFor": { - "description": "Kullanıcının farklı bir tür aramayı denemesi için öneri" - }, - "unableToFindCommunity": "Topluluk bulunamıyor", - "@unableToFindCommunity": {}, - "unableToFindCommunityName": "'{ communityName }' topluluğu bulunamıyor", - "@unableToFindCommunityName": { - "description": "Ad da dahil olmak üzere bir topluluk bulamadığımız zaman için hata mesajı" - }, - "unableToFindCommunityOnInstance": "Seçilen kullanıcı örneğinde seçilen topluluk bulunamıyor.", - "@unableToFindCommunityOnInstance": { - "description": "Bir örnekte bir topluluk bulamadığımızda oluşan hata mesajı" - }, - "unableToFindInstance": "Örnek bulunamıyor", - "@unableToFindInstance": {}, - "unableToFindLanguage": "Dil bulunamıyor", - "@unableToFindLanguage": { - "description": "Dil bulunamadığında hata mesajı." - }, - "unableToFindPost": "Gönderi bulunamıyor", - "@unableToFindPost": { - "description": "Gönderi bulamadığımızda hata mesajı" - }, - "unableToFindUser": "Kullanıcı bulunamıyor", - "@unableToFindUser": {}, - "unableToFindUserName": "'{username}' kullanıcısı bulunamıyor", - "@unableToFindUserName": {}, - "unableToLoadImage": "Görüntü yüklenemiyor", - "@unableToLoadImage": { - "description": "İkili bir görüntü yükleyemediğimiz zamanlar için yer tutucu" - }, - "unableToLoadImageFrom": "{domain} alanından görüntü yüklenemiyor", - "@unableToLoadImageFrom": { - "description": "Bir URL'den resim yükleyemediğimiz zamanlar için yer tutucu" - }, - "unableToLoadInstance": "{instance} yüklenemiyor", - "@unableToLoadInstance": {}, - "unableToLoadPostsFrominstance": "{instance} konumundan gönderiler yüklenemiyor", - "@unableToLoadPostsFrominstance": {}, - "unableToLoadReplies": "Daha fazla yanıt yüklenemiyor.", - "@unableToLoadReplies": {}, - "unableToNavigateToInstance": "{instanceHost} konumuna gidilemiyor. Geçerli bir Lemmy örneği olmayabilir.", - "@unableToNavigateToInstance": { - "description": "Bir Lemmy örneğine gidemediğimiz zamanlar için hata mesajı" - }, - "unbannedUser": "Yasaklanmamış Kullanıcı", - "@unbannedUser": { - "description": "Bir kullanıcının yasağını kaldırmak için moderatör eylemi için kısa açıklama" - }, - "unbannedUserFromCommunity": "Topluluktan Yasaklanmamış Kullanıcı", - "@unbannedUserFromCommunity": { - "description": "Bir kullanıcının bir topluluktaki yasağını kaldırmak için moderatör eylemi için kısa açıklama" - }, - "unblockInstance": "Oluşumun Engelini Kaldır", - "@unblockInstance": { - "description": "Bir örneğin engelini kaldırmak için araç ipucu" - }, - "understandEnable": "Anlıyorum, Etkinleştir", - "@understandEnable": { - "description": "Bir şeyi onaylama ve etkinleştirme eylemi" - }, - "unexpectedError": "Beklenmedik Hata", - "@unexpectedError": {}, - "unfeaturedPost": "Özelliksiz Gönderi", - "@unfeaturedPost": { - "description": "Bir gönderinin özelliğini kaldırmak için moderatör eylemi için kısa açıklama" - }, - "unhidCommunity": "Unhid Topluluğu", - "@unhidCommunity": { - "description": "Bir topluluğu göstermek için moderatör eylemi için kısa açıklama" - }, - "unlockPost": "Gönderinin Kilidini Aç", - "@unlockPost": { - "description": "Bir gönderinin kilidini açma eylemi (moderatör eylemi)" - }, - "unlockedPost": "Kilitli Olmayan Gönderi", - "@unlockedPost": { - "description": "Bir gönderinin kilidini açmak için moderatör eylemi için kısa açıklama" - }, - "unpinFromCommunity": "Topluluktan sabitlemeyi kaldır", - "@unpinFromCommunity": { - "description": "Bir topluluktan bir gönderinin sabitlemesini kaldırma ayarı (moderatör eylemi)" - }, - "unreachable": "Ulaşılamıyor", - "@unreachable": { - "description": "Şu anda ulaşılamayan bir örneği açıklar" - }, - "unsubscribe": "Abonelikten çık", - "@unsubscribe": {}, - "unsubscribeFromCommunity": "Topluluk aboneliğinden çık", - "@unsubscribeFromCommunity": { - "description": "Bir topluluğun aboneliğinden çıkma eylemi" - }, - "unsubscribePending": "Abonelikten çıkma (abonelik beklemede)", - "@unsubscribePending": {}, - "unsubscribed": "Abonelikten çıkıldı", - "@unsubscribed": {}, - "updateReleased": "Güncelleme yayınlandı: {version}", - "@updateReleased": {}, - "uploadImage": "Resim yükle", - "@uploadImage": {}, - "upvote": "Artı oy", - "@upvote": {}, - "uriNotSupported": "Bu tür bir bağlantı şu anda desteklenmiyor.", - "@uriNotSupported": { - "description": "Şu anda desteklenmeyen bağlantı türü." - }, - "url": "URL", - "@url": {}, - "useAdvancedShareSheet": "Gelişmiş Paylaşım Sayfasını Kullan", - "@useAdvancedShareSheet": { - "description": "Gelişmiş paylaşım sayfasını kullanmak için geçiş yapın." - }, - "useCompactView": "Küçük gönderiler için etkinleştirin, büyükler için devre dışı bırakın.", - "@useCompactView": { - "description": "Küçük gönderiler için kompakt görünümü etkinleştirme veya devre dışı bırakma seçeneği." - }, - "useMaterialYouTheme": "Material You Temasını Kullanın", - "@useMaterialYouTheme": { - "description": "Material You temasını kullanmak için geçiş yapın." - }, - "useMaterialYouThemeDescription": "Seçilen özel temayı geçersiz kılar", - "@useMaterialYouThemeDescription": { - "description": "Material You temasını kullanma ayarının altyazısı" - }, - "useSuggestedTitle": "Önerilen başlığı kullanın: {title}", - "@useSuggestedTitle": {}, - "user": "Kullanıcı", - "@user": { - "description": "Kullanıcı için rol adı" - }, - "userFormat": "Kullanıcı Biçimi", - "@userFormat": { - "description": "Kullanıcının tam adı formatı için ayar" - }, - "userNotLoggedIn": "Kullanıcı oturum açmadı", - "@userNotLoggedIn": {}, - "userProfiles": "Kullanıcı Profilleri", - "@userProfiles": { - "description": "Kullanıcı profilleriyle ilgili ayarlar." - }, - "userSettingDescription": "Bu ayarlar Lemmy hesabınızla senkronize edilir ve yalnızca hesap başına esasına göre uygulanır.", - "@userSettingDescription": { - "description": "Ayarların mevcut kullanıcıya global olarak uygulandığını açıklayan açıklama." - }, - "username": "Kullanıcı adı", - "@username": {}, - "users": "Kullanıcılar", - "@users": {}, - "viewAll": "Tümünü görüntüle", - "@viewAll": { - "description": "Bir şeyin tamamını görüntülemek için bir başlık (örneğin, örnekler)" - }, - "viewAllComments": "Tüm yorumları görüntüle", - "@viewAllComments": {}, - "visitCommunity": "Topluluğu ziyaret edin", - "@visitCommunity": {}, - "visitInstance": "Örneği Ziyaret Et", - "@visitInstance": {}, - "visitUserProfile": "Kullanıcı Profilini Ziyaret Edin", - "@visitUserProfile": {}, - "warning": "Uyarı", - "@warning": { - "description": "Uyarı iletişim kutuları başlığı" - }, - "xDownvotes": "{x} olumsuz oy", - "@xDownvotes": {}, - "xScore": "{x} puan", - "@xScore": { - "description": "Gönderi veya yorumun toplam puanı" - }, - "xUpvotes": "{x} destek oyu", - "@xUpvotes": {} -} +{ + "about": "Hakkında", + "@about": { + "description": "Ayarlar kategorisi hakkında." + }, + "accessibility": "Erişilebilirlik", + "@accessibility": {}, + "accessibilityProfilesDescription": "Erişilebilirlik profilleri, belirli bir erişilebilirlik gereksinimini karşılamak için birkaç ayarı aynı anda uygulamaya olanak sağlar.", + "@accessibilityProfilesDescription": {}, + "account": "{count, plural, zero {Hesap} one {Hesap} other {Hesaplar} } ", + "@account": { + "description": "Kullanıcı hesabını tanımlar" + }, + "accountBirthday": "Hesap Doğum Günü {additionalInfo}", + "@accountBirthday": { + "description": "Bugünün tarihinde oluşturulan hesaplar için kullanıcı tanımlayıcısı" + }, + "accountSettingOverrideWarning": "Hesap ayarlarınız aşağıdaki ayarların üzerine yazılır.", + "@accountSettingOverrideWarning": { + "description": "Hesap ayarlarının geçersiz kılınması için uyarı" + }, + "accountSettings": "Hesap Ayarları", + "@accountSettings": {}, + "accountSwitchParentCommentNotFound": "Seçilen yorum '{instance}' üzerinde bulunamadı. Önceki hesaba geri dönülüyor.", + "@accountSwitchParentCommentNotFound": { + "description": "Yanıtlanacak yorumu çözümleyemediğimizde gösterilecek hata mesajı" + }, + "accountSwitchPostNotFound": "Seçilen gönderi '{instance}' üzerinde bulunamadı. Önceki hesaba geri dönülüyor.", + "@accountSwitchPostNotFound": { + "description": "Yanıt verilecek gönderi çözümlenemediğinde hata mesajı" + }, + "actionColors": "Eylem Renkleri", + "@actionColors": { + "description": "Eylem renkleri için başlık ayarlama" + }, + "actionColorsRedirect": "Renkleri özelleştirmek mi istiyorsunuz?", + "@actionColorsRedirect": { + "description": "Eylem renklerine yönlendirme ipucu" + }, + "actions": "Eylemler", + "@actions": {}, + "active": "Aktif", + "@active": {}, + "add": "Ekle", + "@add": {}, + "addAccount": "Hesap Ekle", + "@addAccount": {}, + "addAccountToSeeProfile": "Hesabınızı görmek için giriş yapın.", + "@addAccountToSeeProfile": {}, + "addAnonymousInstance": "Anonim Örnek Ekle", + "@addAnonymousInstance": {}, + "addDiscussionLanguage": "Dil Ekle", + "@addDiscussionLanguage": { + "description": "Dil eklemek için metin alanı ipucu" + }, + "addKeywordFilter": "Anahtar Kelime Ekle", + "@addKeywordFilter": { + "description": "Anahtar kelime eklemek için metin alanı ipucu" + }, + "addToFavorites": "Favorilere ekle", + "@addToFavorites": { + "description": "Çekmecede bir topluluğu favorilere eklemek için eylem" + }, + "addUserLabel": "Kullanıcı Etiketi Ekle", + "@addUserLabel": { + "description": "Bir kullanıcı etiketi ekleme eylemi" + }, + "addedCommunityToSubscriptions": "Topluluğa abone olundu", + "@addedCommunityToSubscriptions": {}, + "addedInstanceMod": "Eklenen Örnek Mod", + "@addedInstanceMod": { + "description": "Bir örneğe mod eklemek için moderatör eyleminin kısa açıklaması" + }, + "addedModToCommunity": "Topluluğa Mod Eklendi", + "@addedModToCommunity": { + "description": "Bir topluluğa bir moderatör eklemek için moderatör eyleminin kısa açıklaması" + }, + "admin": "Yönetici", + "@admin": { + "description": "Yönetici için rol adı" + }, + "advanced": "İleri düzey", + "@advanced": { + "description": "Gelişmiş ayarlar bölümüne doğru" + }, + "ago": "{time} önce", + "@ago": { + "description": "Geçmişte bir süreyi temsil eder" + }, + "all": "Hepsi", + "@all": {}, + "allPosts": "Tüm Gönderiler", + "@allPosts": {}, + "allowOpenSupportedLinks": "Uygulamanın desteklenen bağlantıları açmasına izin ver.", + "@allowOpenSupportedLinks": { + "description": "Uygulamanın desteklenen bağlantıları açmasına izin verme seçeneği." + }, + "alreadyPostedTo": "Zaten gönderildi", + "@alreadyPostedTo": {}, + "always": "Her zaman", + "@always": {}, + "andXMore": "ve {count} daha fazla", + "@andXMore": {}, + "animations": "Animasyonlar", + "@animations": {}, + "anonymous": "Anonim", + "@anonymous": {}, + "appLanguage": "Uygulama Dili", + "@appLanguage": { + "description": "Ayarlar'da dil seçimi." + }, + "appearance": "Görünüm", + "@appearance": { + "description": "Ayarlar'da Görünüm Başlığı -> Görünüm" + }, + "applePushNotificationService": "Apple Push Bildirim Servisi", + "@applePushNotificationService": { + "description": "Apple Push Bildirim Servisi için bildirim türünü tanımlar." + }, + "applied": "Uygulanan", + "@applied": {}, + "apply": "Uygula", + "@apply": {}, + "areNotificationsAllowedBySystem": "Sistem tarafından bildirimlere izin verilir: {yesOrNo}", + "@areNotificationsAllowedBySystem": { + "description": "İşletim sistemi izinlerinin bildirimlere izin verip vermediğini gösteren bir durum göstergesi" + }, + "back": "Geri", + "@back": {}, + "backButton": "Geri düğmesi", + "@backButton": {}, + "backToTop": "Başa Dön", + "@backToTop": {}, + "backgroundCheckWarning": "Bildirim kontrolünün ekstra pil tüketimine neden olacağını unutmayın.", + "@backgroundCheckWarning": { + "description": "Bildirimleri etkinleştirme uyarısı" + }, + "bannedUser": "Yasaklanmış Kullanıcı", + "@bannedUser": { + "description": "Moderatör eylemi olarak bir kullanıcının yasaklanması için kısa açıklama" + }, + "bannedUserFromCommunity": "Topluluktan Yasaklanan Kullanıcı", + "@bannedUserFromCommunity": { + "description": "Bir kullanıcının bir topluluktan yasaklanması için moderatör eyleminin kısa açıklaması" + }, + "base": "Taban", + "@base": { + "description": "Temel yazı tipi ölçeği için açıklama" + }, + "blockCommunity": "Blok Topluluğu", + "@blockCommunity": {}, + "blockInstance": "Blok Örneği", + "@blockInstance": {}, + "blockManagement": "Blok Yönetimi", + "@blockManagement": { + "description": "Kullanıcı, topluluk ve örnek engelleme ayarları için ayarlar" + }, + "blockSettingLabel": "Kullanıcı/Topluluk/Örnek Engelleri", + "@blockSettingLabel": { + "description": "Kullanıcı, topluluk ve örnek engelleme ayarları için ayarlar" + }, + "blockUser": "Kullanıcıyı Engelle", + "@blockUser": {}, + "blockedCommunities": "Engellenmiş Topluluklar", + "@blockedCommunities": {}, + "blockedInstances": "Engellenmiş Örnekler", + "@blockedInstances": {}, + "blockedUsers": "Engellenen Kullanıcılar", + "@blockedUsers": {}, + "blue": "Mavi", + "@blue": { + "description": "Mavi renk" + }, + "bold": "Cesur", + "@bold": { + "description": "Kalın isim kalınlığı/ağırlığı" + }, + "boldCommunityName": "Cesur Topluluk Adı", + "@boldCommunityName": { + "description": "Topluluk adını kalınlaştırma ayarı" + }, + "boldInstanceName": "Kalın Örnek İsmi", + "@boldInstanceName": { + "description": "Örneğin adını kalınlaştırma ayarı" + }, + "boldUserName": "Cesur Kullanıcı Adı", + "@boldUserName": { + "description": "Kullanıcı adını kalınlaştırma ayarı" + }, + "bot": "Bot", + "@bot": { + "description": "Bir bot kullanıcısı için etiket." + }, + "browserMode": "Bağlantı işleme", + "@browserMode": { + "description": "Bağlantı işleme ayarı başlığı (içeride browserMode olarak adlandırılır)" + }, + "browsingAnonymously": "Şu anda {instance} sitesinde anonim olarak geziniyorsunuz.", + "@browsingAnonymously": {}, + "cancel": "İptal", + "@cancel": {}, + "cannotReportOwnComment": "Kendi yorumunuz için bir rapor sunamazsınız.", + "@cannotReportOwnComment": {}, + "cantBlockAdmin": "Bir örnek yöneticisini engelleyemezsiniz.", + "@cantBlockAdmin": {}, + "cantBlockYourself": "Kendini engelleyemezsin.", + "@cantBlockYourself": {}, + "cardPostCardMetadataItems": "Kart Görünümü Meta Verisi", + "@cardPostCardMetadataItems": { + "description": "Kart posta kartı metaveri öğeleri için ayar adı" + }, + "cardView": "Kart Görünümü", + "@cardView": { + "description": "Ayarlar -> Görünüm -> Gönderiler için kart görünümü seçeneği etiketi" + }, + "cardViewDescription": "Kart görünümünü ayarları ayarlamak için etkinleştirin", + "@cardViewDescription": { + "description": "Ayarlar -> Görünüm -> Gönderiler alt kategorisindeki kart görünümü için açıklama" + }, + "cardViewSettings": "Kart Görünümü Ayarları", + "@cardViewSettings": { + "description": "Ayarlar -> Görünüm -> Gönderiler alt kategorisi" + }, + "changeAccountSettingsFor": "Hesap ayarlarını değiştirin için", + "@changeAccountSettingsFor": { + "description": "Hesap ayarlarını değiştirirken profilleyici modülüne yönelme" + }, + "changeNotificationSettings": "Bildirim ayarlarını değiştir...", + "@changeNotificationSettings": { + "description": "Bildirim ayarlarına gitmek için bir kısayol" + }, + "changePassword": "Şifre Değiştir", + "@changePassword": { + "description": "Şifreyi Değiştirme Eylemi" + }, + "changePasswordWarning": "Şifrenizi değiştirmek için, örnek siteye yönlendirileceksiniz. \n\nDevam etmek istediğinizden emin misiniz?", + "@changePasswordWarning": { + "description": "Şifre değiştirme için uyarı etiketi" + }, + "changeSort": "Sıralamayı Değiştir", + "@changeSort": {}, + "clearCache": "Önbelleği Temizle ({cacheSize})", + "@clearCache": { + "description": "İletişim kutusunda önbelleği temizleme eylemi için etiket" + }, + "clearDatabase": "Veritabanını Temizle", + "@clearDatabase": { + "description": "İletişim kutusunda yerel veritabanını temizleme eylemi için etiket" + }, + "clearPreferences": "Tercihleri Temizle", + "@clearPreferences": { + "description": "İletişim kutusunda yerel kullanıcı tercihlerini temizleme eylemi için etiket" + }, + "clearSearch": "Aramayı Temizle", + "@clearSearch": {}, + "clearedCache": "Önbellek başarıyla temizlendi.", + "@clearedCache": { + "description": "Önbelleğin başarıyla temizlendiğini belirten mesaj" + }, + "clearedDatabase": "Yerel veritabanı temizlendi. Yeni değişikliklerin etkili olması için Thunder'ı yeniden başlatın.", + "@clearedDatabase": {}, + "clearedUserPreferences": "Tüm kullanıcı tercihleri temizlendi", + "@clearedUserPreferences": {}, + "close": "Kapat", + "@close": {}, + "collapse": "Çökme", + "@collapse": { + "description": "Bir şeyi çökertme eylemi" + }, + "collapseCommentPreview": "Yorum Önizlemesini Daralt", + "@collapseCommentPreview": { + "description": "Ayarlar -> Görünüm -> Yorumlar bölümünde çökme düğmesi için semantik etiket" + }, + "collapseInformation": "Bilgi Çöküşü", + "@collapseInformation": { + "description": "Bilgiyi çökertme eylemini tanımlar - FAB ayarlarında kullanılır" + }, + "collapseParentCommentBodyOnGesture": "Çöktüğünde Üst Yorumu Gizle", + "@collapseParentCommentBodyOnGesture": { + "description": "Hareketle ana yorum metnini daraltmayı etkinleştir." + }, + "collapsePost": "Gönderiyi çökert", + "@collapsePost": {}, + "collapsePostPreview": "Gönderi Önizlemesini Daralt", + "@collapsePostPreview": { + "description": "Ayarlar -> Görünüm -> Gönderiler'deki çökme düğmesi için semantik etiket" + }, + "collapseSpoiler": "Spoiler'ı Çökert", + "@collapseSpoiler": { + "description": "Spoilerı daraltma etiketi" + }, + "color": "Renk", + "@color": { + "description": "Bir rengi tanımlar" + }, + "colorizeCommunityName": "Topluluk Adını Renklendir", + "@colorizeCommunityName": { + "description": "Topluluk adını renklendirme ayarı" + }, + "colorizeInstanceName": "Örnek İsmi Renklendir", + "@colorizeInstanceName": { + "description": "Örnek adını renklendirme ayarı" + }, + "colorizeUserName": "Kullanıcı Adını Renklendir", + "@colorizeUserName": { + "description": "Kullanıcı adını renklendirme ayarı" + }, + "colors": "Renkler", + "@colors": { + "description": "Renkler için başlık" + }, + "combineCommentScores": "Yorum Puanlarını Birleştir", + "@combineCommentScores": { + "description": "Yorum puanlarını birleştirmek için geçiş yapın." + }, + "combineCommentScoresLabel": "Yorum Puanlarını Birleştir", + "@combineCommentScoresLabel": { + "description": "Yorum puanlarını birleştirme ayarı için etiket" + }, + "combineNavAndFab": "FAB ve Navigasyon Düğmelerini Birleştirin", + "@combineNavAndFab": {}, + "combineNavAndFabDescription": "Yüzen İşlem Düğmesi, navigasyon düğmeleri arasında gösterilecektir.", + "@combineNavAndFabDescription": {}, + "comfortable": "Rahat", + "@comfortable": { + "description": "Rahat bir görsel yoğunluğu tanımlar" + }, + "comment": "Yorum", + "@comment": {}, + "commentBehaviourSettings": "Yorumlar", + "@commentBehaviourSettings": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "commentFontScale": "Yorum İçeriği Yazı Tipi Ölçeği", + "@commentFontScale": { + "description": "Yorum yazı tipi ölçeği ayarı" + }, + "commentPreview": "Verilen ayarlarla yorumların bir önizlemesini göster", + "@commentPreview": { + "description": "Ayarlar -> Görünüm -> Yorumlar bölümünde yorum önizlemesi için açıklama" + }, + "commentReported": "Yorum inceleme için işaretlendi.", + "@commentReported": {}, + "commentSavedAsDraft": "Taslak olarak yorum kaydedildi", + "@commentSavedAsDraft": {}, + "commentShowUserAvatar": "Kullanıcı Avatarını Göster", + "@commentShowUserAvatar": { + "description": "Ayarlar, kullanıcı avatarını yorumlarında görüntü adı yanında göstermek için geçiş düğmesi" + }, + "commentShowUserInstance": "Kullanıcı Örneğini Göster", + "@commentShowUserInstance": { + "description": "Yorumlarda kullanıcı örneğinin görüntü adının yanında görüntülenmesi için ayarlar düğmesi" + }, + "commentSortType": "Yorum Sıralama Türü", + "@commentSortType": {}, + "commentSwipeActions": "Yorum Kaydırma Eylemleri", + "@commentSwipeActions": { + "description": "Yorum kaydırma eylemleri için ayarlar" + }, + "commentSwipeGesturesHint": "Düğmeleri kullanmayı mı düşünüyorsunuz? Genel ayarlarda yorumlar bölümünü etkinleştirin.", + "@commentSwipeGesturesHint": {}, + "comments": "Yorumlar", + "@comments": {}, + "communities": "Topluluklar", + "@communities": {}, + "community": "Topluluk", + "@community": {}, + "communityActions": "Topluluk Eylemleri", + "@communityActions": { + "description": "Topluluk Eylemleri Sayfasına Gidiyor" + }, + "communityEntry": "'Topluluk '{community}'", + "@communityEntry": { + "description": "Bir topluluk için daha fazla eylem gerçekleştirmek üzere liste girişi" + }, + "communityFormat": "Topluluk Formatı", + "@communityFormat": { + "description": "Topluluk tam adı formatı ayarları" + }, + "communityNameColor": "Topluluk Adı Rengi", + "@communityNameColor": { + "description": "Topluluk adı rengi ayarı" + }, + "communityNameThickness": "Topluluk Adı Kalınlığı", + "@communityNameThickness": { + "description": "Topluluk adı kalınlığı ayarı" + }, + "communityStyle": "Topluluk Stili", + "@communityStyle": { + "description": "Topluluk tarzı ortama doğru ilerliyoruz" + }, + "compact": "Kompakt", + "@compact": { + "description": "Yoğun bir görsel yoğunluğu tanımlar." + }, + "compactPostCardMetadataItems": "Kompakt Görünüm Meta Verileri", + "@compactPostCardMetadataItems": { + "description": "Kompakt kartpostal metadata öğeleri için ayar adı" + }, + "compactView": "Kompakt Görünüm", + "@compactView": { + "description": "Ayarlar -> Görünüm -> Gönderiler için kompakt görünüm seçeneği etiketi" + }, + "compactViewDescription": "Ayarları ayarlamak için kompakt görünümü etkinleştirin", + "@compactViewDescription": { + "description": "Ayarlar -> Görünüm -> Gönderiler alt kategorisindeki kompakt görünüm açıklaması" + }, + "compactViewSettings": "Kompakt Görünüm Ayarları", + "@compactViewSettings": { + "description": "Ayarlar -> Görünüm -> Gönderiler alt kategorisi" + }, + "condensed": "Yoğunlaştırılmış", + "@condensed": { + "description": "Yoğunlaştırılmış gönderi gövdesi görünüm tipi" + }, + "confirm": "Onayla", + "@confirm": { + "description": "İletişim kutusundaki onaylama işlemi için etiket" + }, + "confirmLogOutBody": "Çıkış yapmak istediğinize emin misiniz?", + "@confirmLogOutBody": { + "description": "Çıkışı onayla iletişim kutusunun gövdesi" + }, + "confirmLogOutTitle": "Çıkış Yap?", + "@confirmLogOutTitle": { + "description": "Çıkışı Onayla iletişim kutusunun başlığı" + }, + "confirmMarkAllAsReadBody": "Tüm mesajları okundu olarak işaretlemek istediğinize emin misiniz?", + "@confirmMarkAllAsReadBody": { + "description": "Tümünü okundu olarak işaretle iletişim kutusunun gövdesi" + }, + "confirmMarkAllAsReadTitle": "Tümünü Okundu Olarak İşaretle?", + "@confirmMarkAllAsReadTitle": { + "description": "Tümünü okundu olarak işaretle onay kutusu başlığı" + }, + "confirmResetCommentPreferences": "Bu, tüm yorum tercihlerini sıfırlayacak. Devam etmek istediğinizden emin misiniz?", + "@confirmResetCommentPreferences": { + "description": "Ayarlar -> Görünüm -> Yorumlar bölümünde tercihleri sıfırlama iletişim kutusunun açıklaması" + }, + "confirmResetPostPreferences": "Bu, tüm gönderi tercihlerini sıfırlayacak. Devam etmek istediğinizden emin misiniz?", + "@confirmResetPostPreferences": { + "description": "Ayarlar -> Görünüm -> Gönderiler bölümünde tercihleri sıfırlama iletişim kutusunun açıklaması" + }, + "confirmUnsubscription": "Abonelikten çıkmak istediğinize emin misiniz?", + "@confirmUnsubscription": { + "description": "Kullanıcının abonelikten çıkmak istediğini onaylamasını isteyin." + }, + "connectedToUnifiedPushDistributorApp": "{app} ile bağlandı.", + "@connectedToUnifiedPushDistributorApp": { + "description": "Kullanıcıya hangi UnifiedPush uygulamasına bağlı olduğumuzu söyleyin." + }, + "contentManagement": "İçerik Yönetimi", + "@contentManagement": { + "description": "İçerik yönetimi ayarları (diller/engelleme)" + }, + "controversial": "Tartışmalı", + "@controversial": {}, + "copiedToClipboard": "Panoya kopyalandı", + "@copiedToClipboard": {}, + "copy": "Kopyala", + "@copy": {}, + "copyComment": "Yorumu Kopyala", + "@copyComment": { + "description": "Bir yorumun tamamını kopyalama eylemi" + }, + "copySelected": "Seçileni kopyala", + "@copySelected": { + "description": "Seçili metni kopyalama işlemi" + }, + "copyText": "Metin Kopyala", + "@copyText": {}, + "couldNotDetermineCommentDelete": "Hata: Yorumu silmek için gönderi belirlenemedi.", + "@couldNotDetermineCommentDelete": {}, + "couldNotDeterminePostComment": "Hata: Yorum yapılacak gönderi belirlenemedi.", + "@couldNotDeterminePostComment": {}, + "couldntCreateReport": "Yorum raporunuz şu anda gönderilemedi. Lütfen daha sonra tekrar deneyin.", + "@couldntCreateReport": {}, + "couldntFindPost": "İstenilen gönderi yüklenemiyor. Silinmiş veya kaldırılmış olabilir.", + "@couldntFindPost": { + "description": "Bir gönderi yüklenemediğinde görünen hata mesajı." + }, + "countComments": "{count} Yorum", + "@countComments": { + "description": "Yorum sayısı" + }, + "countLocalSubscribers": "{count} Yerel Aboneler", + "@countLocalSubscribers": { + "description": "Yerel abone sayısı" + }, + "countPosts": "{count} Gönderi", + "@countPosts": { + "description": "Gönderi sayısı" + }, + "countSubscribers": "{count} Abone", + "@countSubscribers": { + "description": "Abone sayısı" + }, + "countUsers": "{count} kullanıcı", + "@countUsers": { + "description": "Belirli bir sayıda kullanıcıyı tanımlar" + }, + "countUsersActiveDay": "{count} kullanıcı/gün", + "@countUsersActiveDay": { + "description": "Son bir günde aktif olan kullanıcıların sayısı" + }, + "countUsersActiveHalfYear": "{count} kullanıcı/6 ay", + "@countUsersActiveHalfYear": { + "description": "Son altı ayda aktif olan kullanıcıların sayısı" + }, + "countUsersActiveMonth": "{count} kullanıcı/ay", + "@countUsersActiveMonth": { + "description": "Son ayda aktif olan kullanıcıların sayısı" + }, + "countUsersActiveWeek": "{count} kullanıcı/hafta", + "@countUsersActiveWeek": { + "description": "Son haftada aktif olan kullanıcıların sayısı" + }, + "createAccount": "Hesap Oluştur", + "@createAccount": {}, + "createComment": "Yorum Oluştur", + "@createComment": {}, + "createNewCrossPost": "Yeni çapraz gönderi oluştur", + "@createNewCrossPost": {}, + "createPost": "Gönderi Oluştur", + "@createPost": {}, + "created": "Oluşturulma tarihi {date}", + "@created": { + "description": "Bir şeyin oluşturulduğu tarih" + }, + "createdToday": "Bugün Oluşturuldu", + "@createdToday": { + "description": "Bugün doğum günü olan bir hesap için hesap tanımlayıcısı" + }, + "creator": "Yaratıcı", + "@creator": { + "description": "Aramalar için yaratıcı filtre." + }, + "crossPostedFrom": "{postUrl} adresinden paylaşıldı.", + "@crossPostedFrom": { + "description": "Metin tabanlı çapraz gönderi için başlangıç başlığı" + }, + "crossPostedTo": "Çapraz yayınlandı", + "@crossPostedTo": {}, + "currentLongPress": "Uzun basma olarak ayarlandı", + "@currentLongPress": {}, + "currentNotificationsMode": "Mevcut bildirim modu: {mode}", + "@currentNotificationsMode": { + "description": "Mevcut bildirim modu için durum göstergesi" + }, + "currentSinglePress": "Tek basılı olarak ayarlandı", + "@currentSinglePress": {}, + "customizeSwipeActions": "Kaydırma eylemlerini özelleştir (değiştirmek için dokunun)", + "@customizeSwipeActions": {}, + "dangerZone": "Tehlike Bölgesi", + "@dangerZone": { + "description": "Eylemlerin tehlikeli olabileceği bir bölümü tanımlar (örneğin, hesabı kalıcı olarak silme)" + }, + "dark": "Karanlık", + "@dark": { + "description": "Karanlık temayı kullanmayı açıklar" + }, + "databaseExportWarning": "Veritabanı, Lemmy hesabınızla ilgili hassas bilgiler içerebilir. Eğer onu dışa aktarırsanız, kimseyle paylaşmamalısınız. Devam etmek istiyor musunuz?", + "@databaseExportWarning": { + "description": "Kullanıcıya db dışa aktarılırken gösterilen mesaj" + }, + "databaseExportedSuccessfully": "Veritabanı başarıyla '{savedFilePath}' konumuna aktarıldı.", + "@databaseExportedSuccessfully": { + "description": "Kullanıcıya veritabanı başarıyla dışa aktarıldığında gösterilen mesaj" + }, + "databaseImportedSuccessfully": "Veritabanı başarıyla içe aktarıldı!", + "@databaseImportedSuccessfully": { + "description": "Kullanıcıya veritabanı başarıyla içe aktarıldığında gösterilen mesaj" + }, + "databaseNotExportedSuccessfully": "Veritabanı başarıyla dışa aktarılamadı veya işlem iptal edildi.", + "@databaseNotExportedSuccessfully": { + "description": "Kullanıcıya veritabanı başarısız bir şekilde dışa aktarıldığında gösterilen mesaj" + }, + "databaseNotImportedSuccessfully": "Veritabanı başarıyla içe aktarılmadı veya işlem iptal edildi.", + "@databaseNotImportedSuccessfully": { + "description": "Kullanıcıya veritabanı başarısız bir şekilde içe aktarıldığında gösterilen mesaj" + }, + "dateFormat": "Tarih Formatı", + "@dateFormat": { + "description": "Tarih formatı ayarları" + }, + "debug": "Hata ayıklama", + "@debug": { + "description": "Hata ayıklama ayarları kategorisi." + }, + "debugDescription": "Aşağıdaki hata ayıklama ayarları sadece sorun giderme amaçları için kullanılmalıdır.", + "@debugDescription": { + "description": "Hata ayıklama ayarları sayfası için açıklama" + }, + "debugNotificationsDescription": "Bildirimlerle ilgili sorunları gidermek için aşağıdaki seçenekleri kullanın.", + "@debugNotificationsDescription": { + "description": "Bildirim hata ayıklama bölümü için bir açıklama" + }, + "defaultColor": "Varsayılan", + "@defaultColor": { + "description": "Varsayılan ayar değeri (örneğin, renk)" + }, + "defaultCommentSortType": "Varsayılan Yorum Sıralama Türü", + "@defaultCommentSortType": { + "description": "Varsayılan yorum sıralama türü için ayarlar" + }, + "defaultFeedSortType": "Varsayılan Besleme Sıralama Türü", + "@defaultFeedSortType": { + "description": "Besleme için varsayılan sıralama türü." + }, + "defaultFeedType": "Varsayılan Besleme Türü", + "@defaultFeedType": { + "description": "Besleme için varsayılan listeleme türü." + }, + "delete": "Sil", + "@delete": {}, + "deleteAccount": "Hesabı Sil", + "@deleteAccount": { + "description": "Hesabı silme işlemi" + }, + "deleteAccountDescription": "Hesabınızı kalıcı olarak silmek için, örnek siteye yönlendirileceksiniz. \n\nDevam etmek istediğinizden emin misiniz?", + "@deleteAccountDescription": { + "description": "Hesabı silme işlemini onaylama açıklaması" + }, + "deleteLocalDatabase": "Yerel Veritabanını Sil", + "@deleteLocalDatabase": { + "description": "Yerel veritabanını silme işlemi için etiket" + }, + "deleteLocalDatabaseDescription": "Bu eylem, yerel veritabanını kaldıracak ve sizi tüm hesaplarınızdan çıkaracaktır.\n\nDevam etmek istediğinizden emin misiniz?", + "@deleteLocalDatabaseDescription": { + "description": "Yerel veritabanını silme işlemini onaylama açıklaması" + }, + "deleteLocalPreferences": "Yerel Tercihleri Sil", + "@deleteLocalPreferences": { + "description": "Yerel tercihleri silme işlemi için etiket" + }, + "deleteLocalPreferencesDescription": "Bu, Thunder'daki tüm kullanıcı tercihlerinizi ve ayarlarınızı silecektir.\n\nDevam etmek ister misiniz?", + "@deleteLocalPreferencesDescription": { + "description": "Yerel tercihleri silme işlemini onaylama açıklaması" + }, + "deletedByCreator": "yaratıcı tarafından silindi", + "@deletedByCreator": { + "description": "Yaratıcı tarafından silinen bir yorum için yer tutucu metin. Bunu küçük harfli tutmaya emin ol." + }, + "deletedByModerator": "moderatör tarafından silindi", + "@deletedByModerator": { + "description": "Bir moderatör tarafından silinen yorum için yer tutucu metin. Bunu küçük harfli tutmaya emin ol." + }, + "deselectUndeterminedWarning": "Belirsiz'i seçimi kaldırırsanız, çoğu içeriği göremezsiniz.", + "@deselectUndeterminedWarning": {}, + "detailedReason": "Sebep: {reason}", + "@detailedReason": { + "description": "Moderasyon eyleminin sebebi" + }, + "dimReadPosts": "Gönderileri Oku", + "@dimReadPosts": { + "description": "Okunan gönderiler üzerindeki etkinin açıklaması." + }, + "disable": "Devre dışı bırak", + "@disable": { + "description": "Bir şeyi devre dışı bırakma eylemi" + }, + "disablePushNotifications": "Bildirimleri Kapat", + "@disablePushNotifications": { + "description": "Bildirimleri devre dışı bırakma açıklaması" + }, + "disabled": "Engelli", + "@disabled": { + "description": "Bir şeyin devre dışı bırakıldığını tarif eder." + }, + "discussionLanguages": "Tartışma Dilleri", + "@discussionLanguages": { + "description": "Yalnızca kendi dilinizdeki gönderileri ve toplulukları yükleyin." + }, + "discussionLanguagesTooltip": "İçerik, seçilen dillere göre filtrelenmiştir.", + "@discussionLanguagesTooltip": {}, + "dismissRead": "Okuma İptal Et", + "@dismissRead": {}, + "displayName": "Görünen İsim", + "@displayName": { + "description": "Görüntülenecek isim etiketi" + }, + "displayUserScore": "Kullanıcı Skorlarını Göster (Karma).", + "@displayUserScore": { + "description": "Kullanıcı puanlarını veya karmayı görüntüleme seçeneği." + }, + "dividerAppearance": "Bölücü Görünümü", + "@dividerAppearance": { + "description": "Bölücü görünüm kategorisi" + }, + "doNotShowAgain": "Bir Daha Gösterme", + "@doNotShowAgain": { + "description": "Bir şeyi kalıcı olarak saklama eylemi" + }, + "doNotSupportMultipleUnifiedPushApps": "Birden fazla uyumlu uygulama bulundu; lütfen sadece birini yükleyin.", + "@doNotSupportMultipleUnifiedPushApps": { + "description": "Kullanıcıya şu anda birden fazla UnifiedPush uygulamasını desteklemediğimizi söyleyin." + }, + "downloadingMedia": "Medya indiriliyor ve paylaşılıyor...", + "@downloadingMedia": {}, + "downvote": "Aşağı oyla", + "@downvote": { + "description": "Bir gönderi/yorumu beğenmeme eylemi" + }, + "downvoteColor": "Aşağı Oy Rengi", + "@downvoteColor": { + "description": "Aşağı oy renk ayarı adı" + }, + "downvoted": "Oy aşağı", + "@downvoted": { + "description": "Bir gönderi/yorumu beğenmeme kısa açıklaması" + }, + "downvotesDisabled": "Bu durumda eksilenme özelliği kapalıdır.", + "@downvotesDisabled": {}, + "edit": "Düzenle", + "@edit": {}, + "editComment": "Yorumu Düzenle", + "@editComment": {}, + "editPost": "Gönderiyi Düzenle", + "@editPost": { + "description": "Bir gönderiyi düzenlerken başlık." + }, + "email": "E-posta", + "@email": { + "description": "E-posta girişi için etiket" + }, + "empty": "Boş", + "@empty": {}, + "emptyInbox": "Boş Gelen Kutusu", + "@emptyInbox": {}, + "emptyUri": "Bağlantı boş. Lütfen devam etmek için geçerli bir dinamik bağlantı sağlayın.", + "@emptyUri": { + "description": "Boş bağlantı için hata mesajı." + }, + "enableCommentNavigation": "Yorum Navigasyonunu Etkinleştir", + "@enableCommentNavigation": { + "description": "Yorum navigasyonunu etkinleştirme ayarı" + }, + "enableExperimentalFeatures": "Deneysel özellikleri etkinleştir", + "@enableExperimentalFeatures": { + "description": "Deneysel özellikleri etkinleştirmek için ayarlar" + }, + "enableFeedFab": "Beslemelerde Yüzen Düğmeyi Etkinleştir", + "@enableFeedFab": { + "description": "Besleme için Yüzen İşlem Düğmesini etkinleştirin" + }, + "enableFloatingButtonOnFeeds": "Beslemelerde Yüzen Düğmeyi Etkinleştir", + "@enableFloatingButtonOnFeeds": { + "description": "Beslemelerde yüzen düğmeyi etkinleştirme ayarı" + }, + "enableFloatingButtonOnPosts": "Gönderilerde Yüzen Düğmeyi Etkinleştir", + "@enableFloatingButtonOnPosts": { + "description": "Gönderilerde gezinme düğmesini etkinleştirme ayarı" + }, + "enableInboxNotifications": "Gelen Kutusu Bildirimlerini Etkinleştir", + "@enableInboxNotifications": { + "description": "Gelen kutusu bildirimleri için ayar adı" + }, + "enablePostFab": "Gönderilerde Yüzen Düğmeyi Etkinleştir", + "@enablePostFab": { + "description": "Gönderi için Yüzen İşlem Düğmesini etkinleştirin" + }, + "endSearch": "Aramayı Sonlandır", + "@endSearch": {}, + "errorDownloadingMedia": "Medya dosyasını paylaşmak için indirilemedi: {errorMessage}", + "@errorDownloadingMedia": {}, + "errorMarkingReplyRead": "Yanıtı okundu olarak işaretleme hatası oluştu.", + "@errorMarkingReplyRead": { + "description": "Bir yanıtı okundu olarak işaretleme hatası mesajı" + }, + "errorMarkingReplyUnread": "Yanıtı okunmadı olarak işaretleme hatası oluştu.", + "@errorMarkingReplyUnread": { + "description": "Bir yanıtı okunmamış olarak işaretleme hatası mesajı" + }, + "exceptionProcessingUri": "Bağlantı işlenirken bir hata oluştu. Bu, sizin örneğinizde mevcut olmayabilir.", + "@exceptionProcessingUri": { + "description": "Bağlantı işlemi sırasında belirtilmemiş bir hata." + }, + "expand": "Genişle", + "@expand": { + "description": "Bir şeyi genişletme eylemi" + }, + "expandCommentPreview": "Yorum Önizlemesini Genişlet", + "@expandCommentPreview": { + "description": "Ayarlar -> Görünüm -> Yorumlar bölümünde genişletme düğmesi için semantik etiket" + }, + "expandInformation": "Bilgiyi Genişlet", + "@expandInformation": { + "description": "Bilgiyi genişletme eylemini tanımlar - FAB ayarlarında kullanılır" + }, + "expandOptions": "Seçenekleri genişlet", + "@expandOptions": {}, + "expandPost": "Gönderiyi genişlet", + "@expandPost": {}, + "expandPostPreview": "Gönderi Önizlemesini Genişlet", + "@expandPostPreview": { + "description": "Ayarlar -> Görünüm -> Gönderiler'deki genişletme düğmesi için semantik etiket" + }, + "expandSpoiler": "Spoiler'ı Genişlet", + "@expandSpoiler": { + "description": "Spoilerı genişletme etiketi" + }, + "expanded": "Genişletilmiş", + "@expanded": { + "description": "Genişletilmiş gönderi gövdesi görünüm tipi" + }, + "experimentalFeatures": "Deneysel Özellikler", + "@experimentalFeatures": { + "description": "Deneysel özellikler için ayar adı" + }, + "experimentalFeaturesDescription": "Bu özellikler hala geliştirme aşamasındadır ve kararsız olabilir. Onları kendi riskinizde kullanın. Etkili olması için Thunder'ı yeniden başlatmalısınız.", + "@experimentalFeaturesDescription": { + "description": "Deneysel özellikler ayarı için açıklama" + }, + "exploreInstance": "Örneklemi keşfet", + "@exploreInstance": { + "description": "Bir örneği keşfetme düğmesi" + }, + "exportDatabase": "Veritabanını Dışa Aktar", + "@exportDatabase": { + "description": "Veritabanını dışa aktarma ayarı adı" + }, + "exportDatabaseSubtitle": "Veritabanı, hesaplar, favoriler, anonim abonelikler ve kullanıcı etiketleri hakkında bilgi içerir.", + "@exportDatabaseSubtitle": { + "description": "Veritabanını dışa aktarma ayarları için alt başlık" + }, + "exportSettingsSubtitle": "Ayarlar, Thunder'da yapılandırdığınız tüm tercihleri içerir.", + "@exportSettingsSubtitle": { + "description": "Ayarları dışa aktarma için ayar alt başlığı" + }, + "extraLarge": "Ekstra Büyük", + "@extraLarge": { + "description": "Ekstra büyük yazı tipi ölçeği için açıklama" + }, + "failedToBlock": "Engellenemedi: {errorMessage}", + "@failedToBlock": {}, + "failedToCommunicateWithThunderNotificationServer": "'{serverAddress}' adresindeki Thunder bildirim sunucusuyla iletişim kurulamadı.", + "@failedToCommunicateWithThunderNotificationServer": { + "description": "Thunder bildirim sunucusu ile iletişim kurma hatası mesajı" + }, + "failedToLoadBlocks": "Blok yüklenemedi: {errorMessage}", + "@failedToLoadBlocks": {}, + "failedToLoadVideo": "Video yüklenemedi. Tarayıcıda bağlantıyı aç?", + "@failedToLoadVideo": { + "description": "Bir videoyu yüklemeyi başaramadığımızda görünen hata mesajı" + }, + "failedToUnblock": "Açılamadı: {errorMessage}", + "@failedToUnblock": {}, + "failedToUpdateNotificationSettings": "Bildirim ayarları güncellenemedi", + "@failedToUpdateNotificationSettings": { + "description": "Bildirim ayarları güncellenemediğinde hata mesajı." + }, + "favorites": "Favoriler", + "@favorites": { + "description": "Çekmecedeki favori topluluklar" + }, + "featuredPost": "Öne Çıkan Gönderi", + "@featuredPost": { + "description": "Moderatör eylemi için kısa bir açıklama: Bir gönderiyi öne çıkarmak" + }, + "feed": "Besle", + "@feed": {}, + "feedBehaviourSettings": "Besle", + "@feedBehaviourSettings": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "feedSettings": "Besleme Ayarları", + "@feedSettings": { + "description": "Besleme ayarları kategorisi." + }, + "feedTypeAndSorts": "Varsayılan Besleme Türü ve Sıralama", + "@feedTypeAndSorts": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "fetchAccountError": "Hesap belirlenemedi", + "@fetchAccountError": {}, + "filteringBy": "{entity} tarafından filtreleme", + "@filteringBy": { + "description": "Bir topluluk veya yaratıcıya göre arama filtreleme." + }, + "filters": "Filtreler", + "@filters": { + "description": "Çeşitli filtreleri (kullanıcı, topluluk, örnek engelleme) tanımlamak için kategori" + }, + "floatingActionButton": "Yüzen İşlem Düğmesi", + "@floatingActionButton": { + "description": "Yüzen İşlem Düğmesi ayarları kategorisi." + }, + "floatingActionButtonInformation": "Gök gürültüsü, birkaç jesti destekleyen tamamen özelleştirilebilir bir FAB deneyimine sahiptir.\n- Ek FAB eylemlerini göstermek için yukarı kaydırın\n- FAB'ı gizlemek veya göstermek için aşağı/yukarı kaydırın\n\nFAB için ana ve ikincil eylemleri özelleştirmek için, aşağıdaki eylemlerden birine uzun basın.", + "@floatingActionButtonInformation": { + "description": "Ayarlar sayfasındaki FAB'ın açıklaması" + }, + "floatingActionButtonLongPressDescription": "FAB'ın uzun basma eylemini belirtir.", + "@floatingActionButtonLongPressDescription": { + "description": "FAB'ın uzun basma işleminin açıklaması" + }, + "floatingActionButtonSinglePressDescription": "FAB'ın tek basmalı eylemini belirtir.", + "@floatingActionButtonSinglePressDescription": { + "description": "FAB'ın tek basmalı eyleminin açıklaması" + }, + "fonts": "Yazı tipleri", + "@fonts": { + "description": "Yazı tipleri için ayarlar kategorisi" + }, + "forward": "İleri", + "@forward": { + "description": "İleriye doğru gezinme etiketi (örneğin, bir tarayıcıda)" + }, + "foundUnifiedPushDistribtorApp": "Uygun uygulama bulundu; Thunder'ı bağlanmak için yeniden başlatın.", + "@foundUnifiedPushDistribtorApp": { + "description": "Kullanıcıya uyumlu bir UnifiedPush uygulaması bulduğumuzu ancak bağlanmak için yeniden başlatmaları gerektiğini söyleyin." + }, + "fullScreenNavigationSwipeDescription": "Sol-a sağ hareketler devre dışıyken geri gitmek için herhangi bir yere sürükleyin.", + "@fullScreenNavigationSwipeDescription": {}, + "fullscreenSwipeGestures": "Tam Ekran Kaydırma Hareketleri", + "@fullscreenSwipeGestures": { + "description": "Tam ekran kaydırma hareketleri ayarları" + }, + "general": "Genel", + "@general": { + "description": "Genel ayarlar kategorisi." + }, + "generalSettings": "Genel Ayarlar", + "@generalSettings": { + "description": "Ayarlar'daki Alt Kategori" + }, + "gestures": "Jestler", + "@gestures": {}, + "gettingStarted": "Başlamak", + "@gettingStarted": {}, + "green": "Yeşil", + "@green": { + "description": "Yeşil renk" + }, + "guestModeFeedSettings": "Konuk Modu Besleme Ayarları", + "@guestModeFeedSettings": { + "description": "Misafir hesapları için besleme ayarları" + }, + "guestModeFeedSettingsLabel": "Aşağıdaki ayarlar yalnızca misafir hesaplarına uygulanır. Hesabınız için besleme ayarlarını düzenlemek için Hesap Ayarları'na gidin.", + "@guestModeFeedSettingsLabel": { + "description": "Konuk modu besleme ayarları için açıklama" + }, + "havingIssuesWithNotifications": "Bildirimlerle ilgili sorunlar mı yaşıyorsunuz?", + "@havingIssuesWithNotifications": { + "description": "Bildirim hata ayıklama bölümüne gitmek için bir kısayol" + }, + "hidCommunity": "Hid Topluluğu", + "@hidCommunity": { + "description": "Bir topluluğu gizlemek için moderatör eyleminin kısa açıklaması" + }, + "hideNsfwPostsFromFeed": "Beslemeden NSFW Gönderileri Gizle", + "@hideNsfwPostsFromFeed": { + "description": "Beslemeden NSFW gönderileri gizlemek için geçiş yapın." + }, + "hideNsfwPreviews": "NSFW Önizlemeleri Bulanıklaştır", + "@hideNsfwPreviews": { + "description": "NSFW önizlemelerini bulanıklaştırmak için geçiş yapın." + }, + "hidePassword": "Şifreyi Gizle", + "@hidePassword": {}, + "hideThumbnails": "Küçük Resimleri Gizle", + "@hideThumbnails": { + "description": "Beslemedeki küçük resimleri gizleme seçeneği" + }, + "hideTopBarOnScroll": "Kaydırma Sırasında Üst Çubuğu Gizle", + "@hideTopBarOnScroll": { + "description": "Kaydırma sırasında üst çubuğu gizlemek için ayarlar düğmesi" + }, + "hostInstance": "Ana Bilgisayar Örneği", + "@hostInstance": { + "description": "Topluluğa ev sahipliği yapan örnek" + }, + "hot": "Sıcak", + "@hot": {}, + "image": "Görüntü", + "@image": {}, + "imageCachingMode": "Görüntü Önbellekleme Modu", + "@imageCachingMode": { + "description": "Görüntü Önbellekleme Modu ile İlgili Ayarlar Başlığı" + }, + "imageCachingModeAggressive": "Resimleri agresif bir şekilde önbelleğe al (daha fazla bellek kullanır)", + "@imageCachingModeAggressive": { + "description": "Agresif görüntü önbellekleme modu için uzun açıklama" + }, + "imageCachingModeAggressiveShort": "Saldırgan", + "@imageCachingModeAggressiveShort": { + "description": "Agresif resim önbellekleme modu için kısa açıklama" + }, + "imageCachingModeRelaxed": "Görüntü önbelleklerinin süresi dolmasına izin ver (daha az bellek kullanır ancak görüntülerin daha sık yeniden yüklenmesine neden olur)", + "@imageCachingModeRelaxed": { + "description": "Rahatlatılmış görüntü önbellekleme modu için uzun açıklama" + }, + "imageCachingModeRelaxedShort": "Rahat", + "@imageCachingModeRelaxedShort": { + "description": "Rahat görüntü önbellekleme modu için kısa açıklama" + }, + "imageDimensionTimeout": "Görüntü Boyutu Zaman Aşımı", + "@imageDimensionTimeout": { + "description": "Görüntü boyutlarının alınması için ne kadar süre beklenmesi gerektiğine dair ayarlar" + }, + "importDatabase": "Veritabanını İçe Aktar", + "@importDatabase": { + "description": "Veritabanını içe aktarma ayarı adı" + }, + "importExportDatabase": "Veritabanı İçe Aktar/Dışa Aktar", + "@importExportDatabase": { + "description": "Veritabanını içe aktarma veya dışa aktarma ayarlarının genel adı (arama için)" + }, + "importExportSettings": "İçe/Dışa Ayarları Aktarma", + "@importExportSettings": { + "description": "İthalat ve ihracatla ilgili ayarlar kategorisi." + }, + "importSettings": "Ayarları İçe Aktar", + "@importSettings": { + "description": "Uygulama ayarlarını içe aktarma eylemi." + }, + "inReplyTo": "{post} başlıklı {community} topluluğundaki mesaja yanıt olarak", + "@inReplyTo": {}, + "in_": "içinde", + "@in_": { + "description": "Belirli bir yorumun/mesajın belirli bir topluluktan geldiğini belirtmek için kullanılan bağlantı kelimesi" + }, + "inbox": "Gelen Kutusu", + "@inbox": {}, + "includeCommunity": "Topluluğu Dahil Et", + "@includeCommunity": {}, + "includeExternalLink": "Harici Bağlantıyı Dahil Et", + "@includeExternalLink": {}, + "includeImage": "Resim Ekleyin", + "@includeImage": {}, + "includePostLink": "Gönderi Bağlantısını Dahil Et", + "@includePostLink": {}, + "includeText": "Metni Dahil Et", + "@includeText": {}, + "includeTitle": "Başlık Dahil Et", + "@includeTitle": {}, + "information": "Bilgi", + "@information": { + "description": "Bilgi başlığı - FAB ayarlar sayfasında kullanılır" + }, + "instance": "{count, plural, zero {Instance} one {Instance} other {Instances} } ", + "@instance": { + "description": "Bir örneğe doğru ilerliyoruz" + }, + "instanceActions": "Örnek Eylemler", + "@instanceActions": { + "description": "Örneğin eylemler sayfasına gidiyoruz" + }, + "instanceEntry": "'{username}' örneği", + "@instanceEntry": { + "description": "Bir örneğin daha fazla eylem gerçekleştirmesi için listeye giriş" + }, + "instanceHasAlreadyBenAdded": "{instance} zaten eklendi.", + "@instanceHasAlreadyBenAdded": {}, + "instanceNameColor": "Örnek İsim Renk", + "@instanceNameColor": { + "description": "Örneğin ad rengi ayarı" + }, + "instanceNameThickness": "Örnek İsim Kalınlık", + "@instanceNameThickness": { + "description": "Örneğin adı kalınlık ayarı" + }, + "instances": "Örnekler", + "@instances": { + "description": "Örneklerle İlgili Ayarlar Başlığı" + }, + "internetOrInstanceIssues": "İnternete bağlı olmayabilirsiniz veya örneğiniz şu anda kullanılamıyor olabilir.", + "@internetOrInstanceIssues": {}, + "keywordFilterDescription": "Başlık, gövde veya URL'deki herhangi bir anahtar kelimeyi içeren gönderileri filtreler.", + "@keywordFilterDescription": { + "description": "Anahtar kelime filtre ayarlarının açıklaması" + }, + "keywordFilters": "Anahtar Kelime Filtreleri", + "@keywordFilters": { + "description": "Ayarlar -> Filtreler alt kategorisi" + }, + "label": "Etiket", + "@label": { + "description": "Kullanıcı etiketi için metin alanı etiketi" + }, + "language": "Dil", + "@language": { + "description": "Bir gönderi oluştururken veya düzenlerken etiket." + }, + "languageFilters": "Dil filtreleri mi arıyorsunuz?", + "@languageFilters": {}, + "languageNotAllowed": "Seçtiğiniz dilde gönderi yapmaya izin verilmeyen bir topluluğa gönderi yapıyorsunuz. Başka bir dil dene.", + "@languageNotAllowed": { + "description": "Dil_izin_verilmedi Lemmy istisnası için hata mesajı" + }, + "large": "Büyük", + "@large": { + "description": "Büyük yazı tipi ölçeği için açıklama" + }, + "leftLongSwipe": "Uzun Sol Kaydırma", + "@leftLongSwipe": { + "description": "Sol uzun kaydırma için ayarlar" + }, + "leftShortSwipe": "Sol Kısa Sürükleyiş", + "@leftShortSwipe": { + "description": "Sol kısa kaydırma için ayarlar" + }, + "light": "Işık", + "@light": { + "description": "Aydınlık temayı kullanmayı tarif eder" + }, + "link": "{count, plural, zero {Bağlantı} one {Bağlantı} other {Bağlantılar} } ", + "@link": {}, + "linkActions": "Bağlantı Eylemleri", + "@linkActions": {}, + "linkHandlingCustomTabs": "Uygulama içine gömülü sistem tarayıcısında aç", + "@linkHandlingCustomTabs": { + "description": "Özel sekme bağlantıları işleme açıklaması" + }, + "linkHandlingCustomTabsShort": "Uygulama içi gömülü", + "@linkHandlingCustomTabsShort": { + "description": "Özel sekme bağlantıları işleme için kısa açıklama" + }, + "linkHandlingExternal": "Sistemin dış tarayıcısında açın", + "@linkHandlingExternal": { + "description": "Dış bağlantı işleme için açıklama" + }, + "linkHandlingExternalShort": "Dış\n", + "@linkHandlingExternalShort": { + "description": "Dış bağlantı işleme için kısa açıklama" + }, + "linkHandlingInApp": "Thunder'ın yerleşik tarayıcısını kullanın", + "@linkHandlingInApp": { + "description": "Uygulama içi bağlantı işleme açıklaması" + }, + "linkHandlingInAppShort": "Uygulama içi", + "@linkHandlingInAppShort": { + "description": "Uygulama içi bağlantı işleme için kısa açıklama" + }, + "linksBehaviourSettings": "Bağlantılar", + "@linksBehaviourSettings": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "loadMorePlural": "{count} tane daha yanıt yükleyin...", + "@loadMorePlural": {}, + "loadMoreSingular": "{count} tane daha yanıt yükle…", + "@loadMoreSingular": {}, + "local": "Yerel", + "@local": {}, + "localNotifications": "Yerel Bildirimler", + "@localNotifications": { + "description": "Yerel Bildirimler için bildirim türünü tanımlar" + }, + "localOnly": "Yerel Sadece", + "@localOnly": { + "description": "Yerel topluluk görünürlüğü" + }, + "localPosts": "Yerel Gönderiler", + "@localPosts": {}, + "lockPost": "Gönderiyi Kilitle", + "@lockPost": { + "description": "Bir gönderiyi kilitleme eylemi (moderatör eylemi)" + }, + "lockedPost": "Kilitli Gönderi", + "@lockedPost": { + "description": "Moderatör eylemi için kısa bir açıklama: Bir gönderiyi kilitleme" + }, + "logOut": "Çıkış yap", + "@logOut": {}, + "login": "Giriş yap", + "@login": {}, + "loginFailed": "Giriş yapılamadı. Lütfen tekrar deneyin:({errorMessage})", + "@loginFailed": {}, + "loginSucceeded": "Giriş yapıldı.", + "@loginSucceeded": {}, + "loginToPerformAction": "Bu görevi gerçekleştirebilmek için giriş yapmış olmanız gerekmektedir.", + "@loginToPerformAction": {}, + "loginToSeeInbox": "Gelen kutunuzu görmek için giriş yapın.", + "@loginToSeeInbox": {}, + "malformedUri": "Sağladığınız bağlantı desteklenmeyen bir formatta. Lütfen geçerli bir bağlantı olduğundan emin olun.", + "@malformedUri": { + "description": "Desteklenmeyen bağlantı formatı ile ilgili hata." + }, + "manageAccounts": "Hesapları Yönet", + "@manageAccounts": {}, + "markAllAsRead": "Tümünü Okundu Olarak İşaretle", + "@markAllAsRead": { + "description": "Tümünü okundu olarak işaretle eylemi" + }, + "markAsRead": "Okundu olarak işaretle", + "@markAsRead": { + "description": "Mesajı okundu olarak işaretleme eylemi için etiket" + }, + "markPostAsReadOnMediaView": "Medya Görüntülendikten Sonra İşaretle Okundu", + "@markPostAsReadOnMediaView": { + "description": "Medya görüntülendikten sonra gönderileri okundu olarak işaretlemek için geçiş yapın." + }, + "markPostAsReadOnScroll": "Kaydırma İşareti Okundu", + "@markPostAsReadOnScroll": { + "description": "Beslemeyi kaydırırken gönderileri okundu olarak işaretlemek için geçiş yapın." + }, + "markReadColor": "Okundu/Okunmadı Rengi", + "@markReadColor": { + "description": "Okuma/okunmama işareti renk ayarı adı" + }, + "matrixUser": "Matris Kullanıcısı", + "@matrixUser": { + "description": "Matrix kullanıcı kimliği etiketi" + }, + "me": "Ben", + "@me": { + "description": "Mevcut kullanıcı için etiket." + }, + "medium": "Orta", + "@medium": { + "description": "Orta yazı tipi ölçeği için açıklama" + }, + "mention": "{count, plural, zero {Bahsetme} one {Bahsetme} other {Bahsetmeler} }", + "@mention": {}, + "message": "{count, plural, zero {Mesaj} one {Mesaj} other {Mesajlar} }", + "@message": {}, + "metadataFontScale": "Metaveri Yazı Tipi Ölçeği", + "@metadataFontScale": { + "description": "Metaveri yazı tipi ölçeği ayarı" + }, + "missingErrorMessage": "Hata mesajı mevcut değil", + "@missingErrorMessage": {}, + "modAdd": "Örnek Moderatörleri Ekle/Kaldır", + "@modAdd": { + "description": "Eylemler için modlog filtresi açıklaması, örnekleme moderatörleri ekleyenler." + }, + "modAddCommunity": "Topluluklara Moderatör Ekle/Kaldır", + "@modAddCommunity": { + "description": "Topluluklara moderatör ekleyen eylemler için modlog filtresi açıklaması" + }, + "modBan": "Örnek Kullanıcıları Yasakla/Yasağı Kaldır", + "@modBan": { + "description": "Örnek kullanıcıları yasaklayan eylemler için modlog filtresi açıklaması" + }, + "modBanFromCommunity": "Topluluklardan Kullanıcıları Yasakla/Yasağı Kaldır", + "@modBanFromCommunity": { + "description": "Topluluklardan kullanıcıları yasaklayan eylemler için modlog filtresi açıklaması" + }, + "modFeaturePost": "Gönderileri Öne Çıkar/Kaldır", + "@modFeaturePost": { + "description": "Gönderileri içeren eylemler için modlog filtresi açıklaması" + }, + "modLockPost": "Gönderileri Kilitle/Aç", + "@modLockPost": { + "description": "Gönderileri kilitleyen eylemler için modlog filtresi açıklaması" + }, + "modRemoveComment": "Yorumları Kaldır/Geri Yükle", + "@modRemoveComment": { + "description": "Yorumları kaldıran eylemler için modlog filtresi açıklaması" + }, + "modRemoveCommunity": "Toplulukları Kaldır/Geri Yükle", + "@modRemoveCommunity": { + "description": "Toplulukları kaldıran eylemler için modlog filtresi açıklaması" + }, + "modRemovePost": "Gönderileri Kaldır/Geri Yükle", + "@modRemovePost": { + "description": "Gönderileri kaldıran eylemler için modlog filtresi açıklaması" + }, + "modTransferCommunity": "Toplulukları Aktarma", + "@modTransferCommunity": { + "description": "Moderatörlerin başka bir topluluğa transfer edilmesi eylemleri için modlog filtresi açıklaması" + }, + "moderatedCommunities": "Denetlenen Topluluklar", + "@moderatedCommunities": { + "description": "Mevcut kullanıcı tarafından denetlenen toplulukların bir listesini tanımlar." + }, + "moderator": "{count, plural, zero {Moderatör} one {Moderatör} other {Moderatörler}}", + "@moderator": { + "description": "Moderatör rol adı" + }, + "moderatorActions": "Moderatör Eylemleri", + "@moderatorActions": { + "description": "Moderatörlerin gönderilerde gerçekleştirmesi gereken eylemler" + }, + "modlog": "Mod kaydı", + "@modlog": { + "description": "Modlog görüntüleme seçeneği." + }, + "mostComments": "En Çok Yorumlar", + "@mostComments": {}, + "mustBeLoggedIn": "Giriş yapmanız gerekiyor", + "@mustBeLoggedIn": {}, + "mustBeLoggedInComment": "Yorum yapmak için giriş yapmanız gerekiyor.", + "@mustBeLoggedInComment": {}, + "mustBeLoggedInPost": "Bir gönderi oluşturmak için giriş yapmanız gerekiyor.", + "@mustBeLoggedInPost": {}, + "names": "İsimler", + "@names": { + "description": "İsimle ilgili ayarlar için başlık" + }, + "navbarDoubleTapGestures": "Navbar Çift Dokunma Hareketleri", + "@navbarDoubleTapGestures": { + "description": "Navbar çift dokunma hareketleri için ayarlar" + }, + "navbarSwipeGestures": "Navbar Kaydırma Hareketleri", + "@navbarSwipeGestures": { + "description": "Navbar kaydırma hareketleri için ayarlar" + }, + "navigateDown": "Sonraki yorum", + "@navigateDown": {}, + "navigateUp": "Önceki yorum", + "@navigateUp": {}, + "navigation": "Navigasyon", + "@navigation": {}, + "nestedCommentIndicatorColor": "İç İçe Yorum Göstergesi Rengi", + "@nestedCommentIndicatorColor": { + "description": "İç içe yorum göstergesi rengi için ayarlar" + }, + "nestedCommentIndicatorStyle": "İç İçe Yorum Gösterim Stili", + "@nestedCommentIndicatorStyle": { + "description": "İç içe yorum göstergesi stili için ayarlar" + }, + "never": "Asla", + "@never": {}, + "newComments": "Yeni Yorumlar", + "@newComments": {}, + "newPost": "Yeni Gönderi", + "@newPost": { + "description": "Yeni gönderi için ayarlar" + }, + "new_": "Yeni", + "@new_": {}, + "no": "Hayır", + "@no": { + "description": "Olumsuz durum" + }, + "noComments": "Oh. Yorum yok.", + "@noComments": {}, + "noCommentsFound": "Yorum bulunamadı.", + "@noCommentsFound": {}, + "noCommunitiesFound": "Hiçbir topluluk bulunamadı.", + "@noCommunitiesFound": {}, + "noCommunityBlocks": "Engellenmiş topluluklar yok.", + "@noCommunityBlocks": {}, + "noCompatibleAppFound": "Uygun uygulama bulunamadı", + "@noCompatibleAppFound": { + "description": "Kullanıcıya uyumlu bir UnifiedPush uygulaması bulamadığımızı bildirin." + }, + "noDiscussionLanguages": "Dil temelli hiçbir içerik gizlenmemiştir.", + "@noDiscussionLanguages": { + "description": "Tartışma dili eklenmeyen mesaj" + }, + "noDisplayNameSet": "Görüntüleme adı ayarlanmamış", + "@noDisplayNameSet": { + "description": "Görüntülenecek isim ayarlanmadı mesajı" + }, + "noEmailSet": "E-posta ayarlanmadı", + "@noEmailSet": { + "description": "E-posta ayarlanmamış mesajı" + }, + "noFavoritedCommunities": "Favori topluluklar yok", + "@noFavoritedCommunities": { + "description": "Çekmecede favori topluluklar için bir mesaj yok" + }, + "noInstanceBlocks": "Engellenmiş örnek yok.", + "@noInstanceBlocks": {}, + "noItems": "Hiçbir öğe", + "@noItems": { + "description": "Öğe bulunmamaktadır" + }, + "noKeywordFilters": "Hiçbir anahtar kelime filtresi eklenmedi", + "@noKeywordFilters": { + "description": "Anahtar kelime filtresi eklenmedi mesajı" + }, + "noLanguage": "Hiçbir dil", + "@noLanguage": { + "description": "Bir gönderi dili seçerken dil girişi yok" + }, + "noMatrixUserSet": "Hiçbir matris kullanıcısı ayarlanmadı", + "@noMatrixUserSet": { + "description": "Hiçbir matris kullanıcısı ayarlanmadı için mesaj" + }, + "noPostsFound": "Hiç gönderi bulunamadı.", + "@noPostsFound": {}, + "noProfileBioSet": "Profil biyografisi ayarlanmamış.", + "@noProfileBioSet": { + "description": "Profil biyografisi ayarlanmamış mesajı" + }, + "noReplies": "Cevap yok", + "@noReplies": { + "description": "Listede hiç yanıt yok etiketi" + }, + "noResultsFound": "Sonuç bulunamadı.", + "@noResultsFound": {}, + "noSubscriptions": "Abonelik Yok", + "@noSubscriptions": { + "description": "Çekmecede hiç abonelik yok mesajı" + }, + "noUserBlocks": "Engellenmiş kullanıcı yok.", + "@noUserBlocks": {}, + "noUsersFound": "Kullanıcı bulunamadı.", + "@noUsersFound": {}, + "none": "Hiçbiri", + "@none": { + "description": "Bildirim türünü, push bildirimleri devre dışı bırakıldığında tanımlar." + }, + "normal": "Normal", + "@normal": { + "description": "Normal isim kalınlığı/ağırlığı" + }, + "notValidLemmyInstance": "{instance} geçerli bir Lemmy örneği gibi görünmüyor.", + "@notValidLemmyInstance": {}, + "notValidUrl": "Geçerli bir URL değil", + "@notValidUrl": {}, + "nothingToShare": "Paylaşacak bir şey yok", + "@nothingToShare": {}, + "notifications": "{count, plural, zero {Bildirim} one {Bildirimler} other {Bildirimler}}", + "@notifications": { + "description": "Bildirim sayısı için mesaj biçimlendirme." + }, + "notificationsBehaviourSettings": "Bildirimler", + "@notificationsBehaviourSettings": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "notificationsNotAllowed": "Sistem ayarlarında Thunder için bildirimlere izin verilmiyor", + "@notificationsNotAllowed": { + "description": "Uygulama için bildirimlerin artık izin verildiği durum." + }, + "notificationsWarningDialog": "Bildirimler, tüm cihazlarda doğru şekilde çalışmayabilecek bir **deneysel özellik**tir.\n\n - Kontroller her ~15 dakikada bir gerçekleşecek ve ekstra pil tüketilecektir.\n\n - Başarılı bildirimlerin olasılığını artırmak için pil optimizasyonlarını devre dışı bırakın.\n\n Daha fazla bilgi için aşağıdaki sayfayı görüntüleyin.", + "@notificationsWarningDialog": { + "description": "Bildirimler özelliği için uyarı iletişim kutusunun içeriği" + }, + "nsfw": "Uygunsuz İçerik", + "@nsfw": { + "description": "NSFW etiketi için kısa açıklama" + }, + "nsfwWarning": "NSFW - Açmak için dokunun", + "@nsfwWarning": { + "description": "NSFW gönderileri için uyarı" + }, + "off": "kapalı", + "@off": {}, + "offline": "çevrimdışı", + "@offline": { + "description": "Kullanılamayan bir hizmeti tanımlar." + }, + "ok": "Tamam", + "@ok": {}, + "old": "Eski", + "@old": {}, + "on": "üzerinde", + "@on": {}, + "onWifi": "Wifi Üzerinde", + "@onWifi": {}, + "onlyModsCanPostInCommunity": "Bu toplulukta sadece moderatörler gönderi yapabilir.", + "@onlyModsCanPostInCommunity": {}, + "open": "Açık", + "@open": {}, + "openAccountSwitcher": "Hesap değiştiriciyi açın", + "@openAccountSwitcher": {}, + "openByDefault": "Varsayılan olarak açık", + "@openByDefault": { + "description": "Bağlantıların açılmasının varsayılan davranışını belirtme." + }, + "openInBrowser": "Tarayıcıda Aç", + "@openInBrowser": {}, + "openInstance": "Açık Örnek", + "@openInstance": {}, + "openLinksInExternalBrowser": "Harici Tarayıcıda Bağlantıları Aç", + "@openLinksInExternalBrowser": { + "description": "Bağlantıları harici bir tarayıcıda açmak için geçiş yapın." + }, + "openLinksInReaderMode": "Okuyucu Modunda Linkleri Açın", + "@openLinksInReaderMode": { + "description": "Mümkün olduğunda bağlantıları okuyucu modunda açmak için geçiş yapın." + }, + "openSettings": "Ayarları Aç", + "@openSettings": { + "description": "Sistem ayarlarını açmanızı rica ederim." + }, + "orange": "Portakal", + "@orange": { + "description": "Turuncu renk" + }, + "originalPoster": "Orijinal Gönderen", + "@originalPoster": { + "description": "Orijinal gönderenin etiketi." + }, + "overview": "Genel Bakış", + "@overview": {}, + "password": "Şifre", + "@password": {}, + "pending": "Beklemede", + "@pending": { + "description": "'Pending' için abonelik durumunu tanımlar." + }, + "permissionDenied": "Thunder'ın bildirimleri gösterme izni verilmedi. Lütfen sistem ayarlarında etkinleştirin.", + "@permissionDenied": { + "description": "Kullanıcının İS İzinlerini Reddettiği Durumda Hata Başlığı" + }, + "permissionDeniedMessage": "Bu resmi kaydetmek için Thunder'ın bazı izinlere ihtiyacı vardır ve bu izinler reddedilmiştir.", + "@permissionDeniedMessage": { + "description": "Kullanıcı işletim sistemi izinlerini reddettiğinde hata açıklaması" + }, + "pinToCommunity": "Topluluğa Sabitle", + "@pinToCommunity": { + "description": "Bir gönderiyi bir topluluğa sabitleme ayarı (moderatör eylemi)" + }, + "placeholderText": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. En azından küçük bir özür dilemek için, kimse işçilik hakkında egzersiz yapmamı istemiyor, ancak bu rahatlıkla sonuçlanabilir. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, suçlu olanlar işleri terk eden mollit anim id est laborum.", + "@placeholderText": { + "description": "Herhangi bir önizleme için yer tutucu metin. Bu, https://www.lipsum.com/ adresinden geliyor." + }, + "postBehaviourSettings": "Gönderiler", + "@postBehaviourSettings": { + "description": "Ayarlar -> Genel alt kategorisi" + }, + "postBody": "Gönderi İçeriği", + "@postBody": {}, + "postBodySettings": "Gönderi Gövde Ayarları", + "@postBodySettings": { + "description": "Gönderi gövdesi ayarları için ayarlar başlığı" + }, + "postBodySettingsDescription": "Bu ayarlar, gönderi gövdesinin görüntülenmesini etkiler.", + "@postBodySettingsDescription": { + "description": "Gönderi gövde ayarlarının açıklaması" + }, + "postBodyShowCommunityAvatar": "Topluluk Avatarını Göster", + "@postBodyShowCommunityAvatar": { + "description": "Gönderi içerikleri için topluluk avatarını gösterip göstermeme" + }, + "postBodyShowCommunityInstance": "Topluluk Örneğini Göster", + "@postBodyShowCommunityInstance": { + "description": "Gönderi gövdeleri için topluluk örneğini gösterip göstermeme" + }, + "postBodyShowUserInstance": "Kullanıcı Örneğini Göster", + "@postBodyShowUserInstance": { + "description": "Kullanıcı örneğini gönderi gövdeleri için gösterip göstermeme" + }, + "postBodyViewType": "Gönderi Gövde Görünüm Tipi", + "@postBodyViewType": { + "description": "Gönderi gövdesi görünüm türü ayarı için ayar adı" + }, + "postContentFontScale": "Gönderi İçeriği Yazı Tipi Ölçeği", + "@postContentFontScale": { + "description": "Gönderi içeriği yazı tipi ölçeği ayarı" + }, + "postCreatedSuccessfully": "Gönderi başarıyla oluşturuldu!", + "@postCreatedSuccessfully": { + "description": "Kullanıcının gönderisinin başarıyla oluşturulduğunu bildirme" + }, + "postLocked": "Gönderi kilitlendi. Cevap vermek yasaktır.", + "@postLocked": {}, + "postMetadataInstructions": "İstenilen bilgileri sürükleyip bırakarak metaveri bilgilerini özelleştirebilirsiniz.", + "@postMetadataInstructions": { + "description": "Gönderi metaveri bilgisini nasıl özelleştireceğinize dair talimatlar. Bu, Görünüm -> Gönderi ayarları sayfasında bulunur." + }, + "postNSFW": "NSFW olarak işaretle", + "@postNSFW": {}, + "postPreview": "Verilen ayarlarla bir gönderi önizlemesi gösterin", + "@postPreview": { + "description": "Ayarlar -> Görünüm -> Gönderiler için gönderi önizlemesi açıklaması" + }, + "postSavedAsDraft": "Taslak olarak kaydedildi", + "@postSavedAsDraft": {}, + "postShowUserInstance": "Kullanıcı Örneğini Göster", + "@postShowUserInstance": { + "description": "Kullanıcı örneğini gönderi listelerinde gösterip göstermeme" + }, + "postSwipeActions": "Kaydırma Sonrası Eylemler", + "@postSwipeActions": { + "description": "Kaydırma sonrası eylemler için ayarlar" + }, + "postSwipeGesturesHint": "Düğmeleri kullanmayı mı düşünüyorsunuz? Genel ayarlarda posta kartlarındaki düğmelerin ne olduğunu değiştirin.", + "@postSwipeGesturesHint": {}, + "postTitle": "Başlık", + "@postTitle": {}, + "postTitleFontScale": "Yazı Başlığı Font Ölçeği", + "@postTitleFontScale": { + "description": "Gönderi başlığı yazı tipi ölçeği ayarı" + }, + "postTogglePreview": "Önizleme Geçişi", + "@postTogglePreview": {}, + "postURL": "URL", + "@postURL": {}, + "postUploadImageError": "Resim yüklenemedi", + "@postUploadImageError": {}, + "postViewType": "Gönderi Görünüm Tipi", + "@postViewType": { + "description": "Ayarlar -> Görünüm -> Gönderiler bölümünde gönderi görünüm türü (kompakt/kart) için etiket" + }, + "posts": "Gönderiler", + "@posts": {}, + "preview": "Önizleme", + "@preview": {}, + "profileAppliedSuccessfully": "{profile} başarıyla uygulandı!", + "@profileAppliedSuccessfully": {}, + "profileBio": "Profil Biyografisi", + "@profileBio": { + "description": "Profil biyografisi için ayarlar" + }, + "profiles": "Profiller", + "@profiles": {}, + "public": "Kamu", + "@public": { + "description": "Kamu topluluğu görünürlüğü" + }, + "pureBlack": "Saf Siyah", + "@pureBlack": { + "description": "Saf siyah temayı kullanmayı tarif eder" + }, + "purgedComment": "Silinmiş Yorum", + "@purgedComment": { + "description": "Yönetici eyleminin bir yorumu temizleme için kısa açıklaması" + }, + "purgedCommunity": "Temizlenmiş Topluluk", + "@purgedCommunity": { + "description": "Moderatör eylemi için kısa bir açıklama: Bir topluluğu temizleme" + }, + "purgedPerson": "Temizlenmiş Kişi", + "@purgedPerson": { + "description": "Bir kişiyi temizlemek için moderatör eyleminin kısa açıklaması" + }, + "purgedPost": "Silinmiş Gönderi", + "@purgedPost": { + "description": "Moderatör eylemi için kısa bir açıklama: Bir gönderiyi silme" + }, + "purple": "Mor", + "@purple": { + "description": "Mor renk" + }, + "pushNotification": "Bildirimler", + "@pushNotification": { + "description": "Bildirimleri itmek için ayarlar" + }, + "pushNotificationDescription": "Etkinleştirilirse, Thunder JWT belirteç(ler)inizi sunucuya göndererek yeni bildirimler için anket yapar. \n\n **NOT:** Bu, uygulama bir sonraki sefer başlatıldığında etkili olacaktır.", + "@pushNotificationDescription": { + "description": "Bildirim ayarlarının açıklaması" + }, + "pushNotificationServer": "Bildirim Sunucusu", + "@pushNotificationServer": { + "description": "Push bildirim sunucusunu seçme ayarı" + }, + "pushNotificationServerDescription": "Push bildirim sunucusunu yapılandırın. Sunucu, cihazınıza push bildirimleri göndermek için doğru şekilde yapılandırılmalıdır.\n\n **Yalnızca kimlik bilgilerinize güvendiğiniz bir sunucuya girin.**", + "@pushNotificationServerDescription": { + "description": "Bildirim sunucusu ayarını seçme açıklaması" + }, + "reachedTheBottom": "Hmm. Görünüşe göre dibe ulaştınız.", + "@reachedTheBottom": {}, + "readAll": "Hepsini Oku", + "@readAll": {}, + "reason": "Sebep", + "@reason": { + "description": "Moderasyon eyleminin sebebi (örneğin, gönderinin kaldırılması)" + }, + "red": "Kırmızı", + "@red": { + "description": "Kırmızı renk" + }, + "reduceAnimations": "Animasyonları Azalt", + "@reduceAnimations": { + "description": "Animasyonları azaltmak için geçiş yapın." + }, + "reducesAnimations": "Thunder içinde kullanılan animasyonları azaltır", + "@reducesAnimations": {}, + "refresh": "Yenile", + "@refresh": {}, + "refreshContent": "İçeriği Yenile", + "@refreshContent": {}, + "removalReason": "Kaldırma Sebebi", + "@removalReason": { + "description": "Bir gönderiyi kaldırma diyalogu için başlık" + }, + "remove": "Kaldır", + "@remove": {}, + "removeAccount": "Hesabı Kaldır", + "@removeAccount": {}, + "removeFromFavorites": "Favorilerden çıkar", + "@removeFromFavorites": { + "description": "Çekmecedeki bir topluluğu favorilerden kaldırma eylemi" + }, + "removeInstance": "Örneği kaldır", + "@removeInstance": {}, + "removeKeyword": "\"{keyword}\" kaldırılsın mı?", + "@removeKeyword": { + "description": "Bir anahtar kelime filtresinin kaldırılması açıklaması" + }, + "removeKeywordFilter": "Anahtar Kelimeyi Kaldır", + "@removeKeywordFilter": { + "description": "Anahtar Kelime Filtresini Kaldırmak İçin Diyalog Başlığı" + }, + "removePost": "Gönderiyi Kaldır", + "@removePost": { + "description": "Bir gönderiyi kaldırma eylemi (moderatör eylemi)" + }, + "removedComment": "Kaldırılmış Yorum", + "@removedComment": { + "description": "Yönetici eyleminin bir yorumu kaldırma için kısa açıklaması" + }, + "removedCommunity": "Kaldırılan Topluluk", + "@removedCommunity": { + "description": "Topluluk kaldırma için moderatör eyleminin kısa açıklaması" + }, + "removedCommunityFromSubscriptions": "Topluluktan abonelik iptal edildi.", + "@removedCommunityFromSubscriptions": {}, + "removedInstanceMod": "Kaldırılan Örnek Mod", + "@removedInstanceMod": { + "description": "Bir örnekte bir moderatörü kaldırmak için moderatör eyleminin kısa açıklaması" + }, + "removedModFromCommunity": "Topluluktan Mod Kaldırıldı", + "@removedModFromCommunity": { + "description": "Bir topluluktan bir moderatörü kaldırmak için moderatör eyleminin kısa açıklaması" + }, + "removedPost": "Kaldırılan Gönderi", + "@removedPost": { + "description": "Moderatör eyleminin bir gönderiyi kaldırma kısa açıklaması" + }, + "reply": "{count, plural, zero {Yanıt} one {Yanıt} other {Yanıtlar} }", + "@reply": {}, + "replyColor": "Cevap Renk", + "@replyColor": { + "description": "Yanıt renk ayarı adı" + }, + "replyNotSupported": "Bu görünümden yanıt verme şu anda henüz desteklenmiyor.", + "@replyNotSupported": {}, + "replyToPost": "Posta Yanıtla", + "@replyToPost": {}, + "replyingTo": "{author} adlı kişiye yanıt veriyor.", + "@replyingTo": {}, + "report": "{count, plural, zero {Rapor} one {Rapor} other {Raporlar} } ", + "@report": {}, + "reportComment": "Yorumu Bildir", + "@reportComment": {}, + "reset": "Sıfırla", + "@reset": { + "description": "Ayarlar -> Görünüm -> Gönderiler/Yorumlar için sıfırlama düğmesi etiketi" + }, + "resetCommentPreferences": "Yorum tercihlerini sıfırla", + "@resetCommentPreferences": { + "description": "Yorum görünüm ayarlarındaki düğme için semantik etiket." + }, + "resetPostPreferences": "Gönderi tercihlerini sıfırla", + "@resetPostPreferences": { + "description": "Gönderi görünüm ayarlarındaki düğme için semantik etiket." + }, + "resetPreferences": "Tercihleri Sıfırla", + "@resetPreferences": {}, + "resetPreferencesAndData": "Tercihleri ve Verileri Sıfırla", + "@resetPreferencesAndData": { + "description": "Tüm kullanıcı tercihlerini ve verilerini sıfırlamak için hata ayıklama ayarı başlığı." + }, + "restore": "Geri yükle", + "@restore": {}, + "restorePost": "Gönderiyi Geri Yükle", + "@restorePost": { + "description": "Bir gönderiyi geri getirme eylemi (moderatör eylemi)" + }, + "restoredComment": "Yeniden Yorumlanan Yorum", + "@restoredComment": { + "description": "Moderatör eyleminin bir yorumu geri getirme için kısa açıklaması" + }, + "restoredCommentFromDraft": "Taslaktan geri yüklenen yorum", + "@restoredCommentFromDraft": {}, + "restoredCommunity": "Onarılmış Topluluk", + "@restoredCommunity": { + "description": "Moderatör eylemi için kısa açıklama: Bir topluluğu yeniden kurma" + }, + "restoredPost": "Onarılmış Gönderi", + "@restoredPost": { + "description": "Moderatör eyleminin bir gönderiyi geri getirme için kısa açıklaması" + }, + "restoredPostFromDraft": "Taslaktan geri yüklenen gönderi", + "@restoredPostFromDraft": {}, + "retry": "Tekrar dene", + "@retry": {}, + "rightLongSwipe": "Sağ Uzun Kaydırma", + "@rightLongSwipe": { + "description": "Sağa uzun süreli kaydırma ayarı" + }, + "rightShortSwipe": "Sağ Kısa Sürükleme", + "@rightShortSwipe": { + "description": "Sağa kısa süpürme ayarı" + }, + "save": "Kaydet", + "@save": {}, + "saveColor": "Renk Kaydet", + "@saveColor": { + "description": "Renk ayarını kaydet" + }, + "saveSettings": "Ayarları Kaydet", + "@saveSettings": { + "description": "Uygulama ayarlarını kaydetme eylemi." + }, + "saved": "Kaydedildi", + "@saved": {}, + "scaled": "Ölçeklendirilmiş", + "@scaled": {}, + "scrapeMissingLinkPreviews": "Eksik Link Önizlemelerini Kazı", + "@scrapeMissingLinkPreviews": { + "description": "Eksik dış bağlantı önizlemelerini kazımak için geçiş yapın." + }, + "scrapeMissingPreviews": "Etkinleştirme performansı düşürecektir.", + "@scrapeMissingPreviews": { + "description": "Bağlantı kazıma etkinleştirildiğinde olası performans etkisi hakkında bildirim." + }, + "screenReaderProfile": "Ekran Okuyucu Profili", + "@screenReaderProfile": {}, + "screenReaderProfileDescription": "Ekran okuyucuları için Thunder'ı optimize eder, genel öğeleri azaltır ve potansiyel olarak çelişkili hareketleri kaldırır.", + "@screenReaderProfileDescription": {}, + "search": "Ara", + "@search": {}, + "searchByText": "Metinle ara", + "@searchByText": {}, + "searchByUrl": "URL ile ara", + "@searchByUrl": {}, + "searchComments": "Yorumları Ara", + "@searchComments": {}, + "searchCommentsFederatedWith": "{instance} ile federasyon yapılan yorumları ara", + "@searchCommentsFederatedWith": {}, + "searchCommunitiesFederatedWith": "{instance} ile federasyon kurmuş toplulukları ara", + "@searchCommunitiesFederatedWith": {}, + "searchInstance": "{instance} ara", + "@searchInstance": {}, + "searchInstancesFederatedWith": "{instance} ile federasyon kurulan örnekleri arayın.", + "@searchInstancesFederatedWith": { + "description": "Örnekler aramak için yer tutucu metin" + }, + "searchPostSearchType": "Gönderi Arama Türünü Seçin", + "@searchPostSearchType": {}, + "searchPostsFederatedWith": "{instance} ile federasyon kurulan gönderileri ara", + "@searchPostsFederatedWith": {}, + "searchTerm": "Arama terimi", + "@searchTerm": {}, + "searchUsersFederatedWith": "{instance} ile federasyon kurmuş kullanıcıları ara", + "@searchUsersFederatedWith": {}, + "selectAccountToCommentAs": "Yorum yapmak için hesap seçin", + "@selectAccountToCommentAs": { + "description": "Diğer kullanıcı olarak yorum yapma için profil modal başlığı" + }, + "selectAccountToPostAs": "Yayın yapılacak hesabı seçin", + "@selectAccountToPostAs": { + "description": "Gönderi yaparken hesap seçici için başlık" + }, + "selectAll": "Tümünü seç", + "@selectAll": { + "description": "Tüm metni seçme eylemi" + }, + "selectCommunity": "Bir topluluk seçin", + "@selectCommunity": {}, + "selectFeedType": "Besleme Türünü Seçin", + "@selectFeedType": {}, + "selectLanguage": "Dil Seçin", + "@selectLanguage": { + "description": "Yazı oluşturma sayfasında bir dil seçme istemi" + }, + "selectSearchType": "Arama Türü Seçin", + "@selectSearchType": {}, + "selectText": "Metni Seçin", + "@selectText": { + "description": "Metin seçme için gönderi ve yorum eylemi" + }, + "sendBackgroundTestLocalNotification": "Arka plan testi yerel bildirimini gönder", + "@sendBackgroundTestLocalNotification": { + "description": "Test etme için bir arka plan bildirimi gönderme seçeneği" + }, + "sendBackgroundTestUnifiedPushNotification": "Arka plan testi UnifiedPush bildirimi gönder", + "@sendBackgroundTestUnifiedPushNotification": { + "description": "Test için bir arka plan UnifiedPush bildirimi gönderme seçeneği" + }, + "sendTestLocalNotification": "Yerel bildirim testi gönder", + "@sendTestLocalNotification": { + "description": "Test için bir bildirim gönderme seçeneği" + }, + "sendTestUnifiedPushNotification": "Test UnifiedPush bildirimi gönderin", + "@sendTestUnifiedPushNotification": { + "description": "BirleşikPush bildirimi gönderme seçeneği için test yapma" + }, + "sensitiveContentWarning": "Hassas içerik içerebilir. Açmak için dokunun.", + "@sensitiveContentWarning": { + "description": "Hassas içerik uyarısı (örneğin, modlog'dan)" + }, + "sentRequestForTestNotification": "Test bildirimi için istek gönderildi.", + "@sentRequestForTestNotification": {}, + "serverErrorComments": "Daha fazla yorum getirilirken bir sunucu hatasıyla karşılaşıldı: {message}", + "@serverErrorComments": {}, + "setAction": "Eylemi Ayarla", + "@setAction": { + "description": "Eylemi ayarlamak için etiket (kısa basma, uzun basma)" + }, + "setLongPress": "Uzun basma eylemi olarak ayarla", + "@setLongPress": {}, + "setShortPress": "Kısa basma eylemi olarak ayarla", + "@setShortPress": {}, + "settingOverrideLabel": "Bu ayarlar, Thunder'ın varsayılan ayarlarını geçersiz kılar.", + "@settingOverrideLabel": { + "description": "(Mesaj) ayarlarının uygulama ayarlarının üzerine yazıldığını belirtir." + }, + "settingTypeNotSupported": "{settingType} türü ayarlar henüz desteklenmiyor.", + "@settingTypeNotSupported": {}, + "settings": "Ayarlar", + "@settings": {}, + "settingsExportedSuccessfully": "Ayarlar başarıyla ''{savedFilePath}' konumuna kaydedildi.", + "@settingsExportedSuccessfully": { + "description": "Kullanıcıya ayarlar başarıyla dışa aktarıldığında gösterilen mesaj" + }, + "settingsFeedCards": "Bu ayarlar ana beslemekteki kartlara uygulanır, gönderileri gerçekten açtığınızda eylemler her zaman kullanılabilir.", + "@settingsFeedCards": { + "description": "Ana beslemedeki kartların görüntülenmesini kontrol etme ayarları." + }, + "settingsImportedSuccessfully": "Ayarlar başarıyla içe aktarıldı!", + "@settingsImportedSuccessfully": { + "description": "Kullanıcıya ayarlar başarıyla içe aktarıldığında gösterilen mesaj" + }, + "settingsNotExportedSuccessfully": "Ayarlar başarıyla kaydedilmedi veya işlem iptal edildi.", + "@settingsNotExportedSuccessfully": { + "description": "Kullanıcıya ayarlar başarısız bir şekilde dışa aktarıldığında gösterilen mesaj" + }, + "settingsNotImportedSuccessfully": "Ayarlar başarıyla içe aktarılmadı veya işlem iptal edildi.", + "@settingsNotImportedSuccessfully": { + "description": "Kullanıcıya ayarlar başarısız bir şekilde içe aktarıldığında gösterilen mesaj" + }, + "share": "Paylaş", + "@share": {}, + "shareComment": "Yorumu Paylaş Linki", + "@shareComment": { + "description": "Yorum paylaşma menü öğesi" + }, + "shareCommentLocal": "Yorum Paylaş Linki (Benim Örneğim)", + "@shareCommentLocal": { + "description": "Yorum için yerel bir bağlantıyı paylaşma menü öğesi" + }, + "shareCommunity": "Topluluğu Paylaş", + "@shareCommunity": { + "description": "Bir topluluğu paylaşma başlığı" + }, + "shareCommunityLink": "Topluluk Bağlantısını Paylaş", + "@shareCommunityLink": { + "description": "Topluluğu paylaşma menü öğesi" + }, + "shareCommunityLinkLocal": "Topluluk Bağlantısını Paylaş (Benim Örneğim)", + "@shareCommunityLinkLocal": { + "description": "Yerel topluluk bağlantısını paylaşma menü öğesi" + }, + "shareImage": "Resim Paylaş", + "@shareImage": { + "description": "Bir resmi paylaşma eylemi" + }, + "shareLemmyLink": "Lemmy Linkini Paylaş", + "@shareLemmyLink": { + "description": "Lemmy bağlantısını paylaşma menü öğesi" + }, + "shareLink": "Dış Bağlantıyı Paylaş", + "@shareLink": {}, + "shareMedia": "Medya Paylaş", + "@shareMedia": {}, + "shareMediaLink": "Medya Bağlantısını Paylaş", + "@shareMediaLink": { + "description": "Bir gönderiyle ilişkili medyanın bağlantısını paylaşma eylemi" + }, + "shareOriginalLink": "Orijinal Bağlantıyı Paylaş", + "@shareOriginalLink": { + "description": "Bir gönderiyle ilişkilendirilmiş orijinal bağlantıyı paylaşma eylemi" + }, + "sharePost": "Gönderi Bağlantısını Paylaş", + "@sharePost": {}, + "sharePostLocal": "Gönderi Bağlantısını Paylaş (Benim Örneğim)", + "@sharePostLocal": { + "description": "Benim örneğimde bir gönderiye bağlantı paylaşma öğesi" + }, + "shareThumbnail": "Önizleme Paylaş", + "@shareThumbnail": { + "description": "Bir gönderinin küçük resmini paylaşma eylemi" + }, + "shareThumbnailAsImage": "Önizlemeyi Resim Olarak Paylaş", + "@shareThumbnailAsImage": { + "description": "Bir gönderinin küçük resmini bir resim olarak paylaşma eylemi" + }, + "shareUser": "Kullanıcıyı Paylaş", + "@shareUser": { + "description": "Bir kullanıcıyı paylaşma başlığı" + }, + "shareUserLink": "Kullanıcı Bağlantısını Paylaş", + "@shareUserLink": { + "description": "Kullanıcıyı paylaşma menü öğesi" + }, + "shareUserLinkLocal": "Kullanıcı Bağlantısını Paylaş (Benim Örneğim)", + "@shareUserLinkLocal": { + "description": "Yerel kullanıcı bağlantısını paylaşma menü öğesi" + }, + "showAll": "Hepsini Göster", + "@showAll": {}, + "showBotAccounts": "Bot Hesapları Göster", + "@showBotAccounts": { + "description": "Bot hesaplarını gösterme seçeneği." + }, + "showCommentActionButtons": "Yorum Eylem Düğmelerini Göster", + "@showCommentActionButtons": { + "description": "Yorum eylem düğmelerini göstermek için geçiş yapın." + }, + "showCommunityDisplayNames": "Topluluk Görünen İsimlerini Göster", + "@showCommunityDisplayNames": { + "description": "Topluluk görüntüleme isimlerini gösterme ayarı" + }, + "showCrossPosts": "Çapraz Gönderileri Göster", + "@showCrossPosts": { + "description": "Çapraz gönderileri göstermek için geçiş yapın." + }, + "showEdgeToEdgeImages": "Kenarından Kenarına Görseller Göster", + "@showEdgeToEdgeImages": { + "description": "Kenarından kenarına görüntüleri görüntülemek için geçiş yapın." + }, + "showFullDate": "Tam Tarihi Göster", + "@showFullDate": { + "description": "Gönderilerde tam tarihi göster seçeneğini açın" + }, + "showFullDateDescription": "Gönderilerde tam tarihi göster", + "@showFullDateDescription": { + "description": "Gönderilerde tam tarihi gösterme açıklaması" + }, + "showFullHeightImages": "Tam Yükseklikte Görüntüleri Göster", + "@showFullHeightImages": { + "description": "Tam yükseklikteki görüntüleri görüntülemek için geçiş yapın." + }, + "showInAppUpdateNotifications": "Yeni GitHub Sürümlerinden Haberdar Olun", + "@showInAppUpdateNotifications": { + "description": "Yeni GitHub sürümlerinden haberdar olmak için geçiş yapın." + }, + "showLess": "Daha az göster", + "@showLess": {}, + "showMore": "Daha fazla göster", + "@showMore": {}, + "showNavigationLabels": "Navigasyon Etiketlerini Göster", + "@showNavigationLabels": { + "description": "Navigasyon etiketlerini gösterme ayarı" + }, + "showNavigationLabelsDescription": "Alt navigasyon düğmelerinin altında etiketlerin görüntülenip görüntülenmeyeceği", + "@showNavigationLabelsDescription": { + "description": "Navigasyon etiketlerini gösterme ayarı için açıklama" + }, + "showNsfwContent": "NSFW İçeriği Göster", + "@showNsfwContent": { + "description": "NSFW içeriği göstermek için geçiş yapın." + }, + "showPassword": "Şifreyi Göster", + "@showPassword": {}, + "showPostAuthor": "Gönderi Yazarını Göster", + "@showPostAuthor": { + "description": "Yazı yazarını göstermek için geçiş yapın." + }, + "showPostAuthorSubtitle": "Yazı yazarı her zaman topluluk beslemelerinde gösterilir.", + "@showPostAuthorSubtitle": { + "description": "Gösteri Yayın Yazarı ayarı için altyazı" + }, + "showPostCommunityIcons": "Topluluk Simgelerini Göster", + "@showPostCommunityIcons": { + "description": "Topluluk simgelerini göstermek için geçiş yapın." + }, + "showPostSaveAction": "Kaydet Butonunu Göster", + "@showPostSaveAction": { + "description": "Kaydet düğmesini göstermek için geçiş yapın." + }, + "showPostTextContentPreview": "Metin Önizlemesini Göster", + "@showPostTextContentPreview": { + "description": "Metin önizlemelerini göstermek için geçiş yapın." + }, + "showPostTitleFirst": "İlk Önce Gösteri Başlığını", + "@showPostTitleFirst": { + "description": "İlk olarak gönderi başlığını göstermeye geçin." + }, + "showPostVoteActions": "Oy Verme Düğmelerini Göster", + "@showPostVoteActions": { + "description": "Oy verme düğmelerini göstermek için geçiş yapın." + }, + "showReadPosts": "Okunan Gönderileri Göster", + "@showReadPosts": { + "description": "Beslemede okunan gönderileri gösterme ayarı." + }, + "showScoreCounters": "Kullanıcı Skorlarını Göster", + "@showScoreCounters": { + "description": "Kullanıcı puanlarını göstermek için geçiş yapın." + }, + "showScores": "Gönderi/Yorum Puanlarını Göster", + "@showScores": { + "description": "Gönderilerde ve yorumlarda puanları gösterme seçeneği." + }, + "showTextPostIndicator": "Metin Gönderi Göstergesini Göster", + "@showTextPostIndicator": { + "description": "Metin gönderi göstergesini aç/kapat." + }, + "showThumbnailPreviewOnRight": "Sağda Küçük Resimleri Göster", + "@showThumbnailPreviewOnRight": { + "description": "Sağ tarafta küçük resimleri göstermek için geçiş yapın." + }, + "showUpdateChangelogs": "Güncelleme Değişiklik Günlüklerini Göster", + "@showUpdateChangelogs": { + "description": "Güncellemelerden sonra değişiklik kayıtlarını gösterme ayarı" + }, + "showUpdateChangelogsSubtitle": "Bir güncellemeden sonra değişikliklerin bir listesini gösterin", + "@showUpdateChangelogsSubtitle": { + "description": "Güncellemelerden sonra değişiklik kayıtlarını gösterme ayarı için altyazı" + }, + "showUserAvatar": "Kullanıcı Avatarını Göster", + "@showUserAvatar": { + "description": "Kullanıcı avatarını göstermek için geçiş yapın." + }, + "showUserDisplayNames": "Kullanıcı Görünen İsimlerini Göster", + "@showUserDisplayNames": { + "description": "Kullanıcı görüntüleme isimlerini göstermek için geçiş yapın." + }, + "showUserInstance": "Kullanıcı Örneğini Göster", + "@showUserInstance": { + "description": "Kullanıcı örneğini göstermek için geçiş yapın." + }, + "sidebar": "Kenar çubuğu", + "@sidebar": {}, + "sidebarBottomNavDoubleTapDescription": "Alt menüyü çift tıklayarak yan menüyü açın", + "@sidebarBottomNavDoubleTapDescription": {}, + "sidebarBottomNavSwipeDescription": "Alt navigasyonu kaydırarak yan menüyü açın", + "@sidebarBottomNavSwipeDescription": {}, + "small": "Küçük", + "@small": { + "description": "Küçük yazı tipi ölçeği için açıklama" + }, + "somethingWentWrong": "Hata oluştu, bir şeyler yanlış gitti!", + "@somethingWentWrong": {}, + "sortBy": "Sırala Göre", + "@sortBy": {}, + "sortByTop": "En Üste Göre Sırala", + "@sortByTop": {}, + "sortOptions": "Sıralama Seçenekleri", + "@sortOptions": {}, + "spoiler": "Spoiler", + "@spoiler": { + "description": "Spoiler için yer tutucu etiket" + }, + "standard": "Standart", + "@standard": { + "description": "Standart bir görsel yoğunluğu tanımlar." + }, + "stats": "İstatistikler", + "@stats": { + "description": "Topluluk istatistikleri" + }, + "status": "Durum", + "@status": { + "description": "Eylemin durumu" + }, + "submit": "Gönder", + "@submit": {}, + "subscribe": "Abone ol", + "@subscribe": {}, + "subscribeToCommunity": "Topluluğa Abone Ol", + "@subscribeToCommunity": { + "description": "Bir topluluğa abone olma eylemi" + }, + "subscribed": "Abone olundu", + "@subscribed": {}, + "subscriptions": "Abonelikler", + "@subscriptions": {}, + "successfullyBlocked": "Engellendi.", + "@successfullyBlocked": {}, + "successfullyBlockedCommunity": "{communityName} engellendi", + "@successfullyBlockedCommunity": {}, + "successfullyBlockedUser": "{username} engellendi", + "@successfullyBlockedUser": { + "description": "Bir kullanıcının başarıyla engellendiği bildirimi" + }, + "successfullyUnblocked": "Engellenmemiş.", + "@successfullyUnblocked": {}, + "successfullyUnblockedCommunity": "Engellenmemiş {communityName}", + "@successfullyUnblockedCommunity": {}, + "successfullyUnblockedUser": "{username} engellemesini kaldırdı.", + "@successfullyUnblockedUser": { + "description": "Bir kullanıcının başarıyla engellemesinin kaldırılması için bildirim" + }, + "suchAs": "gibi", + "@suchAs": { + "description": "Bu bir bağlaç ifadesidir ve 'gibi', 'örneğin', 'mesela' vb. herhangi bir eşdeğerine çevrilebilir." + }, + "suggestedTitle": "Önerilen başlık", + "@suggestedTitle": { + "description": "Gönderi için önerilen başlık." + }, + "system": "Sistem", + "@system": { + "description": "Tema için sistem ayarlarının kullanılmasını açıklar" + }, + "tabletMode": "Tablet Modu (2-sütun görünümü)", + "@tabletMode": { + "description": "2 sütunlu tablet modunu etkinleştirmek için geçiş yapın." + }, + "tapToExit": "Tekrar geri basın çıkmak için", + "@tapToExit": {}, + "tappableAuthorCommunity": "Dokunulabilir Yazarlar & Topluluklar", + "@tappableAuthorCommunity": { + "description": "Yazarları ve toplulukları tıklanabilir yapmak için geçiş yapın." + }, + "teal": "Cam göbeği", + "@teal": { + "description": "Turkuaz rengi" + }, + "testBackgroundNotificationDescription": "Gök gürültüsü kendini kapatacak ve ardından arka planda bir bildirim oluşturmaya çalışacak. (Birkaç dakika sürebilir.)", + "@testBackgroundNotificationDescription": { + "description": "Arka plan test bildirimi hakkında bir mesaj" + }, + "testBackgroundUnifiedPushNotificationDescription": "Gök gürültüsü, bildirim sunucusundan gecikmeli bir bildirim göndermesini isteyecek ve sonra kendini kapatacaktır. (Birkaç dakika sürebilir.)", + "@testBackgroundUnifiedPushNotificationDescription": { + "description": "UnifiedPush arka plan test bildirimi hakkında bir mesaj" + }, + "text": "Metin", + "@text": {}, + "textActions": "Metin İşlemleri", + "@textActions": { + "description": "Metin işlemleri için seçenek" + }, + "theme": "Tema", + "@theme": { + "description": "Tema için ayarlar" + }, + "themeAccentColor": "Aksan Renkleri", + "@themeAccentColor": { + "description": "Tema vurgu rengi için ayarlar" + }, + "themePrimary": "Birincil Tema", + "@themePrimary": { + "description": "Birincil tema renginin adı" + }, + "themeSecondary": "İkincil Tema", + "@themeSecondary": { + "description": "İkincil tema renginin adı" + }, + "themeTertiary": "Üçüncül Tema", + "@themeTertiary": { + "description": "Birincil tema renginin adı" + }, + "theming": "Temalandırma", + "@theming": { + "description": "Ayarlar Başlığı -> Görünüm -> Tema Oluşturma" + }, + "thickness": "Kalınlık", + "@thickness": { + "description": "Bir kalınlığı (örneğin, bölme) tanımlar" + }, + "thisAccount": "Bu Hesap", + "@thisAccount": { + "description": "Hesap ayarlarında şu anda aktif olan hesaba atıfta bulunma" + }, + "thunderHasBeenUpdated": "Gök gürültüsü {version} sürümüne güncellendi!", + "@thunderHasBeenUpdated": { + "description": "Thunder güncellendiğinde değişiklik günlüğü için başlık" + }, + "thunderNotificationServer": "Gök Gürültüsü Bildirim Sunucusu: {server}", + "@thunderNotificationServer": { + "description": "Kullanıcının şu anda seçili olan bildirim sunucusunun ne olduğunu bilmesi için metin" + }, + "timeoutComments": "Hata: Yorumları almak için yapılan denemede zaman aşımı oldu.", + "@timeoutComments": {}, + "timeoutErrorMessage": "Bir yanıt beklerken zaman aşımına uğradı.", + "@timeoutErrorMessage": {}, + "timeoutSaveComment": "Hata: Bir yorumu kaydetmeye çalışırken zaman aşımı oldu.", + "@timeoutSaveComment": {}, + "timeoutSavingPost": "Hata: Gönderi kaydedilmeye çalışılırken zaman aşımı oldu.", + "@timeoutSavingPost": {}, + "timeoutUpvoteComment": "Hata: Yorumda oy kullanmaya çalışırken zaman aşımı oldu.", + "@timeoutUpvoteComment": {}, + "timeoutVotingPost": "Hata: Gönderiye oy verme girişiminde zaman aşımı.", + "@timeoutVotingPost": {}, + "toggelRead": "Okuma Modunu Değiştir", + "@toggelRead": {}, + "top": "Üst", + "@top": {}, + "topAll": "Tüm zamanların en iyisi", + "@topAll": {}, + "topDay": "Bugünün En İyileri", + "@topDay": {}, + "topHour": "Son Saatteki En Üstteki", + "@topHour": {}, + "topMonth": "En İyi Ay", + "@topMonth": {}, + "topNineMonths": "Son 9 Ayın En İyileri", + "@topNineMonths": { + "description": "Geçmiş 9 ay için en üstte sıralama modu" + }, + "topSixHour": "Son 6 Saatte En Üstte", + "@topSixHour": {}, + "topSixMonths": "Son 6 Ayın En İyileri", + "@topSixMonths": { + "description": "Son 6 ayda en üstteki için sıralama modu" + }, + "topThreeMonths": "Son 3 Ayın En İyileri", + "@topThreeMonths": { + "description": "Son 3 ayda en üstte olanları sıralama modu" + }, + "topTwelveHour": "Son 12 Saatte En Çok İzlenenler", + "@topTwelveHour": {}, + "topWeek": "Haftanın Zirvesi", + "@topWeek": {}, + "topYear": "En İyi Yıl", + "@topYear": {}, + "totp": "TOTP (isteğe bağlı)", + "@totp": {}, + "transferredModToCommunity": "Aktarılan Topluluk", + "@transferredModToCommunity": { + "description": "Moderatör eyleminin kısa bir açıklaması topluluğu aktarmak için" + }, + "translationsMayNotBeComplete": "Lütfen çevirilerin tam olmayabileceğini unutmayın.", + "@translationsMayNotBeComplete": { + "description": "Ayarlar bölümünde bir dil seçerken çevirilerin tam olmayabileceği konusunda uyarı." + }, + "trendingCommunities": "Trend Olan Topluluklar", + "@trendingCommunities": {}, + "trySearchingFor": "... aramayı deneyin.", + "@trySearchingFor": { + "description": "Kullanıcının farklı bir tür aramayı denemesi önerisi" + }, + "unableToFindCommunity": "Topluluk bulunamadı", + "@unableToFindCommunity": {}, + "unableToFindCommunityName": "'{communityName}' adlı topluluk bulunamadı.", + "@unableToFindCommunityName": { + "description": "Hata mesajı: İsmini dahil ettiğimiz bir topluluğu bulamadığımızda" + }, + "unableToFindCommunityOnInstance": "Seçilen kullanıcının örneğinde seçilen topluluk bulunamadı.", + "@unableToFindCommunityOnInstance": { + "description": "Bir örnekte bir topluluk bulamadığımızda gösterilecek hata mesajı" + }, + "unableToFindInstance": "Örnek bulunamadı", + "@unableToFindInstance": {}, + "unableToFindLanguage": "Dil bulunamadı", + "@unableToFindLanguage": { + "description": "Dil bulunamadığında hata mesajı." + }, + "unableToFindPost": "Gönderi bulunamadı", + "@unableToFindPost": { + "description": "Bir gönderi bulamadığımızda hata mesajı" + }, + "unableToFindUser": "Kullanıcı bulunamadı", + "@unableToFindUser": {}, + "unableToFindUserName": "Kullanıcı '{username}' bulunamadı.", + "@unableToFindUserName": {}, + "unableToLoadImage": "Resim yüklenemiyor", + "@unableToLoadImage": { + "description": "Bir ikili görüntüyü yükleyemediğimizde kullanılacak yer tutucu" + }, + "unableToLoadImageFrom": "{Domain} adresinden resim yüklenemiyor", + "@unableToLoadImageFrom": { + "description": "URL'den bir resim yükleyemediğimizde kullanılacak yer tutucu" + }, + "unableToLoadInstance": "{instance} yüklenemiyor", + "@unableToLoadInstance": {}, + "unableToLoadPostsFrominstance": "{Instance} 'dan gönderiler yüklenemiyor", + "@unableToLoadPostsFrominstance": {}, + "unableToLoadReplies": "Daha fazla yanıt yüklenemiyor.", + "@unableToLoadReplies": {}, + "unableToNavigateToInstance": "{instanceHost}'a gidilemiyor. Geçerli bir Lemmy örneği olmayabilir.", + "@unableToNavigateToInstance": { + "description": "Bir Lemmy örneğine gidilemediğinde hata mesajı" + }, + "unableToResolveReport": "Raporu çözme konusunda başarısız oldu", + "@unableToResolveReport": { + "description": "Bir raporu çözemediğimizde hata mesajı" + }, + "unableToRetrieveChangelog": "{version} sürümü için değişiklik günlüğü alınamıyor.", + "@unableToRetrieveChangelog": { + "description": "Değişiklik kaydını alamadığımızda gösterilecek hata mesajı." + }, + "unbannedUser": "Yasağı Kaldırılmış Kullanıcı", + "@unbannedUser": { + "description": "Moderatör eylemi olarak bir kullanıcının yasağını kaldırma için kısa açıklama" + }, + "unbannedUserFromCommunity": "Topluluktan Engeli Kaldırılan Kullanıcı", + "@unbannedUserFromCommunity": { + "description": "Bir kullanıcının bir topluluktan yasağının kaldırılması için moderatör eyleminin kısa açıklaması" + }, + "unblockCommunity": "Topluluğun Engelini Kaldır", + "@unblockCommunity": { + "description": "Bir topluluğun engelini kaldırma eylemi" + }, + "unblockInstance": "Örneği Engeli Kaldır", + "@unblockInstance": { + "description": "Bir örneğin kilidini açma araç ipucu" + }, + "understandEnable": "Anladım, Etkinleştir", + "@understandEnable": { + "description": "Bir şeyi kabul etme ve etkinleştirme eylemi" + }, + "unexpectedError": "Beklenmeyen Hata", + "@unexpectedError": {}, + "unfeaturedPost": "Öne Çıkmayan Gönderi", + "@unfeaturedPost": { + "description": "Moderatörün bir gönderinin öne çıkanlar listesinden kaldırılması eylemi için kısa açıklama" + }, + "unhidCommunity": "Unhid Topluluğu", + "@unhidCommunity": { + "description": "Moderatör eylemi için kısa bir açıklama: Bir topluluğun gizliliğini kaldırma" + }, + "unifiedPushDistributorApp": "UnifiedPush Dağıtıcı uygulaması: {app} ({count} mevcut)", + "@unifiedPushDistributorApp": { + "description": "BirleşikPush dağıtıcı uygulamasının mevcut durumunu bildiren bir durum göstergesi" + }, + "unifiedPushNotifications": "BirleşikPush Bildirimleri", + "@unifiedPushNotifications": { + "description": "BirleşikPush Bildirimleri için bildirim türünü tanımlar" + }, + "unifiedPushServer": "BirleşikPush Sunucusu: {server}", + "@unifiedPushServer": { + "description": "Kullanıcının hangi UnifiedPush sunucusunun kullanıldığını belirten hata ayıklama metni" + }, + "unifiedpush": "BirleşikPush", + "@unifiedpush": { + "description": "UnifiedPush bildirim türü (belki çevrilmemeli)" + }, + "unlockPost": "Gönderiyi Kilidini Aç", + "@unlockPost": { + "description": "Bir gönderinin kilidini açma eylemi (moderatör eylemi)" + }, + "unlockedPost": "Kilit Açılmış Gönderi", + "@unlockedPost": { + "description": "Moderatör eyleminin bir gönderiyi kilidini açma için kısa açıklaması" + }, + "unpinFromCommunity": "Topluluktan Kaldır", + "@unpinFromCommunity": { + "description": "Bir topluluktan bir gönderiyi kaldırma ayarı (moderatör eylemi)" + }, + "unreachable": "Ulaşılamaz", + "@unreachable": { + "description": "Şu anda ulaşılamayan bir durumu tanımlar." + }, + "unresolved": "Çözülmemiş", + "@unresolved": { + "description": "Çözülmemiş durum için açıklama (örneğin, çözülmemiş raporlar)" + }, + "unsubscribe": "Abonelikten çık", + "@unsubscribe": {}, + "unsubscribeFromCommunity": "Topluluktan Aboneliği İptal Et", + "@unsubscribeFromCommunity": { + "description": "Bir topluluktan aboneliği iptal etme işlemi" + }, + "unsubscribePending": "Abonelikten çık (abonelik bekleniyor)", + "@unsubscribePending": {}, + "unsubscribed": "Abonelikten çıktı", + "@unsubscribed": {}, + "updateReleased": "Güncelleme yayınlandı: {version}", + "@updateReleased": {}, + "uploadImage": "Görsel yükle", + "@uploadImage": {}, + "upvote": "Oy ver", + "@upvote": { + "description": "Bir gönderi/yorumu beğenme eylemi" + }, + "upvoteColor": "Oy Renk", + "@upvoteColor": { + "description": "Yukarı oy rengi ayarı adı" + }, + "upvoted": "Oy verildi", + "@upvoted": { + "description": "Beğenilen gönderi/yorum için kısa açıklama" + }, + "uriNotSupported": "Bu tür bağlantı şu anda desteklenmiyor.", + "@uriNotSupported": { + "description": "Şu anda desteklenmeyen bağlantı türü." + }, + "url": "URL", + "@url": {}, + "useAdvancedShareSheet": "Gelişmiş Paylaşım Sayfasını Kullanın", + "@useAdvancedShareSheet": { + "description": "Gelişmiş paylaşım sayfasını kullanmak için geçiş yapın." + }, + "useApplePushNotifications": "APNs Bildirimlerini Kullanın", + "@useApplePushNotifications": { + "description": "APNs kullanmak için geçiş yapın." + }, + "useApplePushNotificationsDescription": "Apple'ın Push Bildirim hizmetini kullanır", + "@useApplePushNotificationsDescription": { + "description": "APNs kullanımı için ayarların alt başlığı" + }, + "useCompactView": "Küçük gönderiler için etkinleştir, büyükler için devre dışı bırak.", + "@useCompactView": { + "description": "Küçük gönderiler için sıkıştırılmış görünümü etkinleştirme veya devre dışı bırakma seçeneği." + }, + "useLocalNotifications": "Yerel Bildirimleri Kullanın (Deneysel)", + "@useLocalNotifications": { + "description": "Yerel bildirimleri kullanmak için geçiş yapın." + }, + "useLocalNotificationsDescription": "Arka planda bildirimler için periyodik olarak kontrol eder", + "@useLocalNotificationsDescription": { + "description": "Yerel bildirimleri kullanma ayarı için alt başlık" + }, + "useMaterialYouTheme": "Material You Tema Kullanın", + "@useMaterialYouTheme": { + "description": "Material You temasını kullanmak için geçiş yapın." + }, + "useMaterialYouThemeDescription": "Seçili özel temayı geçersiz kılar", + "@useMaterialYouThemeDescription": { + "description": "Material You temasını kullanma ayarının alt başlığı" + }, + "useProfilePictureForDrawer": "Çekmece için Profil Resmi Kullanın", + "@useProfilePictureForDrawer": { + "description": "Profil resmini çekmece için kullanmak üzere ayar adı" + }, + "useProfilePictureForDrawerSubtitle": "Giriş yapıldığında, çekmece simgesinin yerine kullanıcının profil resmini gösterir.", + "@useProfilePictureForDrawerSubtitle": { + "description": "Profil resmi için çekmece kullanma alt başlığını ayarlama" + }, + "useSuggestedTitle": "Önerilen başlığı kullanın: {title}", + "@useSuggestedTitle": {}, + "useUnifiedPushNotifications": "UnifiedPush Bildirimlerini Kullanın", + "@useUnifiedPushNotifications": { + "description": "UnifiedPush Bildirimlerini kullanmak için geçiş yapın" + }, + "useUnifiedPushNotificationsDescription": "Uyumlu bir uygulama gerektirir", + "@useUnifiedPushNotificationsDescription": { + "description": "BirleşikPush Bildirimlerini kullanma ayarının alt başlığı" + }, + "user": "Kullanıcı", + "@user": { + "description": "Kullanıcı için rol adı" + }, + "userActions": "Kullanıcı Eylemleri", + "@userActions": { + "description": "Kullanıcı eylemleri sayfasına gidiyor" + }, + "userEntry": "Kullanıcı '{username}'", + "@userEntry": { + "description": "Kullanıcının daha fazla eylem gerçekleştirmesi için liste girişi" + }, + "userFormat": "Kullanıcı Formatı", + "@userFormat": { + "description": "Kullanıcı tam adı formatı ayarları" + }, + "userLabelHint": "Bu benim favori kullanıcım", + "@userLabelHint": { + "description": "Yeni bir kullanıcı etiketi eklemek için ipucu metni" + }, + "userNameColor": "Kullanıcı Adı Rengi", + "@userNameColor": { + "description": "Kullanıcı adı rengi için ayarlar" + }, + "userNameThickness": "Kullanıcı Adı Kalınlığı", + "@userNameThickness": { + "description": "Kullanıcı adı kalınlığı ayarı" + }, + "userNotLoggedIn": "Kullanıcı giriş yapmadı", + "@userNotLoggedIn": {}, + "userProfiles": "Kullanıcı Profilleri", + "@userProfiles": { + "description": "Kullanıcı profilleri ile ilgili ayarlar." + }, + "userSettingDescription": "Bu ayarlar Lemmy hesabınızla senkronize olur ve yalnızca hesap bazında uygulanır.", + "@userSettingDescription": { + "description": "Ayarların geçerli kullanıcıya global olarak uygulandığını açıklayan açıklama." + }, + "userStyle": "Kullanıcı Stili", + "@userStyle": { + "description": "Kullanıcı stil ayarları için başlık" + }, + "username": "Kullanıcı adı", + "@username": {}, + "usernameFormattingRedirect": "Kullanıcı adı biçimlendirme mi arıyorsunuz?", + "@usernameFormattingRedirect": { + "description": "Kullanıcıyı yeni biçimlendirme seçeneklerine yönlendirmek için başlık ayarlama" + }, + "users": "Kullanıcılar", + "@users": {}, + "versionNumber": "Sürüm {version}", + "@versionNumber": { + "description": "Mevcut sürümü tanımlayan dize" + }, + "video": "Video", + "@video": { + "description": "Video için dizi " + }, + "videoAutoFullscreen": "Otomatik Tam Ekran", + "@videoAutoFullscreen": { + "decription": "Video oynatıcıları her zaman yatay modda başlatma seçeneği" + }, + "videoAutoLoop": "Video Döngüsü", + "@videoAutoLoop": { + "description": "Bir videoyu sürekli döngüde oynatma seçeneği" + }, + "videoAutoMute": "Sessiz Videolar", + "@videoAutoMute": { + "description": "Videoları her zaman sessize alma seçeneği" + }, + "videoAutoPlay": "Video Otomatik Oynatma", + "@videoAutoPlay": { + "description": "Videoları her zaman otomatik oynatma seçeneği" + }, + "videoDefaultPlaybackSpeed": "Varsayılan Oynatma Hızı", + "@videoDefaultPlaybackSpeed": { + "description": "Varsayılan video oynatma hızını seçme seçeneği" + }, + "viewAll": "Hepsini gör", + "@viewAll": { + "description": "Bir şeyin tümünü görüntülemek için bir başlık (örneğin, örnekler)" + }, + "viewAllComments": "Tüm yorumları gör", + "@viewAllComments": {}, + "viewCommentSource": "Yorum Kaynağını Görüntüle", + "@viewCommentSource": { + "description": "Bir yorumun kaynağını görüntüleme menü öğesi" + }, + "viewOriginal": "Orijinali görüntüle", + "@viewOriginal": { + "description": "Orijinal metni görüntüleme eylemi (ham markdown'ın aksine)" + }, + "viewPostAsDifferentAccount": "Farklı bir hesap olarak gönderiyi görüntüle", + "@viewPostAsDifferentAccount": { + "description": "Farklı bir hesapla bir gönderiyi görüntüleme eylemi" + }, + "viewPostSource": "Gönderi kaynağını görüntüle", + "@viewPostSource": { + "description": "Bir gönderinin kaynağını görüntüleme menü öğesi" + }, + "viewSource": "Kaynak kodunu görüntüle", + "@viewSource": { + "description": "Kaynak görüntüleme eylemi (ham işaretleme)" + }, + "viewingAll": "Hepsini görüntüle", + "@viewingAll": { + "description": "Tüm olası arama sonuçlarını görüntülemek için çip" + }, + "visibility": "Görünürlük: {visibility}", + "@visibility": { + "description": "Topluluk için görünürlük başlığı" + }, + "visitCommunity": "Topluluğu Ziyaret Et", + "@visitCommunity": {}, + "visitInstance": "Ziyaret Etme Örneği", + "@visitInstance": {}, + "visitUserProfile": "Kullanıcı Profilini Ziyaret Et", + "@visitUserProfile": {}, + "warning": "Uyarı", + "@warning": { + "description": "Uyarı diyalogları için başlık" + }, + "xDownvotes": "{x} aşağı oy", + "@xDownvotes": {}, + "xScore": "{x} puan", + "@xScore": { + "description": "Gönderinin veya yorumun toplam puanı" + }, + "xUpvotes": "{x} beğeni", + "@xUpvotes": {}, + "xYearsOld": "{count, plural, zero {{x} yaşında} one {{x} yaşında} other {{x} yaşında}}", + "@xYearsOld": { + "description": "Bir hesabın ne kadar eski olduğunu temsil eden bir hesap tanımlayıcısı" + }, + "yes": "Evet", + "@yes": { + "description": "Pozitif durum" + } +} diff --git a/lib/notification/utils/navigate_notification.dart b/lib/notification/utils/navigate_notification.dart index 25d6155e3..a386a8508 100644 --- a/lib/notification/utils/navigate_notification.dart +++ b/lib/notification/utils/navigate_notification.dart @@ -15,6 +15,7 @@ import 'package:thunder/core/auth/bloc/auth_bloc.dart'; import 'package:thunder/core/auth/helpers/fetch_account.dart'; import 'package:thunder/core/singletons/lemmy_client.dart'; import 'package:thunder/inbox/bloc/inbox_bloc.dart'; +import 'package:thunder/inbox/enums/inbox_type.dart'; import 'package:thunder/shared/pages/loading_page.dart'; import 'package:thunder/thunder/bloc/thunder_bloc.dart'; import 'package:thunder/thunder/pages/notifications_pages.dart'; @@ -90,8 +91,6 @@ void navigateToNotificationReplyPage(BuildContext context, {required int? replyI ); pushOnTopOfLoadingPage(context, route).then((_) { - context.read().add(const GetInboxEvent(reset: true)); - // If needed, switch back to the original account or anonymous instance if (switchedAccount) { if (originalAccount != null) { @@ -104,6 +103,8 @@ void navigateToNotificationReplyPage(BuildContext context, {required int? replyI context.read().add(InstanceChanged(instance: originalAnonymousInstance)); } } + + context.read().add(const GetInboxEvent(reset: true, inboxType: InboxType.all)); }); } } diff --git a/lib/post/cubit/create_post_cubit.dart b/lib/post/cubit/create_post_cubit.dart index 3f407f267..919dc7392 100644 --- a/lib/post/cubit/create_post_cubit.dart +++ b/lib/post/cubit/create_post_cubit.dart @@ -19,19 +19,27 @@ class CreatePostCubit extends Cubit { emit(state.copyWith(status: CreatePostStatus.initial, message: null)); } - Future uploadImage(String imageFile, {bool isPostImage = false}) async { + Future uploadImages(List imageFiles, {bool isPostImage = false}) async { Account? account = await fetchActiveProfileAccount(); if (account == null) return; PictrsApi pictrs = PictrsApi(account.instance!); + List urls = []; isPostImage ? emit(state.copyWith(status: CreatePostStatus.postImageUploadInProgress)) : emit(state.copyWith(status: CreatePostStatus.imageUploadInProgress)); try { - PictrsUpload result = await pictrs.upload(filePath: imageFile, auth: account.jwt); - String url = "https://${account.instance!}/pictrs/image/${result.files[0].file}"; + for (String imageFile in imageFiles) { + PictrsUpload result = await pictrs.upload(filePath: imageFile, auth: account.jwt); + String url = "https://${account.instance!}/pictrs/image/${result.files[0].file}"; - isPostImage ? emit(state.copyWith(status: CreatePostStatus.postImageUploadSuccess, imageUrl: url)) : emit(state.copyWith(status: CreatePostStatus.imageUploadSuccess, imageUrl: url)); + urls.add(url); + + // Add a delay between each upload to avoid possible rate limiting + await Future.wait(urls.map((url) => Future.delayed(const Duration(milliseconds: 500)))); + } + + isPostImage ? emit(state.copyWith(status: CreatePostStatus.postImageUploadSuccess, imageUrls: urls)) : emit(state.copyWith(status: CreatePostStatus.imageUploadSuccess, imageUrls: urls)); } catch (e) { isPostImage ? emit(state.copyWith(status: CreatePostStatus.postImageUploadFailure, message: e.toString())) diff --git a/lib/post/cubit/create_post_state.dart b/lib/post/cubit/create_post_state.dart index 8efb8e6e7..bb0699ab4 100644 --- a/lib/post/cubit/create_post_state.dart +++ b/lib/post/cubit/create_post_state.dart @@ -19,7 +19,7 @@ class CreatePostState extends Equatable { const CreatePostState({ this.status = CreatePostStatus.initial, this.postViewMedia, - this.imageUrl, + this.imageUrls, this.message, }); @@ -29,8 +29,8 @@ class CreatePostState extends Equatable { /// The result of the created or edited post final PostViewMedia? postViewMedia; - /// The url of the uploaded image - final String? imageUrl; + /// The urls of the uploaded images + final List? imageUrls; /// The info or error message to be displayed as a snackbar final String? message; @@ -38,17 +38,17 @@ class CreatePostState extends Equatable { CreatePostState copyWith({ required CreatePostStatus status, PostViewMedia? postViewMedia, - String? imageUrl, + List? imageUrls, String? message, }) { return CreatePostState( status: status, postViewMedia: postViewMedia ?? this.postViewMedia, - imageUrl: imageUrl ?? this.imageUrl, + imageUrls: imageUrls ?? this.imageUrls, message: message ?? this.message, ); } @override - List get props => [status, postViewMedia, imageUrl, message]; + List get props => [status, postViewMedia, imageUrls, message]; } diff --git a/lib/post/utils/comment_action_helpers.dart b/lib/post/utils/comment_action_helpers.dart index de33bc23f..5bf85bdd9 100644 --- a/lib/post/utils/comment_action_helpers.dart +++ b/lib/post/utils/comment_action_helpers.dart @@ -16,8 +16,8 @@ import 'package:thunder/feed/view/feed_page.dart'; import 'package:thunder/instance/bloc/instance_bloc.dart'; import 'package:thunder/instance/enums/instance_action.dart'; import 'package:thunder/post/bloc/post_bloc.dart'; +import 'package:thunder/post/utils/user_label_utils.dart'; import 'package:thunder/post/widgets/report_comment_dialog.dart'; -import 'package:thunder/shared/dialogs.dart'; import 'package:thunder/shared/multi_picker_item.dart'; import 'package:thunder/shared/picker_item.dart'; import 'package:thunder/shared/snackbar.dart'; @@ -562,56 +562,7 @@ class _CommentActionPickerState extends State { break; case CommentCardAction.userLabel: action = () async { - // Load up any existing label - final String username = UserLabel.usernameFromParts(widget.commentView.creator.name, widget.commentView.creator.actorId); - final UserLabel? existingLabel = await UserLabel.fetchUserLabel(username); - - if (!context.mounted) return; - - final TextEditingController controller = TextEditingController(text: existingLabel?.label); - - await showThunderDialog( - // We're checking context.mounted above, so ignore this warning - // ignore: use_build_context_synchronously - context: context, - title: l10n.addUserLabel, - contentWidgetBuilder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - textInputAction: TextInputAction.done, - keyboardType: TextInputType.text, - controller: controller, - decoration: InputDecoration( - isDense: true, - border: const OutlineInputBorder(), - labelText: l10n.label, - hintText: l10n.userLabelHint, - ), - autofocus: true, - ), - ], - ); - }, - tertiaryButtonText: existingLabel != null ? l10n.delete : null, - onTertiaryButtonPressed: (dialogContext) async { - Navigator.of(dialogContext).pop(); - await UserLabel.deleteUserLabel(username); - }, - secondaryButtonText: l10n.cancel, - onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(), - primaryButtonText: l10n.save, - onPrimaryButtonPressed: (dialogContext, _) async { - Navigator.of(dialogContext).pop(); - - if (controller.text.isNotEmpty) { - await UserLabel.upsertUserLabel(UserLabel(id: '', username: username, label: controller.text)); - } else { - await UserLabel.deleteUserLabel(username); - } - }, - ); + await showUserLabelEditorDialog(context, UserLabel.usernameFromParts(widget.commentView.creator.name, widget.commentView.creator.actorId)); }; break; case CommentCardAction.instanceActions: diff --git a/lib/post/utils/user_label_utils.dart b/lib/post/utils/user_label_utils.dart new file mode 100644 index 000000000..83f1d118b --- /dev/null +++ b/lib/post/utils/user_label_utils.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:thunder/account/models/user_label.dart'; +import 'package:thunder/shared/dialogs.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +/// Shows a dialog which allows the user to create/modify/edit a label for the given [username]. +/// Tip: Call `UserLabel.usernameFromParts` to generate a [username] in the right format. +/// If an existing user label was found (regardless of whether it was changed or deleted) or a new user label was created, +/// it will be returned in the record. +/// If a user label was found and deleted, the deleted flag will be set in the record. +Future<({UserLabel? userLabel, bool deleted})> showUserLabelEditorDialog(BuildContext context, String username) async { + final l10n = AppLocalizations.of(context)!; + + // Load up any existing label + UserLabel? existingLabel = await UserLabel.fetchUserLabel(username); + bool deleted = false; + + if (!context.mounted) return (userLabel: existingLabel, deleted: false); + + final TextEditingController controller = TextEditingController(text: existingLabel?.label); + + await showThunderDialog( + // We're checking context.mounted above, so ignore this warning + // ignore: use_build_context_synchronously + context: context, + title: l10n.addUserLabel, + contentWidgetBuilder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + controller: controller, + decoration: InputDecoration( + isDense: true, + border: const OutlineInputBorder(), + labelText: l10n.label, + hintText: l10n.userLabelHint, + ), + autofocus: true, + ), + ], + ); + }, + tertiaryButtonText: existingLabel != null ? l10n.delete : null, + onTertiaryButtonPressed: (dialogContext) async { + await UserLabel.deleteUserLabel(username); + deleted = true; + + if (dialogContext.mounted) { + Navigator.of(dialogContext).pop(); + } + }, + secondaryButtonText: l10n.cancel, + onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(), + primaryButtonText: l10n.save, + onPrimaryButtonPressed: (dialogContext, _) async { + if (controller.text.isNotEmpty) { + existingLabel = await UserLabel.upsertUserLabel(UserLabel(id: '', username: username, label: controller.text)); + } else { + await UserLabel.deleteUserLabel(username); + deleted = true; + } + + if (dialogContext.mounted) { + Navigator.of(dialogContext).pop(); + } + }, + ); + + return (userLabel: existingLabel, deleted: deleted); +} diff --git a/lib/post/widgets/post_view.dart b/lib/post/widgets/post_view.dart index c4fa6409c..e4ca8cb51 100644 --- a/lib/post/widgets/post_view.dart +++ b/lib/post/widgets/post_view.dart @@ -55,6 +55,7 @@ class PostSubview extends StatefulWidget { final bool showExpandableButton; final bool selectable; final bool showReplyEditorButtons; + final void Function(String? selection)? onSelectionChanged; const PostSubview({ super.key, @@ -67,6 +68,7 @@ class PostSubview extends StatefulWidget { this.showExpandableButton = true, this.selectable = false, this.showReplyEditorButtons = false, + this.onSelectionChanged, }); @override @@ -200,6 +202,7 @@ class _PostSubviewState extends State with SingleTickerProviderStat anchors: selectableRegionState.contextMenuAnchors, ); }, + onSelectionChanged: (value) => widget.onSelectionChanged?.call(value?.plainText), child: child, ); }, diff --git a/lib/routes.dart b/lib/routes.dart index 8e62c24a7..882c41c8b 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -17,6 +17,7 @@ import 'package:thunder/settings/pages/general_settings_page.dart'; import 'package:thunder/settings/pages/gesture_settings_page.dart'; import 'package:thunder/settings/pages/post_appearance_settings_page.dart'; import 'package:thunder/settings/pages/theme_settings_page.dart'; +import 'package:thunder/settings/pages/user_labels_settings_page.dart'; import 'package:thunder/settings/pages/video_player_settings.dart'; import 'package:thunder/settings/settings.dart'; import 'package:thunder/thunder/thunder.dart'; @@ -155,6 +156,16 @@ final GoRouter router = GoRouter( ); }, ), + GoRoute( + name: 'user_labels', + path: 'user_labels', + builder: (context, state) { + return BlocProvider.value( + value: (state.extra! as List)[0] as ThunderBloc, + child: UserLabelSettingsPage(settingToHighlight: (state.extra! as List).elementAtOrNull(1) as LocalSettings?), + ); + }, + ), GoRoute( name: 'about', path: 'about', diff --git a/lib/settings/pages/settings_page.dart b/lib/settings/pages/settings_page.dart index 5945836dc..6eff262fb 100644 --- a/lib/settings/pages/settings_page.dart +++ b/lib/settings/pages/settings_page.dart @@ -44,6 +44,7 @@ class _SettingsPageState extends State { SettingTopic(title: l10n.floatingActionButton, icon: Icons.settings_applications_rounded, path: SETTINGS_FAB_PAGE), SettingTopic(title: l10n.accessibility, icon: Icons.accessibility, path: SETTINGS_ACCESSIBILITY_PAGE), SettingTopic(title: l10n.account(0), icon: Icons.person_rounded, path: SETTINGS_ACCOUNT_PAGE), + SettingTopic(title: l10n.userLabels, icon: Icons.label_rounded, path: SETTINGS_USER_LABELS_PAGE), SettingTopic(title: l10n.about, icon: Icons.info_rounded, path: SETTINGS_ABOUT_PAGE), SettingTopic(title: l10n.debug, icon: Icons.developer_mode_rounded, path: SETTINGS_DEBUG_PAGE), ]; @@ -89,7 +90,7 @@ class _SettingsPageState extends State { localSettings.length, (index) => ListTile( subtitle: Text( - "${l10n.getLocalSettingLocalization(localSettings[index].category!.toString())} > ${l10n.getLocalSettingLocalization(localSettings[index].subCategory.toString())}"), + "${l10n.getLocalSettingLocalization(localSettings[index].category!.toString())}${localSettings[index].subCategory == null ? '' : ' > ${l10n.getLocalSettingLocalization(localSettings[index].subCategory.toString())}'}"), onTap: () { navigateToSetting(context, localSettings[index]); controller.closeView(null); diff --git a/lib/settings/pages/theme_settings_page.dart b/lib/settings/pages/theme_settings_page.dart index 3f45759ec..0b667674a 100644 --- a/lib/settings/pages/theme_settings_page.dart +++ b/lib/settings/pages/theme_settings_page.dart @@ -507,6 +507,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: userFullNameInstanceNameThickness, instanceNameColor: userFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForUsers, ), icon: Icons.person_rounded, payload: userSeparator, @@ -523,6 +524,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: userFullNameInstanceNameThickness, instanceNameColor: userFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForUsers, ), payload: FullNameSeparator.dot, capitalizeLabel: false, @@ -537,6 +539,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: userFullNameInstanceNameThickness, instanceNameColor: userFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForUsers, ), payload: FullNameSeparator.at, capitalizeLabel: false, @@ -551,6 +554,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: userFullNameInstanceNameThickness, instanceNameColor: userFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForUsers, ), payload: FullNameSeparator.lemmy, capitalizeLabel: false, @@ -581,6 +585,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: userFullNameInstanceNameThickness, instanceNameColor: userFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForUsers, ), items: [ ListPickerItem( @@ -729,6 +734,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: communityFullNameInstanceNameThickness, instanceNameColor: communityFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForCommunities, ), icon: Icons.people_rounded, payload: communitySeparator, @@ -745,6 +751,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: communityFullNameInstanceNameThickness, instanceNameColor: communityFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForCommunities, ), payload: FullNameSeparator.dot, capitalizeLabel: false, @@ -759,6 +766,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: communityFullNameInstanceNameThickness, instanceNameColor: communityFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForCommunities, ), payload: FullNameSeparator.at, capitalizeLabel: false, @@ -773,6 +781,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: communityFullNameInstanceNameThickness, instanceNameColor: communityFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForCommunities, ), payload: FullNameSeparator.lemmy, capitalizeLabel: false, @@ -803,6 +812,7 @@ class _ThemeSettingsPageState extends State { instanceNameThickness: communityFullNameInstanceNameThickness, instanceNameColor: communityFullNameInstanceNameColor, textStyle: theme.textTheme.bodyMedium, + useDisplayName: useDisplayNamesForCommunities, ), items: [ ListPickerItem( diff --git a/lib/settings/pages/user_labels_settings_page.dart b/lib/settings/pages/user_labels_settings_page.dart new file mode 100644 index 000000000..b6247f024 --- /dev/null +++ b/lib/settings/pages/user_labels_settings_page.dart @@ -0,0 +1,203 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smooth_highlight/smooth_highlight.dart'; +import 'package:thunder/account/models/user_label.dart'; + +import 'package:thunder/core/enums/local_settings.dart'; +import 'package:thunder/post/utils/user_label_utils.dart'; +import 'package:thunder/shared/dialogs.dart'; +import 'package:thunder/shared/full_name_widgets.dart'; +import 'package:thunder/shared/input_dialogs.dart'; + +class UserLabelSettingsPage extends StatefulWidget { + final LocalSettings? settingToHighlight; + + const UserLabelSettingsPage({super.key, this.settingToHighlight}); + + @override + State createState() => _UserLabelSettingsPageState(); +} + +class _UserLabelSettingsPageState extends State with SingleTickerProviderStateMixin { + GlobalKey settingToHighlightKey = GlobalKey(); + LocalSettings? settingToHighlight; + + List userLabels = []; + + void _updateChangedUserLabel(({UserLabel? userLabel, bool deleted}) result) { + if (result.userLabel == null) return; + + UserLabel? existingLabel = userLabels.firstWhereOrNull((userLabel) => userLabel.username == result.userLabel!.username); + if (existingLabel == null && !result.deleted) { + // It doesn't exist in our list yet, add it! + setState(() => userLabels.add(result.userLabel!)); + } else if (existingLabel != null) { + if (result.deleted) { + // It exists in our list and was deleted, so remove it. + setState(() => userLabels.removeWhere((userLabel) => userLabel.username == result.userLabel!.username)); + } else { + // It exists in our list but was changed, so update it. + setState(() => userLabels[userLabels.indexOf(existingLabel)] = result.userLabel!); + } + } + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + if (widget.settingToHighlight != null) { + setState(() => settingToHighlight = widget.settingToHighlight); + + // Need some delay to finish building, even though we're in a post-frame callback. + Timer(const Duration(milliseconds: 500), () { + if (settingToHighlightKey.currentContext != null) { + // Ensure that the selected setting is visible on the screen + Scrollable.ensureVisible( + settingToHighlightKey.currentContext!, + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOut, + ); + } + + // Give time for the highlighting to appear, then turn it off + Timer(const Duration(seconds: 1), () { + setState(() => settingToHighlight = null); + }); + }); + } + + // Load the user labels + userLabels = await UserLabel.fetchAllUserLabels(); + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final AppLocalizations l10n = AppLocalizations.of(context)!; + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () async { + // First, show the user dialog so we can pick who to label + showUserInputDialog( + context, + title: l10n.username, + onUserSelected: (personView) async { + // Then show the label editor + ({UserLabel? userLabel, bool deleted}) result = await showUserLabelEditorDialog(context, UserLabel.usernameFromParts(personView.person.name, personView.person.actorId)); + _updateChangedUserLabel(result); + }, + ); + }, + child: const Icon(Icons.add_rounded), + ), + body: CustomScrollView( + slivers: [ + SliverAppBar( + title: Text(l10n.userLabels), + centerTitle: false, + toolbarHeight: 70.0, + pinned: true, + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), + child: Text( + l10n.userLabelsSettingsPageDescription, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.textTheme.bodyMedium?.color?.withOpacity(0.8), + ), + ), + ), + ), + SliverToBoxAdapter( + child: SmoothHighlight( + key: settingToHighlight == LocalSettings.userLabels ? settingToHighlightKey : null, + useInitialHighLight: settingToHighlight == LocalSettings.userLabels, + enabled: settingToHighlight == LocalSettings.userLabels, + color: theme.colorScheme.primaryContainer, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(l10n.userLabels, style: theme.textTheme.titleMedium), + ], + ), + ), + ), + ), + SliverToBoxAdapter( + child: Align( + alignment: Alignment.centerLeft, + child: userLabels.isEmpty + ? Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0), + child: Text( + l10n.noUserLabels, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.textTheme.bodyMedium?.color?.withOpacity(0.8), + ), + ), + ) + : ListView.builder( + padding: const EdgeInsets.only(bottom: 20), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: userLabels.length, + itemBuilder: (context, index) { + return ListTile( + contentPadding: const EdgeInsetsDirectional.only(start: 16.0, end: 12.0), + title: UserFullNameWidget( + context, + UserLabel.partsFromUsername(userLabels[index].username).username, + null, + UserLabel.partsFromUsername(userLabels[index].username).instance, + textStyle: theme.textTheme.bodyLarge, + ), + subtitle: Text(userLabels[index].label), + trailing: IconButton( + icon: Icon(Icons.clear, semanticLabel: l10n.remove), + onPressed: () async { + bool result = false; + + await showThunderDialog( + context: context, + title: l10n.confirm, + contentText: l10n.deleteUserLabelConfirmation, + onSecondaryButtonPressed: (dialogContext) => Navigator.of(dialogContext).pop(), + secondaryButtonText: l10n.cancel, + onPrimaryButtonPressed: (dialogContext, _) async { + Navigator.of(dialogContext).pop(); + result = true; + }, + primaryButtonText: l10n.delete, + ); + + if (result) { + UserLabel.deleteUserLabel(userLabels[index].username); + _updateChangedUserLabel((userLabel: userLabels[index], deleted: true)); + } + }, + ), + onTap: () async { + ({bool deleted, UserLabel? userLabel}) result = await showUserLabelEditorDialog(context, userLabels[index].username); + _updateChangedUserLabel(result); + }, + ); + }, + ), + ), + ), + const SliverToBoxAdapter(child: SizedBox(height: 128.0)), + ], + ), + ); + } +} diff --git a/lib/shared/comment_content.dart b/lib/shared/comment_content.dart index a09bf8407..ac5642eee 100644 --- a/lib/shared/comment_content.dart +++ b/lib/shared/comment_content.dart @@ -35,6 +35,7 @@ class CommentContent extends StatefulWidget { final void Function() onViewSourceToggled; final bool selectable; final bool showReplyEditorButtons; + final void Function(String? selection)? onSelectionChanged; const CommentContent({ super.key, @@ -54,6 +55,7 @@ class CommentContent extends StatefulWidget { required this.onViewSourceToggled, this.selectable = false, this.showReplyEditorButtons = false, + this.onSelectionChanged, }); @override @@ -130,6 +132,7 @@ class _CommentContentState extends State with SingleTickerProvid anchors: selectableRegionState.contextMenuAnchors, ); }, + onSelectionChanged: (value) => widget.onSelectionChanged?.call(value?.plainText), child: child, ); }, diff --git a/lib/shared/cross_posts.dart b/lib/shared/cross_posts.dart index 91ac02552..738903ada 100644 --- a/lib/shared/cross_posts.dart +++ b/lib/shared/cross_posts.dart @@ -8,6 +8,7 @@ import 'package:thunder/core/models/post_view_media.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:thunder/post/utils/post.dart'; +import 'package:thunder/shared/full_name_widgets.dart'; import 'package:thunder/utils/instance.dart'; import 'package:thunder/post/utils/navigate_create_post.dart'; import 'package:thunder/post/utils/navigate_post.dart'; @@ -95,14 +96,12 @@ class _CrossPostsState extends State with SingleTickerProviderStateM ' to ', style: crossPostTextStyle, ), - Text( - generateCommunityFullName( - context, - widget.crossPosts[index].community.name, - widget.crossPosts[index].community.title, - fetchInstanceNameFromUrl(widget.crossPosts[index].community.actorId), - ), - style: crossPostLinkTextStyle, + CommunityFullNameWidget( + context, + widget.crossPosts[index].community.name, + widget.crossPosts[index].community.title, + fetchInstanceNameFromUrl(widget.crossPosts[index].community.actorId), + textStyle: crossPostLinkTextStyle, ), const Spacer(), CrossPostMetaData(crossPost: widget.crossPosts[index]), @@ -140,19 +139,18 @@ class _CrossPostsState extends State with SingleTickerProviderStateM : '${l10n.crossPostedTo} ', style: theme.textTheme.bodySmall?.copyWith(color: theme.textTheme.bodyMedium?.color?.withOpacity(0.5)), ), + if (!_areCrossPostsExpanded) + WidgetSpan( + child: CommunityFullNameWidget( + context, + widget.crossPosts[0].community.name, + widget.crossPosts[0].community.title, + fetchInstanceNameFromUrl(widget.crossPosts[0].community.actorId), + textStyle: theme.textTheme.bodySmall?.copyWith(color: crossPostLinkTextStyle?.color), + ), + ), TextSpan( - text: _areCrossPostsExpanded - ? '' - : '${generateCommunityFullName( - context, - widget.crossPosts[0].community.name, - widget.crossPosts[0].community.title, - fetchInstanceNameFromUrl(widget.crossPosts[0].community.actorId), - )} ', - style: crossPostLinkTextStyle?.copyWith(fontSize: 12), - ), - TextSpan( - text: _areCrossPostsExpanded || widget.crossPosts.length == 1 ? '' : l10n.andXMore(widget.crossPosts.length - 1), + text: _areCrossPostsExpanded || widget.crossPosts.length == 1 ? '' : ' ${l10n.andXMore(widget.crossPosts.length - 1)}', style: theme.textTheme.bodySmall?.copyWith(color: theme.textTheme.bodyMedium?.color?.withOpacity(0.5)), ), ], diff --git a/lib/shared/full_name_widgets.dart b/lib/shared/full_name_widgets.dart index f16620e0e..17159001f 100644 --- a/lib/shared/full_name_widgets.dart +++ b/lib/shared/full_name_widgets.dart @@ -27,20 +27,24 @@ class UserFullNameWidget extends StatelessWidget { final Color? Function(Color?)? transformColor; final bool? useDisplayName; - const UserFullNameWidget(this.outerContext, this.name, this.displayName, this.instance, - {super.key, - this.userSeparator, - this.userNameThickness, - this.userNameColor, - this.instanceNameThickness, - this.instanceNameColor, - this.textStyle, - this.includeInstance = true, - this.fontScale, - this.autoSize = false, - this.transformColor, - this.useDisplayName}) - : assert(outerContext != null || (userSeparator != null && userNameThickness != null && userNameColor != null && instanceNameThickness != null && instanceNameColor != null)), + const UserFullNameWidget( + this.outerContext, + this.name, + this.displayName, + this.instance, { + super.key, + this.userSeparator, + this.userNameThickness, + this.userNameColor, + this.instanceNameThickness, + this.instanceNameColor, + this.textStyle, + this.includeInstance = true, + this.fontScale, + this.autoSize = false, + this.transformColor, + this.useDisplayName, + }) : assert(outerContext != null || (userSeparator != null && userNameThickness != null && userNameColor != null && instanceNameThickness != null && instanceNameColor != null)), assert(outerContext != null || textStyle != null); @override @@ -133,7 +137,8 @@ class CommunityFullNameWidget extends StatelessWidget { this.autoSize = false, this.transformColor, this.useDisplayName, - }) : assert(outerContext != null || (communitySeparator != null && communityNameThickness != null && communityNameColor != null && instanceNameThickness != null && instanceNameColor != null)), + }) : assert(outerContext != null || + (communitySeparator != null && communityNameThickness != null && communityNameColor != null && instanceNameThickness != null && instanceNameColor != null && useDisplayName != null)), assert(outerContext != null || textStyle != null); @override diff --git a/lib/shared/link_information.dart b/lib/shared/link_information.dart index ec84749d2..a01a9cb32 100644 --- a/lib/shared/link_information.dart +++ b/lib/shared/link_information.dart @@ -54,7 +54,7 @@ class _LinkInformationState extends State { }; return Semantics( - excludeSemantics: true, + link: true, child: InkWell( customBorder: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), onTap: () { diff --git a/lib/thunder/pages/notifications_pages.dart b/lib/thunder/pages/notifications_pages.dart index 24ed4f247..2b72e8d43 100644 --- a/lib/thunder/pages/notifications_pages.dart +++ b/lib/thunder/pages/notifications_pages.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -15,45 +16,40 @@ class NotificationsReplyPage extends StatelessWidget { @override Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final AppLocalizations l10n = AppLocalizations.of(context)!; + final theme = Theme.of(context); + final l10n = AppLocalizations.of(context)!; return MultiBlocProvider( providers: [ BlocProvider.value(value: InboxBloc.withReplies(replies)), BlocProvider.value(value: PostBloc()), ], - child: Container( - color: theme.colorScheme.background, - child: SafeArea( - top: false, - child: CustomScrollView( - slivers: [ - SliverAppBar( - flexibleSpace: const FlexibleSpaceBar(titlePadding: EdgeInsets.zero), - toolbarHeight: 70.0, - pinned: true, - title: ListTile( - title: Text(l10n.inbox, style: theme.textTheme.titleLarge), - subtitle: Text(l10n.reply(replies.length)), - ), - ), - SliverToBoxAdapter( - child: Material( - child: BlocConsumer( - listener: (BuildContext context, InboxState state) { - if (state.replies.isEmpty && (ModalRoute.of(context)?.isCurrent ?? false)) { - Navigator.of(context).pop(); - } - }, - builder: (context, state) => InboxRepliesView( - replies: state.replies, - showAll: false, + child: BlocConsumer( + listener: (BuildContext context, InboxState state) { + if (state.replies.isEmpty && (ModalRoute.of(context)?.isCurrent ?? false)) { + Navigator.of(context).pop(); + } + }, + builder: (context, state) => Material( + child: NestedScrollView( + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: SliverAppBar( + pinned: true, + centerTitle: false, + toolbarHeight: 70.0, + forceElevated: innerBoxIsScrolled, + title: ListTile( + title: Text(l10n.inbox, style: theme.textTheme.titleLarge), + subtitle: Text(l10n.reply(replies.length)), ), ), ), - ), - ], + ]; + }, + body: InboxRepliesView(replies: state.replies), ), ), ), diff --git a/lib/thunder/pages/thunder_page.dart b/lib/thunder/pages/thunder_page.dart index 98ad7fa97..e0a858a78 100644 --- a/lib/thunder/pages/thunder_page.dart +++ b/lib/thunder/pages/thunder_page.dart @@ -166,6 +166,7 @@ class _ThunderState extends State { showSnackbar( AppLocalizations.of(context)!.tapToExit, duration: const Duration(milliseconds: 3500), + closable: false, ); } diff --git a/lib/user/widgets/user_header.dart b/lib/user/widgets/user_header.dart index 77917062c..96d6e79bc 100644 --- a/lib/user/widgets/user_header.dart +++ b/lib/user/widgets/user_header.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:thunder/feed/feed.dart'; import 'package:thunder/shared/avatars/user_avatar.dart'; import 'package:thunder/shared/full_name_widgets.dart'; @@ -30,6 +32,7 @@ class _UserHeaderState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final FeedBloc feedBloc = context.watch(); return Material( elevation: widget.showUserSidebar ? 5.0 : 0, @@ -117,7 +120,7 @@ class _UserHeaderState extends State { useDisplayName: false, ), const SizedBox(height: 8.0), - Row( + Wrap( children: [ IconText( icon: const Icon(Icons.wysiwyg_rounded), @@ -128,6 +131,13 @@ class _UserHeaderState extends State { icon: const Icon(Icons.chat_rounded), text: formatNumberToK(widget.getPersonDetailsResponse.personView.counts.commentCount), ), + if (feedBloc.state.feedType == FeedType.user) ...[ + const SizedBox(width: 8.0), + IconText( + icon: Icon(getSortIcon(feedBloc.state)), + text: getSortName(feedBloc.state), + ), + ], ], ), ], diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 7041fbebb..3f3b12397 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -47,6 +47,7 @@ const String SETTINGS_GESTURES_PAGE = '/settings/gestures'; const String SETTINGS_FAB_PAGE = '/settings/fab'; const String SETTINGS_ACCESSIBILITY_PAGE = '/settings/accessibility'; const String SETTINGS_ACCOUNT_PAGE = '/settings/account'; +const String SETTINGS_USER_LABELS_PAGE = '/settings/user_labels'; const String SETTINGS_ABOUT_PAGE = '/settings/about'; const String SETTINGS_DEBUG_PAGE = '/settings/debug'; const String SETTINGS_APPEARANCE_POSTS_PAGE = '/settings/appearance/posts'; diff --git a/lib/utils/media/image.dart b/lib/utils/media/image.dart index 071bb2592..93d8211d7 100644 --- a/lib/utils/media/image.dart +++ b/lib/utils/media/image.dart @@ -111,11 +111,16 @@ void uploadImage(BuildContext context, ImageBloc imageBloc, {bool postImage = fa } } -Future selectImageToUpload() async { +Future> selectImagesToUpload({bool allowMultiple = false}) async { final ImagePicker picker = ImagePicker(); + if (allowMultiple) { + List? files = await picker.pickMultiImage(); + return files.map((file) => file.path).toList(); + } + XFile? file = await picker.pickImage(source: ImageSource.gallery); - return file!.path; + return [file!.path]; } void showImageViewer(BuildContext context, {String? url, Uint8List? bytes, int? postId, void Function()? navigateToPost}) { diff --git a/lib/utils/settings_utils.dart b/lib/utils/settings_utils.dart index 5a2b4b830..2d08969a9 100644 --- a/lib/utils/settings_utils.dart +++ b/lib/utils/settings_utils.dart @@ -20,6 +20,7 @@ void navigateToSetting(BuildContext context, LocalSettings setting) { LocalSettingsCategories.filters: SETTINGS_FILTERS_PAGE, LocalSettingsCategories.accessibility: SETTINGS_ACCESSIBILITY_PAGE, LocalSettingsCategories.account: SETTINGS_ACCOUNT_PAGE, + LocalSettingsCategories.userLabels: SETTINGS_USER_LABELS_PAGE, LocalSettingsCategories.theming: SETTINGS_APPEARANCE_THEMES_PAGE, LocalSettingsCategories.debug: SETTINGS_DEBUG_PAGE, LocalSettingsCategories.about: SETTINGS_ABOUT_PAGE, diff --git a/pubspec.lock b/pubspec.lock index 57f04f3ea..366a01b24 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1116,8 +1116,8 @@ packages: dependency: "direct main" description: path: "." - ref: "3196471d8e1b26873aecaf04254043cf9817bbe3" - resolved-ref: "3196471d8e1b26873aecaf04254043cf9817bbe3" + ref: "514e3718a42d4398619d66b6ac1ccd53bafd9b3f" + resolved-ref: "514e3718a42d4398619d66b6ac1ccd53bafd9b3f" url: "https://github.com/thunder-app/markdown-editor.git" source: git version: "0.1.0" diff --git a/pubspec.yaml b/pubspec.yaml index 37e8ffd79..0f6a59c2c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: thunder description: An open-source cross-platform Lemmy client for iOS and Android built with Flutter publish_to: "none" -version: 0.5.0-1+58 +version: 0.5.0-2+59 environment: sdk: "^3.0.0" @@ -23,7 +23,7 @@ dependencies: markdown_editor: git: url: https://github.com/thunder-app/markdown-editor.git - ref: 3196471d8e1b26873aecaf04254043cf9817bbe3 + ref: 514e3718a42d4398619d66b6ac1ccd53bafd9b3f lemmy_api_client: git: url: https://github.com/thunder-app/lemmy_api_client.git