Merge branch 'main' into develop
This commit is contained in:
commit
b3ec9ee171
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -19,8 +19,8 @@ jobs:
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: force Xcode 13.0
|
||||
run: sudo xcode-select -switch /Applications/Xcode_13.0.app
|
||||
- name: force Xcode 13.1
|
||||
run: sudo xcode-select -switch /Applications/Xcode_13.1.app
|
||||
- name: setup
|
||||
run: exec ./.github/scripts/setup.sh
|
||||
- name: build
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -122,5 +122,4 @@ xcuserdata
|
||||
|
||||
Localization/StringsConvertor/input
|
||||
Localization/StringsConvertor/output
|
||||
.DS_Store
|
||||
/Mastodon.xcworkspace/xcshareddata/swiftpm
|
||||
.DS_Store
|
@ -17,6 +17,6 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>82</string>
|
||||
<string>88</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -17,6 +17,6 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>82</string>
|
||||
<string>88</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -17,6 +17,6 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>82</string>
|
||||
<string>88</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
"HZSGTr" = "Quel contenu à publier ?";
|
||||
|
||||
"HdGikU" = "Posting failed";
|
||||
"HdGikU" = "Échec lors de la publication";
|
||||
|
||||
"KDNTJ4" = "Raison de l’échec";
|
||||
|
||||
@ -40,11 +40,11 @@
|
||||
|
||||
"gfePDu" = "Échec lors de la publication. ${failureReason}";
|
||||
|
||||
"k7dbKQ" = "Post was sent successfully.";
|
||||
"k7dbKQ" = "Message publié avec succès.";
|
||||
|
||||
"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
|
||||
"oGiqmY-dYQ5NN" = "Juste pour confirmer, vous vouliez « Public » ?";
|
||||
|
||||
"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
|
||||
"oGiqmY-ehFLjY" = "Juste pour confirmer, vous vouliez bien diffuser vers « abonné·e·s uniquement » ?";
|
||||
|
||||
"rM6dvp" = "URL";
|
||||
|
||||
|
@ -24,15 +24,15 @@
|
||||
|
||||
"Zo4jgJ" = "Xuyaniya şandiyê";
|
||||
|
||||
"apSxMG-dYQ5NN" = "Vebijarkên ${count} hene ku li gorî 'Giştî' ne.";
|
||||
"apSxMG-dYQ5NN" = "Vebijarkên ${count} hene ku li gorî 'Gelemperî' ne.";
|
||||
|
||||
"apSxMG-ehFLjY" = "Vebijarkên ${count} hene ku li gorî 'Tenê Şopandin' hene.";
|
||||
"apSxMG-ehFLjY" = "Vebijarkên ${count} hene ku li gorî 'Tenê Şopaneran' hene.";
|
||||
|
||||
"ayoYEb-dYQ5NN" = "${content}, Giştî";
|
||||
"ayoYEb-dYQ5NN" = "${content}, Gelemperî";
|
||||
|
||||
"ayoYEb-ehFLjY" = "${content}, Tenê şopînêr";
|
||||
|
||||
"dUyuGg" = "Li ser Mastodon bişînin";
|
||||
"dUyuGg" = "Di Mastodon de biweşîne";
|
||||
|
||||
"dYQ5NN" = "Gelemperî";
|
||||
|
||||
@ -42,10 +42,10 @@
|
||||
|
||||
"k7dbKQ" = "Şandî bi serkeftî hate şandin.";
|
||||
|
||||
"oGiqmY-dYQ5NN" = "Tenê ji bo pejirandinê, we 'Giştî' dixwest?";
|
||||
"oGiqmY-dYQ5NN" = "Tenê ji bo pejirandinê, te 'Gelemperî' dixwest?";
|
||||
|
||||
"oGiqmY-ehFLjY" = "Tenê ji bo piştrastkirinê, we 'Tenê Şopdarên' dixwest?";
|
||||
"oGiqmY-ehFLjY" = "Tenê ji bo pejirandinê, te 'Tenê Şopîner' dixwest?";
|
||||
|
||||
"rM6dvp" = "Girêdan";
|
||||
|
||||
"ryJLwG" = "Bi serkeftî hat şandin. ";
|
||||
"ryJLwG" = "Şandî bi serkeftî hate şandin. ";
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>There are ${count} options matching ‘${content}’. - 2</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>There are %#@count_option@ matching ‘${content}’.</string>
|
||||
<string>Er zijn %#@count_option@ die overeenkomen met ‘${content}’.</string>
|
||||
<key>count_option</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@ -13,7 +13,7 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>%ld</string>
|
||||
<key>one</key>
|
||||
<string>1 option</string>
|
||||
<string>1 optie</string>
|
||||
<key>other</key>
|
||||
<string>%ld options</string>
|
||||
</dict>
|
||||
@ -21,7 +21,7 @@
|
||||
<key>There are ${count} options matching ‘${visibility}’.</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>There are %#@count_option@ matching ‘${visibility}’.</string>
|
||||
<string>Er zijn %#@count_option@ die overeenkomen met ‘${visibility}’.</string>
|
||||
<key>count_option</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@ -29,7 +29,7 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>%ld</string>
|
||||
<key>one</key>
|
||||
<string>1 option</string>
|
||||
<string>1 optie</string>
|
||||
<key>other</key>
|
||||
<string>%ld options</string>
|
||||
</dict>
|
||||
|
@ -13,13 +13,13 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>zero</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>لا إشعار غير مقروء</string>
|
||||
<key>one</key>
|
||||
<string>إشعار واحِد غير مقروء</string>
|
||||
<key>two</key>
|
||||
<string>إشعاران غير مقروءان</string>
|
||||
<key>few</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>%ld إشعارات غير مقروءة</string>
|
||||
<key>many</key>
|
||||
<string>%ld إشعارًا غيرَ مقروء</string>
|
||||
<key>other</key>
|
||||
@ -277,7 +277,7 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>zero</key>
|
||||
<string>%ld followers</string>
|
||||
<string>لا مُتابِع</string>
|
||||
<key>one</key>
|
||||
<string>مُتابِعٌ واحد</string>
|
||||
<key>two</key>
|
||||
|
@ -6,25 +6,25 @@
|
||||
"please_try_again_later": "يُرجى المحاولة مرة أُخرى لاحقاً."
|
||||
},
|
||||
"sign_up_failure": {
|
||||
"title": "فشل التسجيل"
|
||||
"title": "إخفاق في التسجيل"
|
||||
},
|
||||
"server_error": {
|
||||
"title": "خطأ في الخادم"
|
||||
},
|
||||
"vote_failure": {
|
||||
"title": "فشل التصويت",
|
||||
"title": "إخفاق في التصويت",
|
||||
"poll_ended": "انتهى استطلاع الرأي"
|
||||
},
|
||||
"discard_post_content": {
|
||||
"title": "تجاهل المسودة",
|
||||
"message": "Confirm to discard composed post content."
|
||||
"title": "التخلص من المسودة",
|
||||
"message": "أكِّد للتخلص مِن مُحتوى مَنشور مؤلَّف."
|
||||
},
|
||||
"publish_post_failure": {
|
||||
"title": "أخفقت عملية النشر",
|
||||
"message": "Failed to publish the post.\nPlease check your internet connection.",
|
||||
"title": "إخفاق في عمليَّة النشر",
|
||||
"message": "فَشَلَ نَشر المَنشور.\nيُرجى التحقق من اتصالك بالإنترنت.",
|
||||
"attachments_message": {
|
||||
"video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
|
||||
"more_than_one_video": "Cannot attach more than one video."
|
||||
"video_attach_with_photo": "لا يُمكن إرفاق مقطع مرئي إلى مَنشور يحتوي بالفعل على صُوَر.",
|
||||
"more_than_one_video": "لا يُمكِنُ إرفاق أكثر مِن مَقطع مرئي واحِد."
|
||||
}
|
||||
},
|
||||
"edit_profile_failure": {
|
||||
@ -33,19 +33,19 @@
|
||||
},
|
||||
"sign_out": {
|
||||
"title": "تسجيل الخروج",
|
||||
"message": "هل أنت متأكد من أنك تريد تسجيل الخروج؟",
|
||||
"message": "هل أنت متأكد من رغبتك في تسجيل الخروج؟",
|
||||
"confirm": "تسجيل الخروج"
|
||||
},
|
||||
"block_domain": {
|
||||
"title": "Are you really, really sure you want to block the entire %s? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.",
|
||||
"block_entire_domain": "حظر النطاق"
|
||||
"title": "هل أنتَ مُتأكِّدٌ حقًا مِن رغبتك في حظر %s بالكامل؟ في معظم الحالات، يكون مِنَ الكافي والمُفَضَّل استهداف عدد محدود للحظر أو الكتم. لن ترى محتوى من هذا النطاق وسوف يتم إزالة جميع متابعيك المتواجدين فيه.",
|
||||
"block_entire_domain": "حظر النِطاق"
|
||||
},
|
||||
"save_photo_failure": {
|
||||
"title": "فشل حفظ الصورة",
|
||||
"message": "Please enable the photo library access permission to save the photo."
|
||||
"title": "إخفاق في حفظ الصورة",
|
||||
"message": "يُرجى إتاحة إذن الوصول إلى مكتبة الصور لحفظ الصورة."
|
||||
},
|
||||
"delete_post": {
|
||||
"title": "هل أنت متأكد من أنك تريد حذف هذا المنشور؟",
|
||||
"title": "هل أنت متأكد من رغبتك في حذف هذا المنشور؟",
|
||||
"delete": "احذف"
|
||||
},
|
||||
"clean_cache": {
|
||||
@ -61,30 +61,31 @@
|
||||
"open": "افتح",
|
||||
"add": "إضافة",
|
||||
"remove": "احذف",
|
||||
"edit": "تعديل",
|
||||
"edit": "تحرير",
|
||||
"save": "حفظ",
|
||||
"ok": "حسنًا",
|
||||
"done": "تمّ",
|
||||
"confirm": "تأكيد",
|
||||
"continue": "واصل",
|
||||
"compose": "تأليف",
|
||||
"cancel": "إلغاء",
|
||||
"discard": "تجاهل",
|
||||
"try_again": "المُحاولة مرة أُخرى",
|
||||
"take_photo": "التقط صورة",
|
||||
"take_photo": "التقاط صورة",
|
||||
"save_photo": "حفظ الصورة",
|
||||
"copy_photo": "نسخ الصورة",
|
||||
"sign_in": "تسجيل الدخول",
|
||||
"sign_up": "إنشاء حِساب",
|
||||
"see_more": "عرض المزيد",
|
||||
"preview": "معاينة",
|
||||
"share": "شارك",
|
||||
"share_user": "شارك %s",
|
||||
"share_post": "شارك المنشور",
|
||||
"open_in_safari": "افتحه في سفاري",
|
||||
"preview": "مُعاينة",
|
||||
"share": "المُشارك",
|
||||
"share_user": "مُشاركة %s",
|
||||
"share_post": "مشارك المنشور",
|
||||
"open_in_safari": "الفتح في Safari",
|
||||
"find_people": "ابحث عن أشخاص لمتابعتهم",
|
||||
"manually_search": "البحث يدوياً بدلاً من ذلك",
|
||||
"skip": "تخطي",
|
||||
"reply": "رد",
|
||||
"reply": "الرَد",
|
||||
"report_user": "ابلغ عن %s",
|
||||
"block_domain": "حظر %s",
|
||||
"unblock_domain": "إلغاء حظر %s",
|
||||
@ -100,7 +101,7 @@
|
||||
"keyboard": {
|
||||
"common": {
|
||||
"switch_to_tab": "التبديل إلى %s",
|
||||
"compose_new_post": "إنشاء منشور جديد",
|
||||
"compose_new_post": "تأليف منشور جديد",
|
||||
"show_favorites": "إظهار المفضلة",
|
||||
"open_settings": "أفتح الإعدادات"
|
||||
},
|
||||
@ -111,9 +112,9 @@
|
||||
"open_author_profile": "افتح الملف التعريفي للمؤلف",
|
||||
"open_reblogger_profile": "افتح الملف التعريفي لمشارِك المنشور",
|
||||
"reply_status": "رد على المنشور",
|
||||
"toggle_reblog": "Toggle Reblog on Post",
|
||||
"toggle_favorite": "Toggle Favorite on Post",
|
||||
"toggle_content_warning": "Toggle Content Warning",
|
||||
"toggle_reblog": "تبديل إعادة تدوين منشور",
|
||||
"toggle_favorite": "تبديل المفضلة لِمنشور",
|
||||
"toggle_content_warning": "تبديل تحذير المُحتَوى",
|
||||
"preview_image": "معاينة الصورة"
|
||||
},
|
||||
"segmented_control": {
|
||||
@ -127,9 +128,9 @@
|
||||
"show_post": "اظهر المنشور",
|
||||
"show_user_profile": "اظهر الملف التعريفي للمستخدم",
|
||||
"content_warning": "تحذير عن المحتوى",
|
||||
"media_content_warning": "Tap anywhere to reveal",
|
||||
"media_content_warning": "انقر على أي مكان للكشف",
|
||||
"poll": {
|
||||
"vote": "صَوّت",
|
||||
"vote": "صَوِّت",
|
||||
"closed": "انتهى"
|
||||
},
|
||||
"actions": {
|
||||
@ -179,19 +180,19 @@
|
||||
},
|
||||
"header": {
|
||||
"no_status_found": "لا توجد هناك منشورات",
|
||||
"blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
|
||||
"user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
|
||||
"blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
|
||||
"user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
|
||||
"suspended_warning": "This user has been suspended.",
|
||||
"user_suspended_warning": "%s’s account has been suspended."
|
||||
"blocking_warning": "لا يُمكنك الاطلاع على الملف الشخصي لهذا المُستخدِم\nحتَّى تَرفعَ الحَظر عنه.\nملفًّكَ الشخصي يَظهَرُ بِمثل هذِهِ الحالة بالنسبةِ لَهُ أيضًا.",
|
||||
"user_blocking_warning": "لا يُمكنك الاطلاع على ملف %s الشخصي\nحتَّى تَرفعَ الحَظر عنه.\nملفًّكَ الشخصي يَظهَرُ بِمثل هذِهِ الحالة بالنسبةِ لَهُ أيضًا.",
|
||||
"blocked_warning": "لا يُمكِنُكَ عَرض الملف الشخصي لهذا المُستخدِم\nحتَّى يَرفَعَ الحَظر عَنك.",
|
||||
"user_blocked_warning": "لا يُمكِنُكَ عَرض ملف %s الشخصي\nحتَّى يَرفَعَ الحَظر عَنك.",
|
||||
"suspended_warning": "تمَّ إيقاف هذا المُستخدِم.",
|
||||
"user_suspended_warning": "لقد أوقِفَ حِساب %s."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scene": {
|
||||
"welcome": {
|
||||
"slogan": "Social networking\nback in your hands."
|
||||
"slogan": "شبكات التواصل الاجتماعي\nمرة أُخرى بين يديك."
|
||||
},
|
||||
"server_picker": {
|
||||
"title": "اِختر خادِم،\nأي خادِم.",
|
||||
@ -225,7 +226,7 @@
|
||||
},
|
||||
"empty_state": {
|
||||
"finding_servers": "البحث عن خوادم متوفرة...",
|
||||
"bad_network": "Something went wrong while loading the data. Check your internet connection.",
|
||||
"bad_network": "حدث خطأٌ ما أثناء تحميل البيانات. تحقَّق من اتصالك بالإنترنت.",
|
||||
"no_results": "لا توجد نتائج"
|
||||
}
|
||||
},
|
||||
@ -263,20 +264,20 @@
|
||||
"reason": "السبب"
|
||||
},
|
||||
"reason": {
|
||||
"blocked": "%s contains a disallowed email provider",
|
||||
"unreachable": "%s does not seem to exist",
|
||||
"taken": "%s is already in use",
|
||||
"reserved": "%s is a reserved keyword",
|
||||
"accepted": "%s must be accepted",
|
||||
"blocked": "يحتوي %s على موفِّر خدمة بريد إلكتروني غير مسموح به",
|
||||
"unreachable": "يبدوا أنَّ %s غير موجود",
|
||||
"taken": "إنَّ %s مُستخدَمٌ بالفعل",
|
||||
"reserved": "إنَّ %s عبارة عن كلمة مفتاحيَّة محجوزة",
|
||||
"accepted": "يجب أن يُقبل %s",
|
||||
"blank": "%s مطلوب",
|
||||
"invalid": "%s غير صالح",
|
||||
"too_long": "%s طويل جداً",
|
||||
"too_short": "%s قصير جدا",
|
||||
"inclusion": "%s is not a supported value"
|
||||
"inclusion": "إنَّ %s قيمة غير مدعومة"
|
||||
},
|
||||
"special": {
|
||||
"username_invalid": "Username must only contain alphanumeric characters and underscores",
|
||||
"username_too_long": "Username is too long (can’t be longer than 30 characters)",
|
||||
"username_invalid": "يُمكِن أن يحتوي اسم المستخدم على أحرف أبجدية، أرقام وشرطات سفلية فقط",
|
||||
"username_too_long": "اسم المستخدم طويل جداً (يجب ألّا يكون أطول من 30 رمز)",
|
||||
"email_invalid": "هذا عنوان بريد إلكتروني غير صالح",
|
||||
"password_too_short": "كلمة المرور قصيرة جداً (يجب أن تكون 8 أحرف على الأقل)"
|
||||
}
|
||||
@ -301,12 +302,12 @@
|
||||
},
|
||||
"dont_receive_email": {
|
||||
"title": "تحقق من بريدك الإلكتروني",
|
||||
"description": "Check if your email address is correct as well as your junk folder if you haven’t.",
|
||||
"description": "تحقق ممَّ إذا كان عنوان بريدك الإلكتروني صحيحًا وكذلك تأكد مِن مجلد البريد غير الهام إذا لم تكن قد فعلت ذلك.",
|
||||
"resend_email": "إعادة إرسال البريد الإلكتروني"
|
||||
},
|
||||
"open_email_app": {
|
||||
"title": "تحقَّق من بريدك الوارِد.",
|
||||
"description": "We just sent you an email. Check your junk folder if you haven’t.",
|
||||
"description": "لقد أرسلنا لك بريدًا إلكترونيًا للتو. تحقق من مجلد البريد غير الهام الخاص بك إذا لم تكن قد فعلت ذلك.",
|
||||
"mail": "البريد",
|
||||
"open_email_client": "فتح عميل البريد الإلكتروني"
|
||||
}
|
||||
@ -322,7 +323,7 @@
|
||||
},
|
||||
"suggestion_account": {
|
||||
"title": "ابحث عن أشخاص لمتابعتهم",
|
||||
"follow_explain": "When you follow someone, you’ll see their posts in your home feed."
|
||||
"follow_explain": "عِندَ مُتابَعَتِكَ لأحدِهِم، سَوف تَرى مَنشوراته في تغذيَتِكَ الرئيسة."
|
||||
},
|
||||
"compose": {
|
||||
"title": {
|
||||
@ -340,9 +341,9 @@
|
||||
"attachment": {
|
||||
"photo": "صورة",
|
||||
"video": "فيديو",
|
||||
"attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
|
||||
"description_photo": "Describe the photo for the visually-impaired...",
|
||||
"description_video": "Describe the video for the visually-impaired..."
|
||||
"attachment_broken": "هذا ال%s مُعطَّل ويتعذَّر رفعه إلى ماستودون.",
|
||||
"description_photo": "صِف الصورة للمكفوفين...",
|
||||
"description_video": "صِف المقطع المرئي للمكفوفين..."
|
||||
},
|
||||
"poll": {
|
||||
"duration_time": "المدة: %s",
|
||||
@ -355,7 +356,7 @@
|
||||
"option_number": "الخيار %ld"
|
||||
},
|
||||
"content_warning": {
|
||||
"placeholder": "Write an accurate warning here..."
|
||||
"placeholder": "اكتب تَحذيرًا دَقيقًا هُنا..."
|
||||
},
|
||||
"visibility": {
|
||||
"public": "للعامة",
|
||||
@ -364,7 +365,7 @@
|
||||
"direct": "ففط للأشخاص المشار إليهم"
|
||||
},
|
||||
"auto_complete": {
|
||||
"space_to_add": "Space to add"
|
||||
"space_to_add": "انقر مساحة لإضافتِها"
|
||||
},
|
||||
"accessibility": {
|
||||
"append_attachment": "إضافة مُرفَق",
|
||||
@ -373,7 +374,7 @@
|
||||
"custom_emoji_picker": "منتقي مخصص للإيموجي",
|
||||
"enable_content_warning": "تنشيط تحذير المحتوى",
|
||||
"disable_content_warning": "تعطيل تحذير الحتوى",
|
||||
"post_visibility_menu": "Post Visibility Menu"
|
||||
"post_visibility_menu": "قائمة ظهور المنشور"
|
||||
},
|
||||
"keyboard": {
|
||||
"discard_post": "تجاهُل المنشور",
|
||||
@ -405,14 +406,20 @@
|
||||
"relationship_action_alert": {
|
||||
"confirm_unmute_user": {
|
||||
"title": "إلغاء كتم الحساب",
|
||||
"message": "Confirm to unmute %s"
|
||||
"message": "أكِّد لرفع كتمْ %s"
|
||||
},
|
||||
"confirm_unblock_usre": {
|
||||
"title": "إلغاء حظر الحساب",
|
||||
"message": "Confirm to unblock %s"
|
||||
"message": "أكِّد لرفع حظر %s"
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "لا يُمكِن عَرض المُتابِعين مِنَ الخوادم الأُخرى."
|
||||
},
|
||||
"following": {
|
||||
"footer": "لا يُمكِن عَرض المُتابَعات مِنَ الخوادم الأُخرى."
|
||||
},
|
||||
"search": {
|
||||
"title": "بحث",
|
||||
"search_bar": {
|
||||
@ -423,7 +430,7 @@
|
||||
"button_text": "طالع الكل",
|
||||
"hash_tag": {
|
||||
"title": "ذات شعبية على ماستدون",
|
||||
"description": "Hashtags that are getting quite a bit of attention",
|
||||
"description": "الوسوم التي تحظى بقدر كبير من الاهتمام",
|
||||
"people_talking": "%s أشخاص يتحدَّثوا"
|
||||
},
|
||||
"accounts": {
|
||||
@ -443,7 +450,7 @@
|
||||
"no_results": "ليس هناك أية نتيجة"
|
||||
},
|
||||
"recent_search": "عمليات البحث الأخيرة",
|
||||
"clear": "امسح"
|
||||
"clear": "مَحو"
|
||||
}
|
||||
},
|
||||
"favorite": {
|
||||
@ -512,7 +519,7 @@
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"mastodon_description": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على غيت هب %s (%s)"
|
||||
"mastodon_description": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء على GitHub في %s (%s)"
|
||||
},
|
||||
"keyboard": {
|
||||
"close_settings_window": "إغلاق نافذة الإعدادات"
|
||||
@ -522,27 +529,27 @@
|
||||
"title": "ابلغ عن %s",
|
||||
"step1": "الخطوة 1 من 2",
|
||||
"step2": "الخطوة 2 من 2",
|
||||
"content1": "Are there any other posts you’d like to add to the report?",
|
||||
"content2": "Is there anything the moderators should know about this report?",
|
||||
"send": "ارسل الشكوى",
|
||||
"content1": "هل ترغب في إضافة أي مشاركات أُخرى إلى الشكوى؟",
|
||||
"content2": "هل هناك أي شيء يجب أن يعرفه المُراقبين حول هذه الشكوى؟",
|
||||
"send": "إرسال الشكوى",
|
||||
"skip_to_send": "إرسال بدون تعليق",
|
||||
"text_placeholder": "Type or paste additional comments"
|
||||
"text_placeholder": "اكتب أو الصق تعليقات إضافيَّة"
|
||||
},
|
||||
"preview": {
|
||||
"keyboard": {
|
||||
"close_preview": "إغلاق المعاينة",
|
||||
"close_preview": "إغلاق المُعايَنَة",
|
||||
"show_next": "إظهار التالي",
|
||||
"show_previous": "إظهار السابق"
|
||||
}
|
||||
},
|
||||
"account_list": {
|
||||
"tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
|
||||
"tab_bar_hint": "المِلف المُحدَّد حاليًا: %s. انقر نقرًا مزدوجًا ثم اضغط مع الاستمرار لإظهار مُبدِّل الحِساب",
|
||||
"dismiss_account_switcher": "تجاهُل مبدِّل الحساب",
|
||||
"add_account": "إضافة حساب"
|
||||
},
|
||||
"wizard": {
|
||||
"new_in_mastodon": "جديد في ماستودون",
|
||||
"multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
|
||||
"multiple_account_switch_intro_description": "بدِّل بين حسابات متعددة عبر الاستمرار بالضغط على زر الملف الشخصي.",
|
||||
"accessibility_hint": "انقر نقرًا مزدوجًا لتجاهل النافذة المنبثقة"
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Fet",
|
||||
"confirm": "Confirma",
|
||||
"continue": "Continua",
|
||||
"compose": "Composa",
|
||||
"cancel": "Cancel·la",
|
||||
"discard": "Descarta",
|
||||
"try_again": "Torna a provar",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Els seguidors d'altres servidors no son mostrats."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Els seguits d'altres servidors no son mostrats."
|
||||
},
|
||||
"search": {
|
||||
"title": "Cerca",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Fertig",
|
||||
"confirm": "Bestätigen",
|
||||
"continue": "Fortfahren",
|
||||
"compose": "Compose",
|
||||
"cancel": "Abbrechen",
|
||||
"discard": "Verwerfen",
|
||||
"try_again": "Nochmals versuchen",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Suche",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Listo",
|
||||
"confirm": "Confirmar",
|
||||
"continue": "Continuar",
|
||||
"compose": "Redactar",
|
||||
"cancel": "Cancelar",
|
||||
"discard": "Descartar",
|
||||
"try_again": "Intentá de nuevo",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "No se muestran los seguidores de otros servidores."
|
||||
},
|
||||
"following": {
|
||||
"footer": "No se muestran las cuentas de otros servidores que seguís."
|
||||
},
|
||||
"search": {
|
||||
"title": "Buscar",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Hecho",
|
||||
"confirm": "Confirmar",
|
||||
"continue": "Continuar",
|
||||
"compose": "Redactar",
|
||||
"cancel": "Cancelar",
|
||||
"discard": "Descartar",
|
||||
"try_again": "Inténtalo de nuevo",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "No se muestran los seguidores de otros servidores."
|
||||
},
|
||||
"following": {
|
||||
"footer": "No se muestran los seguidos de otros servidores."
|
||||
},
|
||||
"search": {
|
||||
"title": "Buscar",
|
||||
"search_bar": {
|
||||
|
@ -13,15 +13,15 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>1 unread notification</string>
|
||||
<string>1 notification non lue</string>
|
||||
<key>other</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>%ld notifications non lues</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>a11y.plural.count.input_limit_exceeds</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Input limit exceeds %#@character_count@</string>
|
||||
<string>La limite d’entrée dépasse %#@character_count@</string>
|
||||
<key>character_count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Terminé",
|
||||
"confirm": "Confirmer",
|
||||
"continue": "Continuer",
|
||||
"compose": "Compose",
|
||||
"cancel": "Annuler",
|
||||
"discard": "Abandonner",
|
||||
"try_again": "Réessayer",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Les abonné·e·s issus des autres serveurs ne sont pas affiché·e·s."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Rechercher",
|
||||
"search_bar": {
|
||||
@ -538,12 +545,12 @@
|
||||
"account_list": {
|
||||
"tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
|
||||
"dismiss_account_switcher": "Dismiss Account Switcher",
|
||||
"add_account": "Add Account"
|
||||
"add_account": "Ajouter un compte"
|
||||
},
|
||||
"wizard": {
|
||||
"new_in_mastodon": "New in Mastodon",
|
||||
"multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
|
||||
"accessibility_hint": "Double tap to dismiss this wizard"
|
||||
"new_in_mastodon": "Nouveau dans Mastodon",
|
||||
"multiple_account_switch_intro_description": "Basculez entre plusieurs comptes en appuyant de maniere prolongée sur le bouton profil.",
|
||||
"accessibility_hint": "Tapotez deux fois pour fermer cet assistant"
|
||||
}
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@
|
||||
"done": "Deiseil",
|
||||
"confirm": "Dearbh",
|
||||
"continue": "Lean air adhart",
|
||||
"compose": "Sgrìobh",
|
||||
"cancel": "Sguir dheth",
|
||||
"discard": "Tilg air falbh",
|
||||
"try_again": "Feuch ris a-rithist",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Cha dèid luchd-leantainn o fhrithealaichean eile a shealltainn."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Cha dèid cò air a leanas tu air frithealaichean eile a shealltainn."
|
||||
},
|
||||
"search": {
|
||||
"title": "Lorg",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Selesai",
|
||||
"confirm": "Konfirmasi",
|
||||
"continue": "Lanjut",
|
||||
"compose": "Compose",
|
||||
"cancel": "Batal",
|
||||
"discard": "Discard",
|
||||
"try_again": "Coba Lagi",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Cari",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "完了",
|
||||
"confirm": "確認",
|
||||
"continue": "続ける",
|
||||
"compose": "Compose",
|
||||
"cancel": "キャンセル",
|
||||
"discard": "破棄",
|
||||
"try_again": "再実行",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "検索",
|
||||
"search_bar": {
|
||||
|
@ -109,9 +109,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>1 reblog</string>
|
||||
<string>1 ji nû ve nivîsandin</string>
|
||||
<key>other</key>
|
||||
<string>%ld reblogs</string>
|
||||
<string>%ld ji nû ve nivîsandin</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>plural.count.vote</key>
|
||||
|
@ -16,8 +16,8 @@
|
||||
"poll_ended": "Rapirsîya qediya"
|
||||
},
|
||||
"discard_post_content": {
|
||||
"title": "Reşnivîs jêbibe",
|
||||
"message": "Piştrast bikin ku naveroka posteyê ya hatîye nivîsandin jê bibin."
|
||||
"title": "Reşnivîsê paşguh bike",
|
||||
"message": "Bipejrîne ku naveroka şandiyê ya hatiye nivîsandin paşguh bikî."
|
||||
},
|
||||
"publish_post_failure": {
|
||||
"title": "Weşandin têkçû",
|
||||
@ -28,7 +28,7 @@
|
||||
}
|
||||
},
|
||||
"edit_profile_failure": {
|
||||
"title": "Çewtiya profîlê biguherîne",
|
||||
"title": "Di serrastkirina profîlê çewtî",
|
||||
"message": "Nikare profîlê serrast bike. Jkx dîsa biceribîne."
|
||||
},
|
||||
"sign_out": {
|
||||
@ -42,15 +42,15 @@
|
||||
},
|
||||
"save_photo_failure": {
|
||||
"title": "Tomarkirina wêneyê têkçû",
|
||||
"message": "Ji kerema xwe destûra gihîştina pirtûkxaneya wêneyê çalak bikin da ku wêneyê hilînin."
|
||||
"message": "Ji kerema xwe mafê bide gihîştina wênegehê çalak bike da ku wêne werin tomarkirin."
|
||||
},
|
||||
"delete_post": {
|
||||
"title": "Ma tu dixwazî vê şandiyê jê bibî?",
|
||||
"delete": "Jê bibe"
|
||||
},
|
||||
"clean_cache": {
|
||||
"title": "Pêşbîrê paqij bike",
|
||||
"message": "Pêşbîra %s biserketî hate paqijkirin."
|
||||
"title": "Pêşbîrê pak bike",
|
||||
"message": "Pêşbîra %s biserketî hate pakkirin."
|
||||
}
|
||||
},
|
||||
"controls": {
|
||||
@ -67,12 +67,13 @@
|
||||
"done": "Qediya",
|
||||
"confirm": "Bipejirîne",
|
||||
"continue": "Bidomîne",
|
||||
"compose": "Binivîsîne",
|
||||
"cancel": "Dev jê berde",
|
||||
"discard": "Biavêje",
|
||||
"try_again": "Dîsa biceribîne",
|
||||
"take_photo": "Wêne bikişîne",
|
||||
"save_photo": "Wêneyê hilîne",
|
||||
"copy_photo": "Wêne kopî bikin",
|
||||
"save_photo": "Wêneyê tomar bike",
|
||||
"copy_photo": "Wêneyê jê bigire",
|
||||
"sign_in": "Têkeve",
|
||||
"sign_up": "Tomar bibe",
|
||||
"see_more": "Bêtir bibîne",
|
||||
@ -81,8 +82,8 @@
|
||||
"share_user": "%s parve bike",
|
||||
"share_post": "Şandiyê parve bike",
|
||||
"open_in_safari": "Di Safariyê de veke",
|
||||
"find_people": "Kesên ku bişopînin bibînin",
|
||||
"manually_search": "Ji devlê i destan lêgerînê bike",
|
||||
"find_people": "Mirovan bo şopandinê bibîne",
|
||||
"manually_search": "Ji devlê bi destan lêgerînê bike",
|
||||
"skip": "Derbas bike",
|
||||
"reply": "Bersivê bide",
|
||||
"report_user": "%s ragihîne",
|
||||
@ -111,48 +112,48 @@
|
||||
"open_author_profile": "Profîla nivîskaran veke",
|
||||
"open_reblogger_profile": "Profîla nivîskaran veke",
|
||||
"reply_status": "Bersivê bide şandiyê",
|
||||
"toggle_reblog": "Toggle Reblog on Post",
|
||||
"toggle_favorite": "Di postê da Bijartin veke/bigire",
|
||||
"toggle_content_warning": "Hişyariya naverokê veke/bigire",
|
||||
"preview_image": "Wêneya pêşdîtinê"
|
||||
"toggle_reblog": "Ji vû nivîsandin di şandiyê de biguherîne",
|
||||
"toggle_favorite": "Li ser şandiyê bijarte biguherîne",
|
||||
"toggle_content_warning": "Hişyariya naverokê biguherîne",
|
||||
"preview_image": "Pêşdîtina wêneyê"
|
||||
},
|
||||
"segmented_control": {
|
||||
"previous_section": "Beşa berê",
|
||||
"next_section": "Beşa paşê"
|
||||
"previous_section": "Beşa paş",
|
||||
"next_section": "Beşa pêş"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"user_reblogged": "%s ji nû ve hat blogkirin",
|
||||
"user_reblogged": "%s ji nû ve hate nivîsandin",
|
||||
"user_replied_to": "Bersiv da %s",
|
||||
"show_post": "Şandiyê nîşan bide",
|
||||
"show_user_profile": "Profîla bikarhêner nîşan bide",
|
||||
"content_warning": "Hişyariya naverokê",
|
||||
"media_content_warning": "Ji bo aşkerakirinê derekî bitikîne",
|
||||
"media_content_warning": "Ji bo eşkerekirinê li derekî bitikîne",
|
||||
"poll": {
|
||||
"vote": "Deng",
|
||||
"vote": "Deng bide",
|
||||
"closed": "Girtî"
|
||||
},
|
||||
"actions": {
|
||||
"reply": "Bersivê bide",
|
||||
"reblog": "Ji nû ve blog",
|
||||
"unreblog": "Ji nû ve blogkirin betal bikin",
|
||||
"favorite": "Bijartî",
|
||||
"unfavorite": "Nebijare",
|
||||
"menu": "Menû"
|
||||
"reblog": "Ji nû ve nivîsandin",
|
||||
"unreblog": "Ji nû ve nivîsandinê vegere",
|
||||
"favorite": "Bijarte",
|
||||
"unfavorite": "Nebijarte",
|
||||
"menu": "Kulîn"
|
||||
},
|
||||
"tag": {
|
||||
"url": "URL",
|
||||
"mention": "Behs",
|
||||
"mention": "Qalkirin",
|
||||
"link": "Girêdan",
|
||||
"hashtag": "Etîket",
|
||||
"hashtag": "Hashtag",
|
||||
"email": "E-name",
|
||||
"emoji": "E-name"
|
||||
"emoji": "Emojî"
|
||||
}
|
||||
},
|
||||
"friendship": {
|
||||
"follow": "Bişopîne",
|
||||
"following": "Dişopîne",
|
||||
"request": "Daxwazên şopandinê",
|
||||
"request": "Daxwaz bike",
|
||||
"pending": "Tê nirxandin",
|
||||
"block": "Asteng bike",
|
||||
"block_user": "%s asteng bike",
|
||||
@ -173,18 +174,18 @@
|
||||
"now": "Niha"
|
||||
},
|
||||
"loader": {
|
||||
"load_missing_posts": "Barkirina posteyên kêm",
|
||||
"loading_missing_posts": "Barkirina posteyên kêm...",
|
||||
"load_missing_posts": "Şandiyên wendayî bar bike",
|
||||
"loading_missing_posts": "Şandiyên wendayî tên barkirin...",
|
||||
"show_more_replies": "Bêtir bersivan nîşan bide"
|
||||
},
|
||||
"header": {
|
||||
"no_status_found": "Şandî nehate dîtin",
|
||||
"blocking_warning": "Tu nikarî profîla vî bikarhênerî bibînî\nHeta ku tu wan asteng bikî.\nProfîla te ji wan ra wiha xuya dike.",
|
||||
"user_blocking_warning": "Tu nikarî profîla %s bibînî\nHeta ku tu wan asteng bikî.\nProfîla te ji wan ra wiha xuya dike.",
|
||||
"blocked_warning": "Tu nikarî profîla vî bikarhênerî bibînî\nheta ku astengîya te rakin.",
|
||||
"no_status_found": "Tu şandî nehate dîtin",
|
||||
"blocking_warning": "Tu nikarî profîla vî/ê bikarhênerî bibînî\nHeya ku tu astengiyê li ser wî/ê ranekî.\nProfîla te ji wan ra wiha xuya dike.",
|
||||
"user_blocking_warning": "Tu nikarî profîla %s bibînî\nHeya ku tu astengiyê li ser wî/ê ranekî.\nProfîla te ji wan ra wiha xuya dike.",
|
||||
"blocked_warning": "Tu nikarî profîla vî/ê bikarhênerî bibînî\nheya ku ew astengiyê li ser te rakin.",
|
||||
"user_blocked_warning": "Tu nikarî profîla %s bibînî\nHeta ku astengîya te rakin.",
|
||||
"suspended_warning": "Ev bikarhêner hat sekinandin.",
|
||||
"user_suspended_warning": "Hesaba %s hat sekinandin."
|
||||
"suspended_warning": "Ev bikarhêner hatiye rawestandin.",
|
||||
"user_suspended_warning": "Ajimêra %s hatiye rawestandin."
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,15 +219,15 @@
|
||||
"label": {
|
||||
"language": "ZIMAN",
|
||||
"users": "BIKARHÊNER",
|
||||
"category": "KATEGORÎ"
|
||||
"category": "BEŞ"
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "Serverek bibînin an jî beşdarî ya xwe bibin..."
|
||||
"placeholder": "Rajekarekî bibîne an jî beşdarî ya xwe bibe..."
|
||||
},
|
||||
"empty_state": {
|
||||
"finding_servers": "Dîtina serverên berdest...",
|
||||
"bad_network": "Di dema barkirina daneyan da tiştek xelet derket. Girêdana xwe ya înternetê kontrol bike.",
|
||||
"no_results": "Encam nade"
|
||||
"finding_servers": "Peydakirina rajekarên berdest...",
|
||||
"bad_network": "Di dema barkirina daneyan da çewtî derket. Girêdana xwe ya înternetê kontrol bike.",
|
||||
"no_results": "Encam tune"
|
||||
}
|
||||
},
|
||||
"register": {
|
||||
@ -246,8 +247,8 @@
|
||||
"placeholder": "e-name"
|
||||
},
|
||||
"password": {
|
||||
"placeholder": "şîfre",
|
||||
"hint": "Şîfreya we herî kêm heşt tîpan hewce dike"
|
||||
"placeholder": "pêborîn",
|
||||
"hint": "Pêborîna te herî kêm divê ji 8 tîpan pêk bê"
|
||||
},
|
||||
"invite": {
|
||||
"registration_user_invite_request": "Tu çima dixwazî beşdar bibî?"
|
||||
@ -257,44 +258,44 @@
|
||||
"item": {
|
||||
"username": "Navê bikarhêner",
|
||||
"email": "E-name",
|
||||
"password": "Şîfre",
|
||||
"agreement": "Lihevhatin",
|
||||
"locale": "Herêm",
|
||||
"password": "Pêborîn",
|
||||
"agreement": "Peyman",
|
||||
"locale": "Zimanê navrûyê",
|
||||
"reason": "Sedem"
|
||||
},
|
||||
"reason": {
|
||||
"blocked": "%s peydekerê e-nameya bêdestûr dihewîne",
|
||||
"unreachable": "%s xuya nake",
|
||||
"blocked": "%s peydekerê e-peyamê yê qedexekirî dihewîne",
|
||||
"unreachable": "%s xuya ye ku tune ye",
|
||||
"taken": "%s jixwe tê bikaranîn",
|
||||
"reserved": "%s peyveke mifteya veqetandî ye",
|
||||
"accepted": "%s divê were qebûlkirin",
|
||||
"reserved": "%s peyveke parastî ye",
|
||||
"accepted": "%s divê were pejirandin",
|
||||
"blank": "%s pêwist e",
|
||||
"invalid": "%s ne derbasdar e",
|
||||
"too_long": "%s gelekî dirêj e",
|
||||
"too_long": "%s pir dirêj e",
|
||||
"too_short": "%s pir kurt e",
|
||||
"inclusion": "%s nirxeke ku tê destekirin nîn e"
|
||||
"inclusion": "%s ne nirxek piştgirî ye"
|
||||
},
|
||||
"special": {
|
||||
"username_invalid": "Navê bikarhêner divê tenê tîpên alfanumerîk û binxet hebe",
|
||||
"username_invalid": "Navê bikarhêner divê tenê ji tîpên alfajimarî û binxêz pêk be",
|
||||
"username_too_long": "Navê bikarhêner pir dirêj e (ji 30 tîpan dirêjtir nabe)",
|
||||
"email_invalid": "Ev ne navnîşana e-nameyek derbasdar e",
|
||||
"password_too_short": "Şîfre pir kurt e (divê herî kêm 8 tîpan be)"
|
||||
"email_invalid": "Ev navnîşaneke e-nameyê ne derbasdar e",
|
||||
"password_too_short": "Pêborîn pir kurt e (divê herî kêm 8 tîp be)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server_rules": {
|
||||
"title": "Hin qaîdeyên bingehîn.",
|
||||
"title": "Hinek rêzikên bingehîn.",
|
||||
"subtitle": "Ev rêzik ji aliyê rêvebirên %s ve tên sazkirin.",
|
||||
"prompt": "Bi berdewamî, hûn ji bo %s di bin şertên polîtîkaya xizmet û nepenîtiyê da ne.",
|
||||
"terms_of_service": "şert û mercên xizmetê",
|
||||
"privacy_policy": "polîtîkaya nepenîtiyê",
|
||||
"prompt": "Bi domandinê, tu ji bo %s di bin mercên bikaranînê û polîtîkaya nepenîtiyê dipejirînî.",
|
||||
"terms_of_service": "mercên bikaranînê",
|
||||
"privacy_policy": "polîtikaya nihêniyê",
|
||||
"button": {
|
||||
"confirm": "Ez tev dibim"
|
||||
"confirm": "Ez dipejirînim"
|
||||
}
|
||||
},
|
||||
"confirm_email": {
|
||||
"title": "Tiştekî dawî.",
|
||||
"subtitle": "We just sent an email to %s,\ntap the link to confirm your account.",
|
||||
"subtitle": "Me tenê e-nameyek ji %s re şand,\ngirêdanê bitikne da ku ajimêra xwe bidî piştrastkirin.",
|
||||
"button": {
|
||||
"open_email_app": "Sepana e-nameyê veke",
|
||||
"dont_receive_email": "Min hîç e-nameyeke nesitand"
|
||||
@ -305,8 +306,8 @@
|
||||
"resend_email": "E-namyê yê dîsa bişîne"
|
||||
},
|
||||
"open_email_app": {
|
||||
"title": "Check your inbox.",
|
||||
"description": "We just sent you an email. Check your junk folder if you haven’t.",
|
||||
"title": "Nameyên xwe yên wergirtî kontrol bike.",
|
||||
"description": "Me tenê ji te re e-nameyek şand. Heke nehatiye peldanka xwe ya spamê kontrol bike.",
|
||||
"mail": "E-name",
|
||||
"open_email_client": "Rajegirê e-nameyê veke"
|
||||
}
|
||||
@ -334,15 +335,15 @@
|
||||
"photo_library": "Wênegeh",
|
||||
"browse": "Bigere"
|
||||
},
|
||||
"content_input_placeholder": "Type or paste what’s on your mind",
|
||||
"content_input_placeholder": "Tiştê ku di hişê te de ye binivîsin an jî pêve bike",
|
||||
"compose_action": "Biweşîne",
|
||||
"replying_to_user": "bersiv bide %s",
|
||||
"attachment": {
|
||||
"photo": "wêne",
|
||||
"video": "vîdyo",
|
||||
"attachment_broken": "Ev %s naxebite û nayê barkirin\n li ser Mastodon.",
|
||||
"description_photo": "Describe the photo for the visually-impaired...",
|
||||
"description_video": "Describe the video for the visually-impaired..."
|
||||
"description_photo": "Wêneyê ji bo kêmbînên dîtbar bide nasîn...",
|
||||
"description_video": "Vîdyoyê ji bo kêmbînên dîtbar bide nasîn..."
|
||||
},
|
||||
"poll": {
|
||||
"duration_time": "Dirêjî: %s",
|
||||
@ -355,7 +356,7 @@
|
||||
"option_number": "Vebijêrk %ld"
|
||||
},
|
||||
"content_warning": {
|
||||
"placeholder": "Write an accurate warning here..."
|
||||
"placeholder": "Li vir hişyariyek hûrgilî binivîsine..."
|
||||
},
|
||||
"visibility": {
|
||||
"public": "Gelemperî",
|
||||
@ -364,71 +365,77 @@
|
||||
"direct": "Tenê mirovên ku min qalkirî"
|
||||
},
|
||||
"auto_complete": {
|
||||
"space_to_add": "Space to add"
|
||||
"space_to_add": "Bicîhkirinê tevlî bike"
|
||||
},
|
||||
"accessibility": {
|
||||
"append_attachment": "Pêvek tevlî bike",
|
||||
"append_poll": "Rapirsî tevlî bike",
|
||||
"remove_poll": "Rapirsî rake",
|
||||
"custom_emoji_picker": "Custom Emoji Picker",
|
||||
"enable_content_warning": "Enable Content Warning",
|
||||
"custom_emoji_picker": "Hilbijêrê emojî yên kesanekirî",
|
||||
"enable_content_warning": "Hişyariya naverokê çalak bike",
|
||||
"disable_content_warning": "Hişyariya naverokê neçalak bike",
|
||||
"post_visibility_menu": "Menuya Xuyabûna Şandiyê"
|
||||
"post_visibility_menu": "Kulîna xuyabûna şandiyê"
|
||||
},
|
||||
"keyboard": {
|
||||
"discard_post": "Şandî bihelîne",
|
||||
"publish_post": "Şandiye bide weşan",
|
||||
"toggle_poll": "Anketê veke/bigire",
|
||||
"toggle_content_warning": "Hişyariya naverokê veke/bigire",
|
||||
"append_attachment_entry": "Pêvek lê zêde bike - %s",
|
||||
"select_visibility_entry": "Xuyanîbûn hilbijêre - %s"
|
||||
"discard_post": "Şandî paşguh bike",
|
||||
"publish_post": "Şandiyê biweşîne",
|
||||
"toggle_poll": "Rapirsiyê biguherîne",
|
||||
"toggle_content_warning": "Hişyariya naverokê biguherîne",
|
||||
"append_attachment_entry": "Pêvek tevlî bike - %s",
|
||||
"select_visibility_entry": "Xuyabûnê hilbijêre - %s"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"dashboard": {
|
||||
"posts": "şandîyan",
|
||||
"posts": "şandî",
|
||||
"following": "dişopîne",
|
||||
"followers": "şopîneran"
|
||||
"followers": "şopîner"
|
||||
},
|
||||
"fields": {
|
||||
"add_row": "Rêzê lê zêde bike",
|
||||
"add_row": "Rêzê tevlî bike",
|
||||
"placeholder": {
|
||||
"label": "Nîşan",
|
||||
"content": "Naverok"
|
||||
}
|
||||
},
|
||||
"segmented_control": {
|
||||
"posts": "Şandîyan",
|
||||
"replies": "Bersivan",
|
||||
"posts": "Şandî",
|
||||
"replies": "Bersiv",
|
||||
"media": "Medya"
|
||||
},
|
||||
"relationship_action_alert": {
|
||||
"confirm_unmute_user": {
|
||||
"title": "Hesabê ji bê deng rake",
|
||||
"message": "Ji bo vekirina bê dengkirinê bipejirin %s"
|
||||
"title": "Ajimêrê bêdeng neke",
|
||||
"message": "Ji bo vekirina bêdengkirinê bipejirîne %s"
|
||||
},
|
||||
"confirm_unblock_usre": {
|
||||
"title": "Hesabê ji bloke rake",
|
||||
"message": "Ji bo rakirina blokê bipejirin %s"
|
||||
"title": "Astengiyê li ser ajimêr rake",
|
||||
"message": "Ji bo rakirina astengkirinê bipejirîne %s"
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Şopîner ji rajekerên din nayê dîtin."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Şopandin ji rajekerên din nayê dîtin."
|
||||
},
|
||||
"search": {
|
||||
"title": "Bigere",
|
||||
"search_bar": {
|
||||
"placeholder": "Li etîketan û bikarhêneran bigerin",
|
||||
"cancel": "Betal kirin"
|
||||
"placeholder": "Li hashtag û bikarhêneran bigere",
|
||||
"cancel": "Dev jê berde"
|
||||
},
|
||||
"recommend": {
|
||||
"button_text": "Hemûyé bibîne",
|
||||
"button_text": "Hemûyan bibîne",
|
||||
"hash_tag": {
|
||||
"title": "Trend li ser Mastodon",
|
||||
"description": "Etîketên ku pir balê dikişînin",
|
||||
"title": "Rojev li ser Mastodon",
|
||||
"description": "Hashtag ên ku pir balê dikişînin",
|
||||
"people_talking": "%s kes diaxivin"
|
||||
},
|
||||
"accounts": {
|
||||
"title": "Hesabên ku hûn dikarin hez bikin",
|
||||
"description": "Dibe ku tu bixwazî van hesaban bişopînî",
|
||||
"title": "Ajimêrên ku belkî tu jê hez bikî",
|
||||
"description": "Dibe ku tu bixwazî van ajimêran bişopînî",
|
||||
"follow": "Bişopîne"
|
||||
}
|
||||
},
|
||||
@ -436,38 +443,38 @@
|
||||
"segment": {
|
||||
"all": "Hemû",
|
||||
"people": "Mirov",
|
||||
"hashtags": "Etîketan",
|
||||
"posts": "Şandîyan"
|
||||
"hashtags": "Hashtag",
|
||||
"posts": "Şandî"
|
||||
},
|
||||
"empty_state": {
|
||||
"no_results": "Encam tune"
|
||||
},
|
||||
"recent_search": "Lêgerînên dawî",
|
||||
"clear": "Paqij bike"
|
||||
"clear": "Pak bike"
|
||||
}
|
||||
},
|
||||
"favorite": {
|
||||
"title": "Bijareyên te"
|
||||
"title": "Bijarteyên te"
|
||||
},
|
||||
"notification": {
|
||||
"title": {
|
||||
"Everything": "Her tişt",
|
||||
"Mentions": "Behs"
|
||||
"Mentions": "Qalkirin"
|
||||
},
|
||||
"user_followed_you": "%s te şopand",
|
||||
"user_favorited your post": "%s posta we bijarte",
|
||||
"user_favorited your post": "%s şandiya te hez kir",
|
||||
"user_reblogged_your_post": "%s posta we ji nû ve tomar kir",
|
||||
"user_mentioned_you": "%s behsa te kir",
|
||||
"user_requested_to_follow_you": "%s daxwaza şopandina te kir",
|
||||
"user_your_poll_has_ended": "%s Anketa te qediya",
|
||||
"user_mentioned_you": "%s qale te kir",
|
||||
"user_requested_to_follow_you": "%s dixwazê te bişopîne",
|
||||
"user_your_poll_has_ended": "Rapirsîya te qediya",
|
||||
"keyobard": {
|
||||
"show_everything": "Her tiştî nîşan bide",
|
||||
"show_mentions": "Behskirîya nîşan bike"
|
||||
"show_mentions": "Qalkirinan nîşan bike"
|
||||
}
|
||||
},
|
||||
"thread": {
|
||||
"back_title": "Şandî",
|
||||
"title": "Post from %s"
|
||||
"title": "Şandî ji %s"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Sazkarî",
|
||||
@ -481,8 +488,8 @@
|
||||
"notifications": {
|
||||
"title": "Agahdarî",
|
||||
"favorites": "Şandiyên min hez kir",
|
||||
"follows": "Min şopand",
|
||||
"boosts": "Reblogs my post",
|
||||
"follows": "Min dişopîne",
|
||||
"boosts": "Şandiya min ji nû ve nivîsand",
|
||||
"mentions": "Qale min kir",
|
||||
"trigger": {
|
||||
"anyone": "her kes",
|
||||
@ -494,39 +501,39 @@
|
||||
},
|
||||
"preference": {
|
||||
"title": "Hilbijarte",
|
||||
"true_black_dark_mode": "True black dark mode",
|
||||
"disable_avatar_animation": "Disable animated avatars",
|
||||
"disable_emoji_animation": "Disable animated emojis",
|
||||
"using_default_browser": "Use default browser to open links"
|
||||
"true_black_dark_mode": "Moda tarî ya reş a rastîn",
|
||||
"disable_avatar_animation": "Avatarên anîmasyonî neçalak bike",
|
||||
"disable_emoji_animation": "Emojiyên anîmasyonî neçalak bike",
|
||||
"using_default_browser": "Ji bo vekirina girêdanan geroka berdest bi kar bîne"
|
||||
},
|
||||
"boring_zone": {
|
||||
"title": "The Boring Zone",
|
||||
"account_settings": "Account Settings",
|
||||
"terms": "Terms of Service",
|
||||
"privacy": "Privacy Policy"
|
||||
"title": "Devera acizker",
|
||||
"account_settings": "Sazkariyên ajimêr",
|
||||
"terms": "Mercên bikaranînê",
|
||||
"privacy": "Polîtikaya nihêniyê"
|
||||
},
|
||||
"spicy_zone": {
|
||||
"title": "The Spicy Zone",
|
||||
"clear": "Clear Media Cache",
|
||||
"signout": "Sign Out"
|
||||
"title": "Devera germ",
|
||||
"clear": "Pêşbîra medyayê pak bike",
|
||||
"signout": "Derkeve"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
|
||||
"mastodon_description": "Mastodon nermalava çavkaniya vekirî ye. Tu dikarî pirsgirêkan li ser GitHub-ê ragihînî di %s (%s) de"
|
||||
},
|
||||
"keyboard": {
|
||||
"close_settings_window": "Close Settings Window"
|
||||
"close_settings_window": "Sazkariyên çarçoveyê bigire"
|
||||
}
|
||||
},
|
||||
"report": {
|
||||
"title": "%s ragihîne",
|
||||
"step1": "Gav 1 ji 2",
|
||||
"step2": "Gav 2 ji 2",
|
||||
"content1": "Are there any other posts you’d like to add to the report?",
|
||||
"content2": "Is there anything the moderators should know about this report?",
|
||||
"content1": "Şandiyên din hene ku tu dixwazî tevlî ragihandinê bikî?",
|
||||
"content2": "Derbarê vê ragihandinê de tiştek heye ku divê çavdêr bizanin?",
|
||||
"send": "Ragihandinê bişîne",
|
||||
"skip_to_send": "Bêyî şirove bişîne",
|
||||
"text_placeholder": "Type or paste additional comments"
|
||||
"text_placeholder": "Şiroveyên daxwazkirê binivîsine an jî pê ve bike"
|
||||
},
|
||||
"preview": {
|
||||
"keyboard": {
|
||||
@ -537,13 +544,13 @@
|
||||
},
|
||||
"account_list": {
|
||||
"tab_bar_hint": "Profîla hilbijartî ya niha: %s. Du caran bitikîne û paşê dest bide ser da ku guhêrbara ajimêr were nîşandan",
|
||||
"dismiss_account_switcher": "Dismiss Account Switcher",
|
||||
"dismiss_account_switcher": "Guherkera ajimêrê paş guh bike",
|
||||
"add_account": "Ajimêr tevlî bike"
|
||||
},
|
||||
"wizard": {
|
||||
"new_in_mastodon": "Nû di Mastodon de",
|
||||
"multiple_account_switch_intro_description": "Dest bide ser bişkoja profîlê da ku di navbera gelek ajimêrann de biguherînî.",
|
||||
"accessibility_hint": "Double tap to dismiss this wizard"
|
||||
"accessibility_hint": "Du caran bitikîne da ku çarçoveyahilpekok ji holê rakî"
|
||||
}
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@
|
||||
"done": "완료",
|
||||
"confirm": "확인",
|
||||
"continue": "계속",
|
||||
"compose": "Compose",
|
||||
"cancel": "취소",
|
||||
"discard": "버리기",
|
||||
"try_again": "다시 시도",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "검색",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Klaar",
|
||||
"confirm": "Bevestigen",
|
||||
"continue": "Doorgaan",
|
||||
"compose": "Compose",
|
||||
"cancel": "Annuleren",
|
||||
"discard": "Weggooien",
|
||||
"try_again": "Probeer Opnieuw",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Zoeken",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Готово",
|
||||
"confirm": "Подтвердить",
|
||||
"continue": "Продолжить",
|
||||
"compose": "Compose",
|
||||
"cancel": "Отмена",
|
||||
"discard": "Отмена",
|
||||
"try_again": "Попробовать снова",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Поиск",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Fortsätt",
|
||||
"compose": "Compose",
|
||||
"cancel": "Avbryt",
|
||||
"discard": "Discard",
|
||||
"try_again": "Försök igen",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Fortsätt",
|
||||
"compose": "Compose",
|
||||
"cancel": "Avbryt",
|
||||
"discard": "Discard",
|
||||
"try_again": "Försök igen",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "เสร็จสิ้น",
|
||||
"confirm": "ยืนยัน",
|
||||
"continue": "ดำเนินการต่อ",
|
||||
"compose": "เขียน",
|
||||
"cancel": "ยกเลิก",
|
||||
"discard": "ละทิ้ง",
|
||||
"try_again": "ลองอีกครั้ง",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "ไม่ได้แสดงผู้ติดตามจากเซิร์ฟเวอร์อื่น ๆ"
|
||||
},
|
||||
"following": {
|
||||
"footer": "ไม่ได้แสดงการติดตามจากเซิร์ฟเวอร์อื่น ๆ"
|
||||
},
|
||||
"search": {
|
||||
"title": "ค้นหา",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "完成",
|
||||
"confirm": "确认",
|
||||
"continue": "继续",
|
||||
"compose": "撰写",
|
||||
"cancel": "取消",
|
||||
"discard": "放弃",
|
||||
"try_again": "再试一次",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "不会显示来自其它服务器的关注者"
|
||||
},
|
||||
"following": {
|
||||
"footer": "不会显示来自其它服务器的关注"
|
||||
},
|
||||
"search": {
|
||||
"title": "搜索",
|
||||
"search_bar": {
|
||||
|
@ -67,6 +67,7 @@
|
||||
"done": "Done",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"compose": "Compose",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"try_again": "Try Again",
|
||||
@ -413,6 +414,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"follower": {
|
||||
"footer": "Followers from other servers are not displayed."
|
||||
},
|
||||
"following": {
|
||||
"footer": "Follows from other servers are not displayed."
|
||||
},
|
||||
"search": {
|
||||
"title": "Search",
|
||||
"search_bar": {
|
||||
|
@ -82,6 +82,7 @@
|
||||
"share_user": "Share %s",
|
||||
"share_post": "Share Post",
|
||||
"open_in_safari": "Open in Safari",
|
||||
"open_in_browser": "Open in Browser",
|
||||
"find_people": "Find people to follow",
|
||||
"manually_search": "Manually search instead",
|
||||
"skip": "Skip",
|
||||
|
@ -296,6 +296,11 @@
|
||||
DB59F10425EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F10325EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift */; };
|
||||
DB59F10E25EF724F001F1DAB /* APIService+Poll.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */; };
|
||||
DB59F11825EFA35B001F1DAB /* StripProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB59F11725EFA35B001F1DAB /* StripProgressView.swift */; };
|
||||
DB5B7295273112B100081888 /* FollowingListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5B7294273112B100081888 /* FollowingListViewController.swift */; };
|
||||
DB5B7298273112C800081888 /* FollowingListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5B7297273112C800081888 /* FollowingListViewModel.swift */; };
|
||||
DB5B729A2731137900081888 /* FollowingListViewController+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5B72992731137900081888 /* FollowingListViewController+Provider.swift */; };
|
||||
DB5B729C273113C200081888 /* FollowingListViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5B729B273113C200081888 /* FollowingListViewModel+Diffable.swift */; };
|
||||
DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB5B729D273113F300081888 /* FollowingListViewModel+State.swift */; };
|
||||
DB6180DD263918E30018D199 /* MediaPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180DC263918E30018D199 /* MediaPreviewViewController.swift */; };
|
||||
DB6180E02639194B0018D199 /* MediaPreviewPagingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180DF2639194B0018D199 /* MediaPreviewPagingViewController.swift */; };
|
||||
DB6180E326391A4C0018D199 /* ViewControllerAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180E226391A4C0018D199 /* ViewControllerAnimatedTransitioning.swift */; };
|
||||
@ -310,11 +315,13 @@
|
||||
DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180F726391D660018D199 /* MediaPreviewingViewController.swift */; };
|
||||
DB6180FA26391F2E0018D199 /* MediaPreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */; };
|
||||
DB63BE7F268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB63BE7E268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift */; };
|
||||
DB647C5726F1E97300F7F82C /* MainTabBarController+Wizard.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB647C5626F1E97300F7F82C /* MainTabBarController+Wizard.swift */; };
|
||||
DB647C5926F1EA2700F7F82C /* WizardPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB647C5826F1EA2700F7F82C /* WizardPreference.swift */; };
|
||||
DB66728C25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */; };
|
||||
DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729525F9F91600D60309 /* ComposeStatusSection.swift */; };
|
||||
DB66729C25F9F91F00D60309 /* ComposeStatusItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */; };
|
||||
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08327312970006A36CF /* APIService+Following.swift */; };
|
||||
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08527312E67006A36CF /* WizardViewController.swift */; };
|
||||
DB67D089273256D7006A36CF /* StoreReviewPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D088273256D7006A36CF /* StoreReviewPreference.swift */; };
|
||||
DB68045B2636DC6A00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||
DB68046C2636DC9E00430867 /* MastodonNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68045A2636DC6A00430867 /* MastodonNotification.swift */; };
|
||||
@ -370,6 +377,7 @@
|
||||
DB71FD5225F8CCAA00512AE1 /* APIService+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */; };
|
||||
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
|
||||
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
|
||||
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */; };
|
||||
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73B48F261F030A002E9E9F /* SafariActivity.swift */; };
|
||||
DB73BF3B2711885500781945 /* UserDefaults+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */; };
|
||||
DB73BF4127118B6D00781945 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4027118B6D00781945 /* Instance.swift */; };
|
||||
@ -600,6 +608,8 @@
|
||||
DBF1D24E269DAF5D00C1C08A /* SearchDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF1D24D269DAF5D00C1C08A /* SearchDetailViewController.swift */; };
|
||||
DBF1D251269DB01200C1C08A /* SearchHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF1D250269DB01200C1C08A /* SearchHistoryViewController.swift */; };
|
||||
DBF1D257269DBAC600C1C08A /* SearchDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF1D256269DBAC600C1C08A /* SearchDetailViewModel.swift */; };
|
||||
DBF3B73F2733EAED00E21627 /* local-codes.json in Resources */ = {isa = PBXBuildFile; fileRef = DBF3B73E2733EAED00E21627 /* local-codes.json */; };
|
||||
DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF3B7402733EB9400E21627 /* MastodonLocalCode.swift */; };
|
||||
DBF7A0FC26830C33004176A2 /* FPSIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = DBF7A0FB26830C33004176A2 /* FPSIndicator */; };
|
||||
DBF8AE16263293E400C9C23C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF8AE15263293E400C9C23C /* NotificationService.swift */; };
|
||||
DBF8AE1A263293E400C9C23C /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = DBF8AE13263293E400C9C23C /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@ -1110,6 +1120,11 @@
|
||||
DB59F10325EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCellHeightCacheableContainer.swift; sourceTree = "<group>"; };
|
||||
DB59F10D25EF724F001F1DAB /* APIService+Poll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Poll.swift"; sourceTree = "<group>"; };
|
||||
DB59F11725EFA35B001F1DAB /* StripProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StripProgressView.swift; sourceTree = "<group>"; };
|
||||
DB5B7294273112B100081888 /* FollowingListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingListViewController.swift; sourceTree = "<group>"; };
|
||||
DB5B7297273112C800081888 /* FollowingListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingListViewModel.swift; sourceTree = "<group>"; };
|
||||
DB5B72992731137900081888 /* FollowingListViewController+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowingListViewController+Provider.swift"; sourceTree = "<group>"; };
|
||||
DB5B729B273113C200081888 /* FollowingListViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowingListViewModel+Diffable.swift"; sourceTree = "<group>"; };
|
||||
DB5B729D273113F300081888 /* FollowingListViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowingListViewModel+State.swift"; sourceTree = "<group>"; };
|
||||
DB6180DC263918E30018D199 /* MediaPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewViewController.swift; sourceTree = "<group>"; };
|
||||
DB6180DF2639194B0018D199 /* MediaPreviewPagingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewPagingViewController.swift; sourceTree = "<group>"; };
|
||||
DB6180E226391A4C0018D199 /* ViewControllerAnimatedTransitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerAnimatedTransitioning.swift; sourceTree = "<group>"; };
|
||||
@ -1124,11 +1139,13 @@
|
||||
DB6180F726391D660018D199 /* MediaPreviewingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewingViewController.swift; sourceTree = "<group>"; };
|
||||
DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreviewViewModel.swift; sourceTree = "<group>"; };
|
||||
DB63BE7E268DD1070011D3F9 /* NotificationViewController+StatusProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationViewController+StatusProvider.swift"; sourceTree = "<group>"; };
|
||||
DB647C5626F1E97300F7F82C /* MainTabBarController+Wizard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainTabBarController+Wizard.swift"; sourceTree = "<group>"; };
|
||||
DB647C5826F1EA2700F7F82C /* WizardPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardPreference.swift; sourceTree = "<group>"; };
|
||||
DB66728B25F9F8DC00D60309 /* ComposeViewModel+DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ComposeViewModel+DataSource.swift"; sourceTree = "<group>"; };
|
||||
DB66729525F9F91600D60309 /* ComposeStatusSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusSection.swift; sourceTree = "<group>"; };
|
||||
DB66729B25F9F91F00D60309 /* ComposeStatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusItem.swift; sourceTree = "<group>"; };
|
||||
DB67D08327312970006A36CF /* APIService+Following.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Following.swift"; sourceTree = "<group>"; };
|
||||
DB67D08527312E67006A36CF /* WizardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardViewController.swift; sourceTree = "<group>"; };
|
||||
DB67D088273256D7006A36CF /* StoreReviewPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreReviewPreference.swift; sourceTree = "<group>"; };
|
||||
DB68045A2636DC6A00430867 /* MastodonNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonNotification.swift; sourceTree = "<group>"; };
|
||||
DB68047F2637CD4C00430867 /* AppShared.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppShared.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB6804812637CD4C00430867 /* AppShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppShared.h; sourceTree = "<group>"; };
|
||||
@ -1179,6 +1196,7 @@
|
||||
DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Status.swift"; sourceTree = "<group>"; };
|
||||
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
|
||||
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
|
||||
DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBatchFetchViewModel.swift; sourceTree = "<group>"; };
|
||||
DB73B48F261F030A002E9E9F /* SafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariActivity.swift; sourceTree = "<group>"; };
|
||||
DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Notification.swift"; sourceTree = "<group>"; };
|
||||
DB73BF4027118B6D00781945 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = "<group>"; };
|
||||
@ -1412,6 +1430,8 @@
|
||||
DBF1D24D269DAF5D00C1C08A /* SearchDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchDetailViewController.swift; sourceTree = "<group>"; };
|
||||
DBF1D250269DB01200C1C08A /* SearchHistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHistoryViewController.swift; sourceTree = "<group>"; };
|
||||
DBF1D256269DBAC600C1C08A /* SearchDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchDetailViewModel.swift; sourceTree = "<group>"; };
|
||||
DBF3B73E2733EAED00E21627 /* local-codes.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "local-codes.json"; sourceTree = "<group>"; };
|
||||
DBF3B7402733EB9400E21627 /* MastodonLocalCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLocalCode.swift; sourceTree = "<group>"; };
|
||||
DBF53F5F25C14E88008AAC7B /* Mastodon.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Mastodon.xctestplan; path = Mastodon/Mastodon.xctestplan; sourceTree = "<group>"; };
|
||||
DBF53F6025C14E9D008AAC7B /* MastodonSDK.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MastodonSDK.xctestplan; sourceTree = "<group>"; };
|
||||
DBF8AE13263293E400C9C23C /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -2198,6 +2218,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
164F0EBB267D4FE400249499 /* BoopSound.caf */,
|
||||
DBF3B73E2733EAED00E21627 /* local-codes.json */,
|
||||
DB427DDE25BAA00100D1B89D /* Assets.xcassets */,
|
||||
DB118A8125E4B6E600FAB162 /* Preview Assets.xcassets */,
|
||||
DB564BCE269F2F83001E39A7 /* Localizable.stringsdict */,
|
||||
@ -2316,6 +2337,7 @@
|
||||
2D34D9DA261494120081BFC0 /* APIService+Search.swift */,
|
||||
0F202212261351F5000C64BF /* APIService+HashtagTimeline.swift */,
|
||||
DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */,
|
||||
DB67D08327312970006A36CF /* APIService+Following.swift */,
|
||||
DBCC3B9426157E6E0045B23D /* APIService+Relationship.swift */,
|
||||
5B24BBE1262DB19100A9381B /* APIService+Report.swift */,
|
||||
DBAE3F932616E28B004B8251 /* APIService+Follow.swift */,
|
||||
@ -2434,6 +2456,7 @@
|
||||
DB1D842F26566512000346B3 /* KeyboardPreference.swift */,
|
||||
DBCBCC0C2680B908000F5B51 /* HomeTimelinePreference.swift */,
|
||||
DBD376AB2692ECDB007FEC24 /* ThemePreference.swift */,
|
||||
DB67D088273256D7006A36CF /* StoreReviewPreference.swift */,
|
||||
);
|
||||
path = Preference;
|
||||
sourceTree = "<group>";
|
||||
@ -2453,6 +2476,18 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB5B7296273112B400081888 /* Following */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB5B7294273112B100081888 /* FollowingListViewController.swift */,
|
||||
DB5B72992731137900081888 /* FollowingListViewController+Provider.swift */,
|
||||
DB5B7297273112C800081888 /* FollowingListViewModel.swift */,
|
||||
DB5B729B273113C200081888 /* FollowingListViewModel+Diffable.swift */,
|
||||
DB5B729D273113F300081888 /* FollowingListViewModel+State.swift */,
|
||||
);
|
||||
path = Following;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB6180DE263919350018D199 /* MediaPreview */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2504,6 +2539,14 @@
|
||||
path = Image;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB67D08727312E6A006A36CF /* Wizard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB67D08527312E67006A36CF /* WizardViewController.swift */,
|
||||
);
|
||||
path = Wizard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB6804802637CD4C00430867 /* AppShared */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2745,7 +2788,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB8AF54F25C13703002E6C99 /* MainTabBarController.swift */,
|
||||
DB647C5626F1E97300F7F82C /* MainTabBarController+Wizard.swift */,
|
||||
);
|
||||
path = MainTab;
|
||||
sourceTree = "<group>";
|
||||
@ -2757,6 +2799,7 @@
|
||||
DB6180E426391A500018D199 /* Transition */,
|
||||
DB852D1D26FB021900FC9D81 /* Root */,
|
||||
DB01409B25C40BB600F9F3CF /* Onboarding */,
|
||||
DB67D08727312E6A006A36CF /* Wizard */,
|
||||
DB9F58ED26EF435800E7BBE9 /* Account */,
|
||||
2D38F1D325CD463600561493 /* HomeTimeline */,
|
||||
2D76316325C14BAC00929FB9 /* PublicTimeline */,
|
||||
@ -2902,6 +2945,7 @@
|
||||
DBB5253B2611ECF5002F1F29 /* Timeline */,
|
||||
DBE3CDF1261C6B3100430CC6 /* Favorite */,
|
||||
DB6B74F0272FB55400C70B6E /* Follower */,
|
||||
DB5B7296273112B400081888 /* Following */,
|
||||
DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */,
|
||||
DBAE3F812615DDA3004B8251 /* ProfileViewController+UserProvider.swift */,
|
||||
DBB5255D2611F07A002F1F29 /* ProfileViewModel.swift */,
|
||||
@ -2930,6 +2974,7 @@
|
||||
DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */,
|
||||
2DA6055025F74407006356F9 /* AudioContainerViewModel.swift */,
|
||||
5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */,
|
||||
DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
@ -3109,6 +3154,7 @@
|
||||
DBBC24D626A54BCB00398BB9 /* MastodonRegex.swift */,
|
||||
DBBC24D826A54BCB00398BB9 /* MastodonMetricFormatter.swift */,
|
||||
DBFEF07626A691FB006D7ED1 /* MastodonAuthenticationBox.swift */,
|
||||
DBF3B7402733EB9400E21627 /* MastodonLocalCode.swift */,
|
||||
);
|
||||
path = Helper;
|
||||
sourceTree = "<group>";
|
||||
@ -3626,6 +3672,7 @@
|
||||
DB427DDF25BAA00100D1B89D /* Assets.xcassets in Resources */,
|
||||
DB427DDD25BAA00100D1B89D /* Main.storyboard in Resources */,
|
||||
DBA4B0F626C269880077136E /* Intents.stringsdict in Resources */,
|
||||
DBF3B73F2733EAED00E21627 /* local-codes.json in Resources */,
|
||||
DB118A8225E4B6E600FAB162 /* Preview Assets.xcassets in Resources */,
|
||||
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */,
|
||||
DB68A05D25E9055900CFDF14 /* Settings.bundle in Resources */,
|
||||
@ -3918,6 +3965,7 @@
|
||||
DBAE3F8E2616E0B1004B8251 /* APIService+Block.swift in Sources */,
|
||||
5DF1057F25F88A4100D6C0D4 /* TouchBlockingView.swift in Sources */,
|
||||
DB1D843426579931000346B3 /* TableViewControllerNavigateable.swift in Sources */,
|
||||
DB5B729A2731137900081888 /* FollowingListViewController+Provider.swift in Sources */,
|
||||
0FAA0FDF25E0B57E0017CCDE /* WelcomeViewController.swift in Sources */,
|
||||
2D206B8C25F6015000143C56 /* AudioPlaybackService.swift in Sources */,
|
||||
2D59819B25E4A581000FB903 /* MastodonConfirmEmailViewController.swift in Sources */,
|
||||
@ -3929,6 +3977,7 @@
|
||||
DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */,
|
||||
DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */,
|
||||
DBCC3B9526157E6E0045B23D /* APIService+Relationship.swift in Sources */,
|
||||
DB5B7298273112C800081888 /* FollowingListViewModel.swift in Sources */,
|
||||
2D7631B325C159F700929FB9 /* Item.swift in Sources */,
|
||||
5DF1054125F886D400D6C0D4 /* VideoPlaybackService.swift in Sources */,
|
||||
DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */,
|
||||
@ -4065,6 +4114,8 @@
|
||||
DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */,
|
||||
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */,
|
||||
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
|
||||
DB67D089273256D7006A36CF /* StoreReviewPreference.swift in Sources */,
|
||||
DB5B7295273112B100081888 /* FollowingListViewController.swift in Sources */,
|
||||
0F202201261326E6000C64BF /* HashtagTimelineViewModel.swift in Sources */,
|
||||
DB6D9F9726367249008423CD /* SettingsViewController.swift in Sources */,
|
||||
DB4F097F26A03DA600D62E92 /* SearchHistoryFetchedResultController.swift in Sources */,
|
||||
@ -4075,8 +4126,10 @@
|
||||
DB73BF47271199CA00781945 /* Instance.swift in Sources */,
|
||||
DB0F8150264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift in Sources */,
|
||||
DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */,
|
||||
DB5B729C273113C200081888 /* FollowingListViewModel+Diffable.swift in Sources */,
|
||||
DB9E0D6F25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift in Sources */,
|
||||
DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */,
|
||||
DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */,
|
||||
2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
|
||||
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */,
|
||||
DB59F11825EFA35B001F1DAB /* StripProgressView.swift in Sources */,
|
||||
@ -4097,6 +4150,7 @@
|
||||
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */,
|
||||
5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */,
|
||||
DB73BF43271192BB00781945 /* InstanceService.swift in Sources */,
|
||||
DB67D08427312970006A36CF /* APIService+Following.swift in Sources */,
|
||||
DBA9443A265CC0FC00C537E1 /* Fields.swift in Sources */,
|
||||
2DE0FAC12615F04D00CDF649 /* RecommendHashTagSection.swift in Sources */,
|
||||
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */,
|
||||
@ -4182,6 +4236,7 @@
|
||||
2D24E11D2626D8B100A59D4F /* NotificationStatusTableViewCell.swift in Sources */,
|
||||
DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */,
|
||||
DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */,
|
||||
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */,
|
||||
2D61254D262547C200299647 /* APIService+Notification.swift in Sources */,
|
||||
DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */,
|
||||
DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */,
|
||||
@ -4326,6 +4381,7 @@
|
||||
DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */,
|
||||
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
|
||||
DBBC24C026A5443100398BB9 /* MastodonTheme.swift in Sources */,
|
||||
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */,
|
||||
DBBC24B526A540AE00398BB9 /* AvatarConfigurableView.swift in Sources */,
|
||||
DB9A489026035963008B817C /* APIService+Media.swift in Sources */,
|
||||
DBFEF07726A691FB006D7ED1 /* MastodonAuthenticationBox.swift in Sources */,
|
||||
@ -4339,7 +4395,6 @@
|
||||
DB49A62525FF334C00B98345 /* EmojiService+CustomEmojiViewModel+LoadState.swift in Sources */,
|
||||
DB4924E226312AB200E9DB22 /* NotificationService.swift in Sources */,
|
||||
DB6D9F6F2635807F008423CD /* Setting.swift in Sources */,
|
||||
DB647C5726F1E97300F7F82C /* MainTabBarController+Wizard.swift in Sources */,
|
||||
DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */,
|
||||
DBB525412611ED54002F1F29 /* ProfileHeaderViewController.swift in Sources */,
|
||||
DB6B74F8272FBFB100C70B6E /* FollowerListViewController+Provider.swift in Sources */,
|
||||
@ -4356,6 +4411,7 @@
|
||||
2D38F1FE25CD481700561493 /* StatusProvider.swift in Sources */,
|
||||
DB1EE7B2267F9525000CC337 /* StatusProvider+StatusNodeDelegate.swift in Sources */,
|
||||
5B24BBE2262DB19100A9381B /* APIService+Report.swift in Sources */,
|
||||
DBF3B7412733EB9400E21627 /* MastodonLocalCode.swift in Sources */,
|
||||
DB4F096A269EDAD200D62E92 /* SearchResultViewModel+State.swift in Sources */,
|
||||
5BB04FF5262F0E6D0043BFF6 /* ReportSection.swift in Sources */,
|
||||
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */,
|
||||
@ -4863,7 +4919,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
@ -4878,7 +4934,7 @@
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@ -4892,7 +4948,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
@ -4906,7 +4962,7 @@
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
@ -5000,11 +5056,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5031,11 +5087,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5060,11 +5116,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5090,11 +5146,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5157,7 +5213,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5171,7 +5227,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@ -5182,7 +5238,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5196,7 +5252,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Debug";
|
||||
@ -5207,7 +5263,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5221,7 +5277,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Release";
|
||||
@ -5232,7 +5288,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = MastodonIntent/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5246,7 +5302,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
@ -5257,7 +5313,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5271,7 +5327,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@ -5282,7 +5338,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5296,7 +5352,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Debug";
|
||||
@ -5307,7 +5363,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5321,7 +5377,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Release";
|
||||
@ -5332,7 +5388,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = ShareActionExtension/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5346,7 +5402,7 @@
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
@ -5423,7 +5479,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
@ -5438,7 +5494,7 @@
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Release";
|
||||
@ -5490,11 +5546,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5539,7 +5595,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5552,7 +5608,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Release";
|
||||
@ -5564,11 +5620,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5660,7 +5716,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = Mastodon/Info.plist;
|
||||
@ -5675,7 +5731,7 @@
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Debug";
|
||||
@ -5727,11 +5783,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = CoreDataStack/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5776,7 +5832,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5789,7 +5845,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = "ASDK - Debug";
|
||||
@ -5801,11 +5857,11 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 82;
|
||||
DYLIB_CURRENT_VERSION = 88;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = AppShared/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
@ -5831,7 +5887,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5844,7 +5900,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@ -5855,7 +5911,7 @@
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 82;
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 5Z4GVSS33P;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -5868,7 +5924,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
@ -6026,7 +6082,7 @@
|
||||
repositoryURL = "https://github.com/TwidereProject/MetaTextKit.git";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 2.1.1;
|
||||
version = 2.1.2;
|
||||
};
|
||||
};
|
||||
DB0E2D2C26833FF600865C3C /* XCRemoteSwiftPackageReference "Nuke-FLAnimatedImage-Plugin" */ = {
|
||||
|
@ -7,87 +7,92 @@
|
||||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>42</integer>
|
||||
<integer>44</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>43</integer>
|
||||
<integer>45</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>Mastodon - RTL.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>13</integer>
|
||||
<integer>17</integer>
|
||||
</dict>
|
||||
<key>Mastodon - Release.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ar.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>10</integer>
|
||||
<integer>11</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ca.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>16</integer>
|
||||
<integer>23</integer>
|
||||
</dict>
|
||||
<key>Mastodon - de.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>11</integer>
|
||||
<integer>13</integer>
|
||||
</dict>
|
||||
<key>Mastodon - en.xcscheme_^#shared#^_</key>
|
||||
<key>Mastodon - double length.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>Mastodon - en.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>Mastodon - es-419.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>Mastodon - es.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>Mastodon - fr.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>9</integer>
|
||||
</dict>
|
||||
<key>Mastodon - es.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
<key>Mastodon - fr.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
<key>Mastodon - jp.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>14</integer>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>Mastodon - nl.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>12</integer>
|
||||
<integer>15</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ru.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>Mastodon - th.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>Mastodon - zh_Hans.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>15</integer>
|
||||
<integer>21</integer>
|
||||
</dict>
|
||||
<key>Mastodon.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
@ -97,7 +102,7 @@
|
||||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>44</integer>
|
||||
<integer>43</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
@ -112,12 +117,12 @@
|
||||
<key>NotificationService.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>41</integer>
|
||||
<integer>42</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
@ -96,8 +96,8 @@
|
||||
"repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "a5f412b72fc08cd1348e2388fc7ec326365e1823",
|
||||
"version": "2.1.1"
|
||||
"revision": "7af4182f64329440a4656f2cba307cb5848e496a",
|
||||
"version": "2.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ final public class SceneCoordinator {
|
||||
|
||||
private(set) weak var tabBarController: MainTabBarController!
|
||||
private(set) weak var splitViewController: RootSplitViewController?
|
||||
private(set) var wizardViewController: WizardViewController?
|
||||
|
||||
private(set) var secondaryStackHashValues = Set<Int>()
|
||||
|
||||
@ -179,6 +180,7 @@ extension SceneCoordinator {
|
||||
case profile(viewModel: ProfileViewModel)
|
||||
case favorite(viewModel: FavoriteViewModel)
|
||||
case follower(viewModel: FollowerListViewModel)
|
||||
case following(viewModel: FollowingListViewModel)
|
||||
|
||||
// setting
|
||||
case settings(viewModel: SettingsViewModel)
|
||||
@ -220,17 +222,34 @@ extension SceneCoordinator {
|
||||
extension SceneCoordinator {
|
||||
|
||||
func setup() {
|
||||
let rootViewController: UIViewController
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .phone:
|
||||
let viewController = MainTabBarController(context: appContext, coordinator: self)
|
||||
sceneDelegate.window?.rootViewController = viewController
|
||||
tabBarController = viewController
|
||||
self.splitViewController = nil
|
||||
self.tabBarController = viewController
|
||||
rootViewController = viewController
|
||||
default:
|
||||
let splitViewController = RootSplitViewController(context: appContext, coordinator: self)
|
||||
self.splitViewController = splitViewController
|
||||
self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController
|
||||
sceneDelegate.window?.rootViewController = splitViewController
|
||||
rootViewController = splitViewController
|
||||
}
|
||||
|
||||
let wizardViewController = WizardViewController()
|
||||
if !wizardViewController.items.isEmpty,
|
||||
let delegate = rootViewController as? WizardViewControllerDelegate
|
||||
{
|
||||
// do not add as child view controller.
|
||||
// otherwise, the tab bar controller will add as a new tab
|
||||
wizardViewController.delegate = delegate
|
||||
wizardViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
wizardViewController.view.frame = rootViewController.view.bounds
|
||||
rootViewController.view.addSubview(wizardViewController.view)
|
||||
self.wizardViewController = wizardViewController
|
||||
}
|
||||
|
||||
sceneDelegate.window?.rootViewController = rootViewController
|
||||
}
|
||||
|
||||
func setupOnboardingIfNeeds(animated: Bool) {
|
||||
@ -429,6 +448,10 @@ private extension SceneCoordinator {
|
||||
let _viewController = FollowerListViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .following(let viewModel):
|
||||
let _viewController = FollowingListViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
viewController = _viewController
|
||||
case .suggestionAccount(let viewModel):
|
||||
let _viewController = SuggestionAccountViewController()
|
||||
_viewController.viewModel = viewModel
|
||||
|
@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
enum NotificationItem {
|
||||
case notification(objectID: NSManagedObjectID, attribute: Item.StatusAttribute)
|
||||
|
||||
case notificationStatus(objectID: NSManagedObjectID, attribute: Item.StatusAttribute) // display notification status without card wrapper
|
||||
case bottomLoader
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ extension NotificationItem: Equatable {
|
||||
switch (lhs, rhs) {
|
||||
case (.notification(let idLeft, _), .notification(let idRight, _)):
|
||||
return idLeft == idRight
|
||||
case (.notificationStatus(let idLeft, _), .notificationStatus(let idRight, _)):
|
||||
return idLeft == idRight
|
||||
case (.bottomLoader, .bottomLoader):
|
||||
return true
|
||||
default:
|
||||
@ -32,6 +34,8 @@ extension NotificationItem: Hashable {
|
||||
switch self {
|
||||
case .notification(let id, _):
|
||||
hasher.combine(id)
|
||||
case .notificationStatus(let id, _):
|
||||
hasher.combine(id)
|
||||
case .bottomLoader:
|
||||
hasher.combine(String(describing: NotificationItem.bottomLoader.self))
|
||||
}
|
||||
@ -43,6 +47,8 @@ extension NotificationItem {
|
||||
switch self {
|
||||
case .notification(let objectID, _):
|
||||
return .mastodonNotification(objectID: objectID)
|
||||
case .notificationStatus(let objectID, _):
|
||||
return .mastodonNotification(objectID: objectID)
|
||||
case .bottomLoader:
|
||||
return nil
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import CoreData
|
||||
|
||||
enum UserItem: Hashable {
|
||||
case follower(objectID: NSManagedObjectID)
|
||||
case following(objectID: NSManagedObjectID)
|
||||
case bottomLoader
|
||||
case bottomHeader(text: String)
|
||||
}
|
||||
|
@ -21,9 +21,10 @@ enum NotificationSection: Equatable, Hashable {
|
||||
extension NotificationSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
delegate: NotificationTableViewCellDelegate,
|
||||
dependency: NeedsDependency
|
||||
statusTableViewCellDelegate: StatusTableViewCellDelegate
|
||||
) -> UITableViewDiffableDataSource<NotificationSection, NotificationItem> {
|
||||
UITableViewDiffableDataSource(tableView: tableView) {
|
||||
[weak delegate, weak dependency]
|
||||
@ -32,137 +33,47 @@ extension NotificationSection {
|
||||
switch notificationItem {
|
||||
case .notification(let objectID, let attribute):
|
||||
guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification,
|
||||
!notification.isDeleted else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
!notification.isDeleted
|
||||
else { return UITableViewCell() }
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: NotificationStatusTableViewCell.self), for: indexPath) as! NotificationStatusTableViewCell
|
||||
cell.delegate = delegate
|
||||
|
||||
// configure author
|
||||
cell.configure(
|
||||
with: AvatarConfigurableViewConfiguration(
|
||||
avatarImageURL: notification.account.avatarImageURL()
|
||||
)
|
||||
configure(
|
||||
tableView: tableView,
|
||||
cell: cell,
|
||||
notification: notification,
|
||||
dependency: dependency,
|
||||
attribute: attribute
|
||||
)
|
||||
cell.delegate = delegate
|
||||
cell.isAccessibilityElement = true
|
||||
NotificationSection.configureStatusAccessibilityLabel(cell: cell)
|
||||
return cell
|
||||
|
||||
func createActionImage() -> UIImage? {
|
||||
return UIImage(
|
||||
systemName: notification.notificationType.actionImageName,
|
||||
withConfiguration: UIImage.SymbolConfiguration(
|
||||
pointSize: 12, weight: .semibold
|
||||
)
|
||||
)?
|
||||
.withTintColor(.systemBackground)
|
||||
.af.imageAspectScaled(toFit: CGSize(width: 14, height: 14))
|
||||
}
|
||||
case .notificationStatus(objectID: let objectID, attribute: let attribute):
|
||||
guard let notification = try? managedObjectContext.existingObject(with: objectID) as? MastodonNotification,
|
||||
!notification.isDeleted,
|
||||
let status = notification.status,
|
||||
let requestUserID = dependency.context.authenticationService.activeMastodonAuthenticationBox.value?.userID
|
||||
else { return UITableViewCell() }
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell
|
||||
|
||||
cell.avatarButton.badgeImageView.backgroundColor = notification.notificationType.color
|
||||
cell.avatarButton.badgeImageView.image = createActionImage()
|
||||
cell.traitCollectionDidChange
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] in
|
||||
guard let cell = cell else { return }
|
||||
cell.avatarButton.badgeImageView.image = createActionImage()
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// configure author name, notification description, timestamp
|
||||
let nameText = notification.account.displayNameWithFallback
|
||||
let titleLabelText: String = {
|
||||
switch notification.notificationType {
|
||||
case .favourite: return L10n.Scene.Notification.userFavoritedYourPost(nameText)
|
||||
case .follow: return L10n.Scene.Notification.userFollowedYou(nameText)
|
||||
case .followRequest: return L10n.Scene.Notification.userRequestedToFollowYou(nameText)
|
||||
case .mention: return L10n.Scene.Notification.userMentionedYou(nameText)
|
||||
case .poll: return L10n.Scene.Notification.userYourPollHasEnded(nameText)
|
||||
case .reblog: return L10n.Scene.Notification.userRebloggedYourPost(nameText)
|
||||
default: return ""
|
||||
}
|
||||
}()
|
||||
|
||||
do {
|
||||
let nameContent = MastodonContent(content: nameText, emojis: notification.account.emojiMeta)
|
||||
let nameMetaContent = try MastodonMetaContent.convert(document: nameContent)
|
||||
|
||||
let mastodonContent = MastodonContent(content: titleLabelText, emojis: notification.account.emojiMeta)
|
||||
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
||||
|
||||
cell.titleLabel.configure(content: metaContent)
|
||||
|
||||
if let nameRange = metaContent.string.range(of: nameMetaContent.string) {
|
||||
let nsRange = NSRange(nameRange, in: metaContent.string)
|
||||
cell.titleLabel.textStorage.addAttributes([
|
||||
.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20),
|
||||
.foregroundColor: Asset.Colors.brandBlue.color,
|
||||
], range: nsRange)
|
||||
}
|
||||
|
||||
} catch {
|
||||
let metaContent = PlaintextMetaContent(string: titleLabelText)
|
||||
cell.titleLabel.configure(content: metaContent)
|
||||
}
|
||||
|
||||
let createAt = notification.createAt
|
||||
cell.timestampLabel.text = createAt.localizedSlowedTimeAgoSinceNow
|
||||
AppContext.shared.timestampUpdatePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.timestampLabel.text = createAt.localizedSlowedTimeAgoSinceNow
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// configure follow request (if exist)
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.acceptButton.publisher(for: .touchUpInside)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.delegate?.notificationTableViewCell(cell, notification: notification, acceptButtonDidPressed: cell.acceptButton)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.rejectButton.publisher(for: .touchUpInside)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.delegate?.notificationTableViewCell(cell, notification: notification, rejectButtonDidPressed: cell.rejectButton)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.buttonStackView.isHidden = false
|
||||
} else {
|
||||
cell.buttonStackView.isHidden = true
|
||||
}
|
||||
|
||||
// configure status (if exist)
|
||||
if let status = notification.status {
|
||||
let frame = CGRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right,
|
||||
height: tableView.readableContentGuide.layoutFrame.height
|
||||
)
|
||||
StatusSection.configure(
|
||||
cell: cell,
|
||||
tableView: tableView,
|
||||
timelineContext: .notifications,
|
||||
dependency: dependency,
|
||||
readableLayoutFrame: frame,
|
||||
status: status,
|
||||
requestUserID: notification.userID,
|
||||
statusItemAttribute: attribute
|
||||
)
|
||||
cell.statusContainerView.isHidden = false
|
||||
cell.containerStackView.alignment = .top
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 0
|
||||
} else {
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.containerStackView.alignment = .top
|
||||
} else {
|
||||
cell.containerStackView.alignment = .center
|
||||
}
|
||||
cell.statusContainerView.isHidden = true
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 5 // 5pt margin when no status view
|
||||
}
|
||||
|
||||
// configure cell
|
||||
StatusSection.configureStatusTableViewCell(
|
||||
cell: cell,
|
||||
tableView: tableView,
|
||||
timelineContext: .notifications,
|
||||
dependency: dependency,
|
||||
readableLayoutFrame: tableView.readableContentGuide.layoutFrame,
|
||||
status: status,
|
||||
requestUserID: requestUserID,
|
||||
statusItemAttribute: attribute
|
||||
)
|
||||
cell.statusView.headerContainerView.isHidden = true // set header hide
|
||||
cell.statusView.actionToolbarContainer.isHidden = true // set toolbar hide
|
||||
cell.statusView.actionToolbarPlaceholderPaddingView.isHidden = false
|
||||
cell.delegate = statusTableViewCellDelegate
|
||||
cell.isAccessibilityElement = true
|
||||
StatusSection.configureStatusAccessibilityLabel(cell: cell)
|
||||
return cell
|
||||
|
||||
case .bottomLoader:
|
||||
@ -174,3 +85,162 @@ extension NotificationSection {
|
||||
}
|
||||
}
|
||||
|
||||
extension NotificationSection {
|
||||
static func configure(
|
||||
tableView: UITableView,
|
||||
cell: NotificationStatusTableViewCell,
|
||||
notification: MastodonNotification,
|
||||
dependency: NeedsDependency,
|
||||
attribute: Item.StatusAttribute
|
||||
) {
|
||||
// configure author
|
||||
cell.configure(
|
||||
with: AvatarConfigurableViewConfiguration(
|
||||
avatarImageURL: notification.account.avatarImageURL()
|
||||
)
|
||||
)
|
||||
|
||||
func createActionImage() -> UIImage? {
|
||||
return UIImage(
|
||||
systemName: notification.notificationType.actionImageName,
|
||||
withConfiguration: UIImage.SymbolConfiguration(
|
||||
pointSize: 12, weight: .semibold
|
||||
)
|
||||
)?
|
||||
.withTintColor(.systemBackground)
|
||||
.af.imageAspectScaled(toFit: CGSize(width: 14, height: 14))
|
||||
}
|
||||
|
||||
cell.avatarButton.badgeImageView.backgroundColor = notification.notificationType.color
|
||||
cell.avatarButton.badgeImageView.image = createActionImage()
|
||||
cell.traitCollectionDidChange
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] in
|
||||
guard let cell = cell else { return }
|
||||
cell.avatarButton.badgeImageView.image = createActionImage()
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// configure author name, notification description, timestamp
|
||||
let nameText = notification.account.displayNameWithFallback
|
||||
let titleLabelText: String = {
|
||||
switch notification.notificationType {
|
||||
case .favourite: return L10n.Scene.Notification.userFavoritedYourPost(nameText)
|
||||
case .follow: return L10n.Scene.Notification.userFollowedYou(nameText)
|
||||
case .followRequest: return L10n.Scene.Notification.userRequestedToFollowYou(nameText)
|
||||
case .mention: return L10n.Scene.Notification.userMentionedYou(nameText)
|
||||
case .poll: return L10n.Scene.Notification.userYourPollHasEnded(nameText)
|
||||
case .reblog: return L10n.Scene.Notification.userRebloggedYourPost(nameText)
|
||||
default: return ""
|
||||
}
|
||||
}()
|
||||
|
||||
do {
|
||||
let nameContent = MastodonContent(content: nameText, emojis: notification.account.emojiMeta)
|
||||
let nameMetaContent = try MastodonMetaContent.convert(document: nameContent)
|
||||
|
||||
let mastodonContent = MastodonContent(content: titleLabelText, emojis: notification.account.emojiMeta)
|
||||
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
||||
|
||||
cell.titleLabel.configure(content: metaContent)
|
||||
|
||||
if let nameRange = metaContent.string.range(of: nameMetaContent.string) {
|
||||
let nsRange = NSRange(nameRange, in: metaContent.string)
|
||||
cell.titleLabel.textStorage.addAttributes([
|
||||
.font: UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold), maximumPointSize: 20),
|
||||
.foregroundColor: Asset.Colors.brandBlue.color,
|
||||
], range: nsRange)
|
||||
}
|
||||
|
||||
} catch {
|
||||
let metaContent = PlaintextMetaContent(string: titleLabelText)
|
||||
cell.titleLabel.configure(content: metaContent)
|
||||
}
|
||||
|
||||
let createAt = notification.createAt
|
||||
cell.timestampLabel.text = createAt.localizedSlowedTimeAgoSinceNow
|
||||
AppContext.shared.timestampUpdatePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.timestampLabel.text = createAt.localizedSlowedTimeAgoSinceNow
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// configure follow request (if exist)
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.acceptButton.publisher(for: .touchUpInside)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.delegate?.notificationTableViewCell(cell, notification: notification, acceptButtonDidPressed: cell.acceptButton)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.rejectButton.publisher(for: .touchUpInside)
|
||||
.sink { [weak cell] _ in
|
||||
guard let cell = cell else { return }
|
||||
cell.delegate?.notificationTableViewCell(cell, notification: notification, rejectButtonDidPressed: cell.rejectButton)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
cell.buttonStackView.isHidden = false
|
||||
} else {
|
||||
cell.buttonStackView.isHidden = true
|
||||
}
|
||||
|
||||
// configure status (if exist)
|
||||
if let status = notification.status {
|
||||
let frame = CGRect(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right,
|
||||
height: tableView.readableContentGuide.layoutFrame.height
|
||||
)
|
||||
StatusSection.configure(
|
||||
cell: cell,
|
||||
tableView: tableView,
|
||||
timelineContext: .notifications,
|
||||
dependency: dependency,
|
||||
readableLayoutFrame: frame,
|
||||
status: status,
|
||||
requestUserID: notification.userID,
|
||||
statusItemAttribute: attribute
|
||||
)
|
||||
cell.statusContainerView.isHidden = false
|
||||
cell.containerStackView.alignment = .top
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 0
|
||||
} else {
|
||||
if case .followRequest = notification.notificationType {
|
||||
cell.containerStackView.alignment = .top
|
||||
} else {
|
||||
cell.containerStackView.alignment = .center
|
||||
}
|
||||
cell.statusContainerView.isHidden = true
|
||||
cell.containerStackViewBottomLayoutConstraint.constant = 5 // 5pt margin when no status view
|
||||
}
|
||||
}
|
||||
|
||||
static func configureStatusAccessibilityLabel(cell: NotificationStatusTableViewCell) {
|
||||
// FIXME:
|
||||
cell.accessibilityLabel = {
|
||||
var accessibilityViews: [UIView?] = []
|
||||
accessibilityViews.append(contentsOf: [
|
||||
cell.titleLabel,
|
||||
cell.timestampLabel,
|
||||
cell.statusView
|
||||
])
|
||||
if !cell.statusContainerView.isHidden {
|
||||
if !cell.statusView.headerContainerView.isHidden {
|
||||
accessibilityViews.append(cell.statusView.headerInfoLabel)
|
||||
}
|
||||
accessibilityViews.append(contentsOf: [
|
||||
cell.statusView.nameMetaLabel,
|
||||
cell.statusView.dateLabel,
|
||||
cell.statusView.contentMetaText.textView,
|
||||
])
|
||||
}
|
||||
return accessibilityViews
|
||||
.compactMap { $0?.accessibilityLabel }
|
||||
.joined(separator: " ")
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,15 @@ extension PollSection {
|
||||
managedObjectContext.performAndWait {
|
||||
let option = managedObjectContext.object(with: objectID) as! PollOption
|
||||
PollSection.configure(cell: cell, pollOption: option, pollItemAttribute: attribute)
|
||||
|
||||
cell.isAccessibilityElement = true
|
||||
cell.accessibilityLabel = {
|
||||
var labels: [String] = [option.title]
|
||||
if let percentage = cell.pollOptionView.optionPercentageLabel.text {
|
||||
labels.append(percentage)
|
||||
}
|
||||
return labels.joined(separator: ",")
|
||||
}()
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
@ -158,6 +158,11 @@ extension StatusSection {
|
||||
accessibilityElements.append(cell.statusView.avatarView)
|
||||
accessibilityElements.append(cell.statusView.nameMetaLabel)
|
||||
accessibilityElements.append(cell.statusView.dateLabel)
|
||||
// poll
|
||||
accessibilityElements.append(cell.statusView.pollTableView)
|
||||
accessibilityElements.append(cell.statusView.pollVoteCountLabel)
|
||||
accessibilityElements.append(cell.statusView.pollCountdownLabel)
|
||||
accessibilityElements.append(cell.statusView.pollVoteButton)
|
||||
// TODO: a11y
|
||||
accessibilityElements.append(cell.statusView.contentMetaText.textView)
|
||||
accessibilityElements.append(contentsOf: cell.statusView.statusMosaicImageViewContainer.imageViews)
|
||||
@ -389,7 +394,7 @@ extension StatusSection {
|
||||
// set timestamp
|
||||
let createdAt = (status.reblog ?? status).createdAt
|
||||
cell.statusView.dateLabel.text = createdAt.localizedSlowedTimeAgoSinceNow
|
||||
cell.statusView.dateLabel.accessibilityValue = createdAt.timeAgoSinceNow
|
||||
cell.statusView.dateLabel.accessibilityLabel = createdAt.timeAgoSinceNow
|
||||
AppContext.shared.timestampUpdatePublisher
|
||||
.receive(on: RunLoop.main) // will be paused when scrolling (on purpose)
|
||||
.sink { [weak cell] _ in
|
||||
@ -978,6 +983,7 @@ extension StatusSection {
|
||||
cell.statusView.pollCountdownLabel.text = "-"
|
||||
}
|
||||
|
||||
cell.statusView.isUserInteractionEnabled = !poll.expired // make voice over touch passthroughable
|
||||
cell.statusView.pollTableView.allowsSelection = !poll.expired
|
||||
|
||||
let votedOptions = poll.options.filter { option in
|
||||
|
@ -30,7 +30,8 @@ extension UserSection {
|
||||
] tableView, indexPath, item -> UITableViewCell? in
|
||||
guard let dependency = dependency else { return UITableViewCell() }
|
||||
switch item {
|
||||
case .follower(let objectID):
|
||||
case .follower(let objectID),
|
||||
.following(let objectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UserTableViewCell.self), for: indexPath) as! UserTableViewCell
|
||||
managedObjectContext.performAndWait {
|
||||
let user = managedObjectContext.object(with: objectID) as! MastodonUser
|
||||
|
@ -106,6 +106,8 @@ internal enum L10n {
|
||||
}
|
||||
/// Cancel
|
||||
internal static let cancel = L10n.tr("Localizable", "Common.Controls.Actions.Cancel")
|
||||
/// Compose
|
||||
internal static let compose = L10n.tr("Localizable", "Common.Controls.Actions.Compose")
|
||||
/// Confirm
|
||||
internal static let confirm = L10n.tr("Localizable", "Common.Controls.Actions.Confirm")
|
||||
/// Continue
|
||||
@ -523,6 +525,14 @@ internal enum L10n {
|
||||
/// Your Favorites
|
||||
internal static let title = L10n.tr("Localizable", "Scene.Favorite.Title")
|
||||
}
|
||||
internal enum Follower {
|
||||
/// Followers from other servers are not displayed.
|
||||
internal static let footer = L10n.tr("Localizable", "Scene.Follower.Footer")
|
||||
}
|
||||
internal enum Following {
|
||||
/// Follows from other servers are not displayed.
|
||||
internal static let footer = L10n.tr("Localizable", "Scene.Following.Footer")
|
||||
}
|
||||
internal enum HomeTimeline {
|
||||
/// Home
|
||||
internal static let title = L10n.tr("Localizable", "Scene.HomeTimeline.Title")
|
||||
|
12
Mastodon/Helper/MastodonLocalCode.swift
Normal file
12
Mastodon/Helper/MastodonLocalCode.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// MastodonLocalCode.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-4.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// https://github.com/gunchleoc/mastodon/blob/ed6153b8f24d3a8f5a124cc95683bd1f20aec882/app/helpers/settings_helper.rb
|
||||
// last update 2021/11/4
|
||||
typealias MastodonLocalCode = [String: String]
|
@ -30,7 +30,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>82</string>
|
||||
<string>88</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
|
26
Mastodon/Preference/StoreReviewPreference.swift
Normal file
26
Mastodon/Preference/StoreReviewPreference.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// StoreReviewPreference.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UserDefaults {
|
||||
|
||||
@objc dynamic var processCompletedCount: Int {
|
||||
get {
|
||||
return integer(forKey: #function)
|
||||
}
|
||||
set { self[#function] = newValue }
|
||||
}
|
||||
|
||||
@objc dynamic var lastVersionPromptedForReview: String? {
|
||||
get {
|
||||
return string(forKey: #function)
|
||||
}
|
||||
set { self[#function] = newValue }
|
||||
}
|
||||
|
||||
}
|
@ -1,62 +1,63 @@
|
||||
"Common.Alerts.BlockDomain.BlockEntireDomain" = "حظر النطاق";
|
||||
"Common.Alerts.BlockDomain.Title" = "Are you really, really sure you want to block the entire %@? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.";
|
||||
"Common.Alerts.BlockDomain.BlockEntireDomain" = "حظر النِطاق";
|
||||
"Common.Alerts.BlockDomain.Title" = "هل أنتَ مُتأكِّدٌ حقًا مِن رغبتك في حظر %@ بالكامل؟ في معظم الحالات، يكون مِنَ الكافي والمُفَضَّل استهداف عدد محدود للحظر أو الكتم. لن ترى محتوى من هذا النطاق وسوف يتم إزالة جميع متابعيك المتواجدين فيه.";
|
||||
"Common.Alerts.CleanCache.Message" = "تمَّ مَحو ذاكرة التخزين المؤقت %@ بنجاح.";
|
||||
"Common.Alerts.CleanCache.Title" = "مَحو ذاكرة التخزين المؤقت";
|
||||
"Common.Alerts.Common.PleaseTryAgain" = "يُرجى المحاولة مرة أُخرى.";
|
||||
"Common.Alerts.Common.PleaseTryAgainLater" = "يُرجى المحاولة مرة أُخرى لاحقاً.";
|
||||
"Common.Alerts.DeletePost.Delete" = "احذف";
|
||||
"Common.Alerts.DeletePost.Title" = "هل أنت متأكد من أنك تريد حذف هذا المنشور؟";
|
||||
"Common.Alerts.DiscardPostContent.Message" = "Confirm to discard composed post content.";
|
||||
"Common.Alerts.DiscardPostContent.Title" = "تجاهل المسودة";
|
||||
"Common.Alerts.DeletePost.Title" = "هل أنت متأكد من رغبتك في حذف هذا المنشور؟";
|
||||
"Common.Alerts.DiscardPostContent.Message" = "أكِّد للتخلص مِن مُحتوى مَنشور مؤلَّف.";
|
||||
"Common.Alerts.DiscardPostContent.Title" = "التخلص من المسودة";
|
||||
"Common.Alerts.EditProfileFailure.Message" = "لا يمكن تعديل الملف الشخصي. يُرجى المحاولة مرة أُخرى.";
|
||||
"Common.Alerts.EditProfileFailure.Title" = "خطأ في تَحرير الملف الشخصي";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo" = "Cannot attach more than one video.";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto" = "Cannot attach a video to a post that already contains images.";
|
||||
"Common.Alerts.PublishPostFailure.Message" = "Failed to publish the post.
|
||||
Please check your internet connection.";
|
||||
"Common.Alerts.PublishPostFailure.Title" = "أخفقت عملية النشر";
|
||||
"Common.Alerts.SavePhotoFailure.Message" = "Please enable the photo library access permission to save the photo.";
|
||||
"Common.Alerts.SavePhotoFailure.Title" = "فشل حفظ الصورة";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo" = "لا يُمكِنُ إرفاق أكثر مِن مَقطع مرئي واحِد.";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto" = "لا يُمكن إرفاق مقطع مرئي إلى مَنشور يحتوي بالفعل على صُوَر.";
|
||||
"Common.Alerts.PublishPostFailure.Message" = "فَشَلَ نَشر المَنشور.
|
||||
يُرجى التحقق من اتصالك بالإنترنت.";
|
||||
"Common.Alerts.PublishPostFailure.Title" = "إخفاق في عمليَّة النشر";
|
||||
"Common.Alerts.SavePhotoFailure.Message" = "يُرجى إتاحة إذن الوصول إلى مكتبة الصور لحفظ الصورة.";
|
||||
"Common.Alerts.SavePhotoFailure.Title" = "إخفاق في حفظ الصورة";
|
||||
"Common.Alerts.ServerError.Title" = "خطأ في الخادم";
|
||||
"Common.Alerts.SignOut.Confirm" = "تسجيل الخروج";
|
||||
"Common.Alerts.SignOut.Message" = "هل أنت متأكد من أنك تريد تسجيل الخروج؟";
|
||||
"Common.Alerts.SignOut.Message" = "هل أنت متأكد من رغبتك في تسجيل الخروج؟";
|
||||
"Common.Alerts.SignOut.Title" = "تسجيل الخروج";
|
||||
"Common.Alerts.SignUpFailure.Title" = "فشل التسجيل";
|
||||
"Common.Alerts.SignUpFailure.Title" = "إخفاق في التسجيل";
|
||||
"Common.Alerts.VoteFailure.PollEnded" = "انتهى استطلاع الرأي";
|
||||
"Common.Alerts.VoteFailure.Title" = "فشل التصويت";
|
||||
"Common.Alerts.VoteFailure.Title" = "إخفاق في التصويت";
|
||||
"Common.Controls.Actions.Add" = "إضافة";
|
||||
"Common.Controls.Actions.Back" = "العودة";
|
||||
"Common.Controls.Actions.BlockDomain" = "حظر %@";
|
||||
"Common.Controls.Actions.Cancel" = "إلغاء";
|
||||
"Common.Controls.Actions.Compose" = "تأليف";
|
||||
"Common.Controls.Actions.Confirm" = "تأكيد";
|
||||
"Common.Controls.Actions.Continue" = "واصل";
|
||||
"Common.Controls.Actions.CopyPhoto" = "نسخ الصورة";
|
||||
"Common.Controls.Actions.Delete" = "احذف";
|
||||
"Common.Controls.Actions.Discard" = "تجاهل";
|
||||
"Common.Controls.Actions.Done" = "تمّ";
|
||||
"Common.Controls.Actions.Edit" = "تعديل";
|
||||
"Common.Controls.Actions.Edit" = "تحرير";
|
||||
"Common.Controls.Actions.FindPeople" = "ابحث عن أشخاص لمتابعتهم";
|
||||
"Common.Controls.Actions.ManuallySearch" = "البحث يدوياً بدلاً من ذلك";
|
||||
"Common.Controls.Actions.Next" = "التالي";
|
||||
"Common.Controls.Actions.Ok" = "حسنًا";
|
||||
"Common.Controls.Actions.Open" = "افتح";
|
||||
"Common.Controls.Actions.OpenInSafari" = "افتحه في سفاري";
|
||||
"Common.Controls.Actions.Preview" = "معاينة";
|
||||
"Common.Controls.Actions.OpenInSafari" = "الفتح في Safari";
|
||||
"Common.Controls.Actions.Preview" = "مُعاينة";
|
||||
"Common.Controls.Actions.Previous" = "السابق";
|
||||
"Common.Controls.Actions.Remove" = "احذف";
|
||||
"Common.Controls.Actions.Reply" = "رد";
|
||||
"Common.Controls.Actions.Reply" = "الرَد";
|
||||
"Common.Controls.Actions.ReportUser" = "ابلغ عن %@";
|
||||
"Common.Controls.Actions.Save" = "حفظ";
|
||||
"Common.Controls.Actions.SavePhoto" = "حفظ الصورة";
|
||||
"Common.Controls.Actions.SeeMore" = "عرض المزيد";
|
||||
"Common.Controls.Actions.Settings" = "الإعدادات";
|
||||
"Common.Controls.Actions.Share" = "شارك";
|
||||
"Common.Controls.Actions.SharePost" = "شارك المنشور";
|
||||
"Common.Controls.Actions.ShareUser" = "شارك %@";
|
||||
"Common.Controls.Actions.Share" = "المُشارك";
|
||||
"Common.Controls.Actions.SharePost" = "مشارك المنشور";
|
||||
"Common.Controls.Actions.ShareUser" = "مُشاركة %@";
|
||||
"Common.Controls.Actions.SignIn" = "تسجيل الدخول";
|
||||
"Common.Controls.Actions.SignUp" = "إنشاء حِساب";
|
||||
"Common.Controls.Actions.Skip" = "تخطي";
|
||||
"Common.Controls.Actions.TakePhoto" = "التقط صورة";
|
||||
"Common.Controls.Actions.TakePhoto" = "التقاط صورة";
|
||||
"Common.Controls.Actions.TryAgain" = "المُحاولة مرة أُخرى";
|
||||
"Common.Controls.Actions.UnblockDomain" = "إلغاء حظر %@";
|
||||
"Common.Controls.Friendship.Block" = "حظر";
|
||||
@ -75,7 +76,7 @@ Please check your internet connection.";
|
||||
"Common.Controls.Friendship.UnblockUser" = "إلغاء حظر %@";
|
||||
"Common.Controls.Friendship.Unmute" = "إلغاء الكتم";
|
||||
"Common.Controls.Friendship.UnmuteUser" = "إلغاء كتم %@";
|
||||
"Common.Controls.Keyboard.Common.ComposeNewPost" = "إنشاء منشور جديد";
|
||||
"Common.Controls.Keyboard.Common.ComposeNewPost" = "تأليف منشور جديد";
|
||||
"Common.Controls.Keyboard.Common.OpenSettings" = "أفتح الإعدادات";
|
||||
"Common.Controls.Keyboard.Common.ShowFavorites" = "إظهار المفضلة";
|
||||
"Common.Controls.Keyboard.Common.SwitchToTab" = "التبديل إلى %@";
|
||||
@ -88,9 +89,9 @@ Please check your internet connection.";
|
||||
"Common.Controls.Keyboard.Timeline.PreviewImage" = "معاينة الصورة";
|
||||
"Common.Controls.Keyboard.Timeline.PreviousStatus" = "المنشور السابق";
|
||||
"Common.Controls.Keyboard.Timeline.ReplyStatus" = "رد على المنشور";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleContentWarning" = "Toggle Content Warning";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleFavorite" = "Toggle Favorite on Post";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "Toggle Reblog on Post";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleContentWarning" = "تبديل تحذير المُحتَوى";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleFavorite" = "تبديل المفضلة لِمنشور";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "تبديل إعادة تدوين منشور";
|
||||
"Common.Controls.Status.Actions.Favorite" = "إضافة إلى المفضلة";
|
||||
"Common.Controls.Status.Actions.Menu" = "القائمة";
|
||||
"Common.Controls.Status.Actions.Reblog" = "إعادة النشر";
|
||||
@ -98,9 +99,9 @@ Please check your internet connection.";
|
||||
"Common.Controls.Status.Actions.Unfavorite" = "إزالة من المفضلة";
|
||||
"Common.Controls.Status.Actions.Unreblog" = "تراجع عن إعادة النشر";
|
||||
"Common.Controls.Status.ContentWarning" = "تحذير عن المحتوى";
|
||||
"Common.Controls.Status.MediaContentWarning" = "Tap anywhere to reveal";
|
||||
"Common.Controls.Status.MediaContentWarning" = "انقر على أي مكان للكشف";
|
||||
"Common.Controls.Status.Poll.Closed" = "انتهى";
|
||||
"Common.Controls.Status.Poll.Vote" = "صَوّت";
|
||||
"Common.Controls.Status.Poll.Vote" = "صَوِّت";
|
||||
"Common.Controls.Status.ShowPost" = "اظهر المنشور";
|
||||
"Common.Controls.Status.ShowUserProfile" = "اظهر الملف التعريفي للمستخدم";
|
||||
"Common.Controls.Status.Tag.Email" = "البريد الإلكتروني";
|
||||
@ -116,43 +117,42 @@ Please check your internet connection.";
|
||||
"Common.Controls.Tabs.Profile" = "الملف التعريفي";
|
||||
"Common.Controls.Tabs.Search" = "بحث";
|
||||
"Common.Controls.Timeline.Filtered" = "مُصفَّى";
|
||||
"Common.Controls.Timeline.Header.BlockedWarning" = "You can’t view this user’s profile
|
||||
until they unblock you.";
|
||||
"Common.Controls.Timeline.Header.BlockingWarning" = "You can’t view this user's profile
|
||||
until you unblock them.
|
||||
Your profile looks like this to them.";
|
||||
"Common.Controls.Timeline.Header.BlockedWarning" = "لا يُمكِنُكَ عَرض الملف الشخصي لهذا المُستخدِم
|
||||
حتَّى يَرفَعَ الحَظر عَنك.";
|
||||
"Common.Controls.Timeline.Header.BlockingWarning" = "لا يُمكنك الاطلاع على الملف الشخصي لهذا المُستخدِم
|
||||
حتَّى تَرفعَ الحَظر عنه.
|
||||
ملفًّكَ الشخصي يَظهَرُ بِمثل هذِهِ الحالة بالنسبةِ لَهُ أيضًا.";
|
||||
"Common.Controls.Timeline.Header.NoStatusFound" = "لا توجد هناك منشورات";
|
||||
"Common.Controls.Timeline.Header.SuspendedWarning" = "This user has been suspended.";
|
||||
"Common.Controls.Timeline.Header.UserBlockedWarning" = "You can’t view %@’s profile
|
||||
until they unblock you.";
|
||||
"Common.Controls.Timeline.Header.UserBlockingWarning" = "You can’t view %@’s profile
|
||||
until you unblock them.
|
||||
Your profile looks like this to them.";
|
||||
"Common.Controls.Timeline.Header.UserSuspendedWarning" = "%@’s account has been suspended.";
|
||||
"Common.Controls.Timeline.Header.SuspendedWarning" = "تمَّ إيقاف هذا المُستخدِم.";
|
||||
"Common.Controls.Timeline.Header.UserBlockedWarning" = "لا يُمكِنُكَ عَرض ملف %@ الشخصي
|
||||
حتَّى يَرفَعَ الحَظر عَنك.";
|
||||
"Common.Controls.Timeline.Header.UserBlockingWarning" = "لا يُمكنك الاطلاع على ملف %@ الشخصي
|
||||
حتَّى تَرفعَ الحَظر عنه.
|
||||
ملفًّكَ الشخصي يَظهَرُ بِمثل هذِهِ الحالة بالنسبةِ لَهُ أيضًا.";
|
||||
"Common.Controls.Timeline.Header.UserSuspendedWarning" = "لقد أوقِفَ حِساب %@.";
|
||||
"Common.Controls.Timeline.Loader.LoadMissingPosts" = "تحميل المنشورات المَفقودة";
|
||||
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "تحميل المزيد من المنشورات...";
|
||||
"Common.Controls.Timeline.Loader.ShowMoreReplies" = "إظهار المزيد من الردود";
|
||||
"Common.Controls.Timeline.Timestamp.Now" = "الأن";
|
||||
"Scene.AccountList.AddAccount" = "إضافة حساب";
|
||||
"Scene.AccountList.DismissAccountSwitcher" = "تجاهُل مبدِّل الحساب";
|
||||
"Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher";
|
||||
"Scene.AccountList.TabBarHint" = "المِلف المُحدَّد حاليًا: %@. انقر نقرًا مزدوجًا ثم اضغط مع الاستمرار لإظهار مُبدِّل الحِساب";
|
||||
"Scene.Compose.Accessibility.AppendAttachment" = "إضافة مُرفَق";
|
||||
"Scene.Compose.Accessibility.AppendPoll" = "اضافة استطلاع رأي";
|
||||
"Scene.Compose.Accessibility.CustomEmojiPicker" = "منتقي مخصص للإيموجي";
|
||||
"Scene.Compose.Accessibility.DisableContentWarning" = "تعطيل تحذير الحتوى";
|
||||
"Scene.Compose.Accessibility.EnableContentWarning" = "تنشيط تحذير المحتوى";
|
||||
"Scene.Compose.Accessibility.PostVisibilityMenu" = "Post Visibility Menu";
|
||||
"Scene.Compose.Accessibility.PostVisibilityMenu" = "قائمة ظهور المنشور";
|
||||
"Scene.Compose.Accessibility.RemovePoll" = "إزالة الاستطلاع";
|
||||
"Scene.Compose.Attachment.AttachmentBroken" = "This %@ is broken and can’t be
|
||||
uploaded to Mastodon.";
|
||||
"Scene.Compose.Attachment.DescriptionPhoto" = "Describe the photo for the visually-impaired...";
|
||||
"Scene.Compose.Attachment.DescriptionVideo" = "Describe the video for the visually-impaired...";
|
||||
"Scene.Compose.Attachment.AttachmentBroken" = "هذا ال%@ مُعطَّل ويتعذَّر رفعه إلى ماستودون.";
|
||||
"Scene.Compose.Attachment.DescriptionPhoto" = "صِف الصورة للمكفوفين...";
|
||||
"Scene.Compose.Attachment.DescriptionVideo" = "صِف المقطع المرئي للمكفوفين...";
|
||||
"Scene.Compose.Attachment.Photo" = "صورة";
|
||||
"Scene.Compose.Attachment.Video" = "فيديو";
|
||||
"Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add";
|
||||
"Scene.Compose.AutoComplete.SpaceToAdd" = "انقر مساحة لإضافتِها";
|
||||
"Scene.Compose.ComposeAction" = "انشر";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "أخبِرنا بِما يَجُولُ فِي ذِهنَك";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here...";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "اكتب تَحذيرًا دَقيقًا هُنا...";
|
||||
"Scene.Compose.Keyboard.AppendAttachmentEntry" = "إضافة مُرفَق - %@";
|
||||
"Scene.Compose.Keyboard.DiscardPost" = "تجاهُل المنشور";
|
||||
"Scene.Compose.Keyboard.PublishPost" = "نَشر المَنشُور";
|
||||
@ -179,10 +179,10 @@ uploaded to Mastodon.";
|
||||
"Scene.Compose.Visibility.Unlisted" = "غير مُدرَج";
|
||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "لم أستلم أبدًا بريدا إلكترونيا";
|
||||
"Scene.ConfirmEmail.Button.OpenEmailApp" = "افتح تطبيق البريد الإلكتروني";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Check if your email address is correct as well as your junk folder if you haven’t.";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "تحقق ممَّ إذا كان عنوان بريدك الإلكتروني صحيحًا وكذلك تأكد مِن مجلد البريد غير الهام إذا لم تكن قد فعلت ذلك.";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.ResendEmail" = "إعادة إرسال البريد الإلكتروني";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Title" = "تحقق من بريدك الإلكتروني";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Description" = "We just sent you an email. Check your junk folder if you haven’t.";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Description" = "لقد أرسلنا لك بريدًا إلكترونيًا للتو. تحقق من مجلد البريد غير الهام الخاص بك إذا لم تكن قد فعلت ذلك.";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Mail" = "البريد";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.OpenEmailClient" = "فتح عميل البريد الإلكتروني";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Title" = "تحقَّق من بريدك الوارِد.";
|
||||
@ -190,6 +190,8 @@ uploaded to Mastodon.";
|
||||
اضغط على الرابط لتأكيد حسابك.";
|
||||
"Scene.ConfirmEmail.Title" = "شيء واحد أخير.";
|
||||
"Scene.Favorite.Title" = "مفضلتك";
|
||||
"Scene.Follower.Footer" = "لا يُمكِن عَرض المُتابِعين مِنَ الخوادم الأُخرى.";
|
||||
"Scene.Following.Footer" = "لا يُمكِن عَرض المُتابَعات مِنَ الخوادم الأُخرى.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "إظهار منشورات جديدة";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "غير متصل";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "تم نشره!";
|
||||
@ -205,7 +207,7 @@ uploaded to Mastodon.";
|
||||
"Scene.Notification.UserRebloggedYourPost" = "أعاد %@ تدوين مشاركتك";
|
||||
"Scene.Notification.UserRequestedToFollowYou" = "طلب %@ متابعتك";
|
||||
"Scene.Notification.UserYourPollHasEnded" = "%@ اِنتهى استطلاعُكَ للرأي";
|
||||
"Scene.Preview.Keyboard.ClosePreview" = "إغلاق المعاينة";
|
||||
"Scene.Preview.Keyboard.ClosePreview" = "إغلاق المُعايَنَة";
|
||||
"Scene.Preview.Keyboard.ShowNext" = "إظهار التالي";
|
||||
"Scene.Preview.Keyboard.ShowPrevious" = "إظهار السابق";
|
||||
"Scene.Profile.Dashboard.Followers" = "متابِع";
|
||||
@ -214,9 +216,9 @@ uploaded to Mastodon.";
|
||||
"Scene.Profile.Fields.AddRow" = "إضافة صف";
|
||||
"Scene.Profile.Fields.Placeholder.Content" = "المحتوى";
|
||||
"Scene.Profile.Fields.Placeholder.Label" = "التسمية";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Message" = "Confirm to unblock %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Message" = "أكِّد لرفع حظر %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Title" = "إلغاء حظر الحساب";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Confirm to unmute %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "أكِّد لرفع كتمْ %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Title" = "إلغاء كتم الحساب";
|
||||
"Scene.Profile.SegmentedControl.Media" = "وسائط";
|
||||
"Scene.Profile.SegmentedControl.Posts" = "منشورات";
|
||||
@ -227,20 +229,20 @@ uploaded to Mastodon.";
|
||||
"Scene.Register.Error.Item.Password" = "الكلمة السرية";
|
||||
"Scene.Register.Error.Item.Reason" = "السبب";
|
||||
"Scene.Register.Error.Item.Username" = "اسم المستخدم";
|
||||
"Scene.Register.Error.Reason.Accepted" = "%@ must be accepted";
|
||||
"Scene.Register.Error.Reason.Accepted" = "يجب أن يُقبل %@";
|
||||
"Scene.Register.Error.Reason.Blank" = "%@ مطلوب";
|
||||
"Scene.Register.Error.Reason.Blocked" = "%@ contains a disallowed email provider";
|
||||
"Scene.Register.Error.Reason.Inclusion" = "%@ is not a supported value";
|
||||
"Scene.Register.Error.Reason.Blocked" = "يحتوي %@ على موفِّر خدمة بريد إلكتروني غير مسموح به";
|
||||
"Scene.Register.Error.Reason.Inclusion" = "إنَّ %@ قيمة غير مدعومة";
|
||||
"Scene.Register.Error.Reason.Invalid" = "%@ غير صالح";
|
||||
"Scene.Register.Error.Reason.Reserved" = "%@ is a reserved keyword";
|
||||
"Scene.Register.Error.Reason.Taken" = "%@ is already in use";
|
||||
"Scene.Register.Error.Reason.Reserved" = "إنَّ %@ عبارة عن كلمة مفتاحيَّة محجوزة";
|
||||
"Scene.Register.Error.Reason.Taken" = "إنَّ %@ مُستخدَمٌ بالفعل";
|
||||
"Scene.Register.Error.Reason.TooLong" = "%@ طويل جداً";
|
||||
"Scene.Register.Error.Reason.TooShort" = "%@ قصير جدا";
|
||||
"Scene.Register.Error.Reason.Unreachable" = "%@ does not seem to exist";
|
||||
"Scene.Register.Error.Reason.Unreachable" = "يبدوا أنَّ %@ غير موجود";
|
||||
"Scene.Register.Error.Special.EmailInvalid" = "هذا عنوان بريد إلكتروني غير صالح";
|
||||
"Scene.Register.Error.Special.PasswordTooShort" = "كلمة المرور قصيرة جداً (يجب أن تكون 8 أحرف على الأقل)";
|
||||
"Scene.Register.Error.Special.UsernameInvalid" = "Username must only contain alphanumeric characters and underscores";
|
||||
"Scene.Register.Error.Special.UsernameTooLong" = "Username is too long (can’t be longer than 30 characters)";
|
||||
"Scene.Register.Error.Special.UsernameInvalid" = "يُمكِن أن يحتوي اسم المستخدم على أحرف أبجدية، أرقام وشرطات سفلية فقط";
|
||||
"Scene.Register.Error.Special.UsernameTooLong" = "اسم المستخدم طويل جداً (يجب ألّا يكون أطول من 30 رمز)";
|
||||
"Scene.Register.Input.Avatar.Delete" = "احذف";
|
||||
"Scene.Register.Input.DisplayName.Placeholder" = "الاسم المعروض";
|
||||
"Scene.Register.Input.Email.Placeholder" = "البريد الإلكتروني";
|
||||
@ -250,24 +252,24 @@ uploaded to Mastodon.";
|
||||
"Scene.Register.Input.Username.DuplicatePrompt" = "اسم المستخدم هذا غير متوفر.";
|
||||
"Scene.Register.Input.Username.Placeholder" = "اسم المستخدم";
|
||||
"Scene.Register.Title" = "أخبرنا عنك.";
|
||||
"Scene.Report.Content1" = "Are there any other posts you’d like to add to the report?";
|
||||
"Scene.Report.Content2" = "Is there anything the moderators should know about this report?";
|
||||
"Scene.Report.Send" = "ارسل الشكوى";
|
||||
"Scene.Report.Content1" = "هل ترغب في إضافة أي مشاركات أُخرى إلى الشكوى؟";
|
||||
"Scene.Report.Content2" = "هل هناك أي شيء يجب أن يعرفه المُراقبين حول هذه الشكوى؟";
|
||||
"Scene.Report.Send" = "إرسال الشكوى";
|
||||
"Scene.Report.SkipToSend" = "إرسال بدون تعليق";
|
||||
"Scene.Report.Step1" = "الخطوة 1 من 2";
|
||||
"Scene.Report.Step2" = "الخطوة 2 من 2";
|
||||
"Scene.Report.TextPlaceholder" = "Type or paste additional comments";
|
||||
"Scene.Report.TextPlaceholder" = "اكتب أو الصق تعليقات إضافيَّة";
|
||||
"Scene.Report.Title" = "ابلغ عن %@";
|
||||
"Scene.Search.Recommend.Accounts.Description" = "قد ترغب في متابعة هذه الحسابات";
|
||||
"Scene.Search.Recommend.Accounts.Follow" = "تابع";
|
||||
"Scene.Search.Recommend.Accounts.Title" = "حسابات قد تعجبك";
|
||||
"Scene.Search.Recommend.ButtonText" = "طالع الكل";
|
||||
"Scene.Search.Recommend.HashTag.Description" = "Hashtags that are getting quite a bit of attention";
|
||||
"Scene.Search.Recommend.HashTag.Description" = "الوسوم التي تحظى بقدر كبير من الاهتمام";
|
||||
"Scene.Search.Recommend.HashTag.PeopleTalking" = "%@ أشخاص يتحدَّثوا";
|
||||
"Scene.Search.Recommend.HashTag.Title" = "ذات شعبية على ماستدون";
|
||||
"Scene.Search.SearchBar.Cancel" = "إلغاء";
|
||||
"Scene.Search.SearchBar.Placeholder" = "البحث عن وسوم أو مستخدمين·ات";
|
||||
"Scene.Search.Searching.Clear" = "امسح";
|
||||
"Scene.Search.Searching.Clear" = "مَحو";
|
||||
"Scene.Search.Searching.EmptyState.NoResults" = "ليس هناك أية نتيجة";
|
||||
"Scene.Search.Searching.RecentSearch" = "عمليات البحث الأخيرة";
|
||||
"Scene.Search.Searching.Segment.All" = "الكل";
|
||||
@ -291,7 +293,7 @@ uploaded to Mastodon.";
|
||||
"Scene.ServerPicker.Button.Category.Tech" = "تكنولوجيا";
|
||||
"Scene.ServerPicker.Button.SeeLess" = "اعرض أقل";
|
||||
"Scene.ServerPicker.Button.SeeMore" = "اعرض المزيد";
|
||||
"Scene.ServerPicker.EmptyState.BadNetwork" = "Something went wrong while loading the data. Check your internet connection.";
|
||||
"Scene.ServerPicker.EmptyState.BadNetwork" = "حدث خطأٌ ما أثناء تحميل البيانات. تحقَّق من اتصالك بالإنترنت.";
|
||||
"Scene.ServerPicker.EmptyState.FindingServers" = "البحث عن خوادم متوفرة...";
|
||||
"Scene.ServerPicker.EmptyState.NoResults" = "لا توجد نتائج";
|
||||
"Scene.ServerPicker.Input.Placeholder" = "ابحث عن خادم أو انضم إلى سيرفر خاص بك...";
|
||||
@ -306,7 +308,7 @@ uploaded to Mastodon.";
|
||||
"Scene.ServerRules.Subtitle" = "تم سنّ هذه القواعد من قبل مشرفي %@.";
|
||||
"Scene.ServerRules.TermsOfService" = "شروط الخدمة";
|
||||
"Scene.ServerRules.Title" = "بعض القواعد الأساسية.";
|
||||
"Scene.Settings.Footer.MastodonDescription" = "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على غيت هب %@ (%@)";
|
||||
"Scene.Settings.Footer.MastodonDescription" = "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء على GitHub في %@ (%@)";
|
||||
"Scene.Settings.Keyboard.CloseSettingsWindow" = "إغلاق نافذة الإعدادات";
|
||||
"Scene.Settings.Section.Appearance.Automatic" = "تلقائي";
|
||||
"Scene.Settings.Section.Appearance.Dark" = "مظلمٌ دائِمًا";
|
||||
@ -335,12 +337,12 @@ uploaded to Mastodon.";
|
||||
"Scene.Settings.Section.SpicyZone.Signout" = "تسجيل الخروج";
|
||||
"Scene.Settings.Section.SpicyZone.Title" = "المنطقة الحارة";
|
||||
"Scene.Settings.Title" = "الإعدادات";
|
||||
"Scene.SuggestionAccount.FollowExplain" = "When you follow someone, you’ll see their posts in your home feed.";
|
||||
"Scene.SuggestionAccount.FollowExplain" = "عِندَ مُتابَعَتِكَ لأحدِهِم، سَوف تَرى مَنشوراته في تغذيَتِكَ الرئيسة.";
|
||||
"Scene.SuggestionAccount.Title" = "ابحث عن أشخاص لمتابعتهم";
|
||||
"Scene.Thread.BackTitle" = "منشور";
|
||||
"Scene.Thread.Title" = "مَنشور مِن %@";
|
||||
"Scene.Welcome.Slogan" = "Social networking
|
||||
back in your hands.";
|
||||
"Scene.Welcome.Slogan" = "شبكات التواصل الاجتماعي
|
||||
مرة أُخرى بين يديك.";
|
||||
"Scene.Wizard.AccessibilityHint" = "انقر نقرًا مزدوجًا لتجاهل النافذة المنبثقة";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button.";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "بدِّل بين حسابات متعددة عبر الاستمرار بالضغط على زر الملف الشخصي.";
|
||||
"Scene.Wizard.NewInMastodon" = "جديد في ماستودون";
|
@ -13,13 +13,13 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>zero</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>لا إشعار غير مقروء</string>
|
||||
<key>one</key>
|
||||
<string>إشعار واحِد غير مقروء</string>
|
||||
<key>two</key>
|
||||
<string>إشعاران غير مقروءان</string>
|
||||
<key>few</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>%ld إشعارات غير مقروءة</string>
|
||||
<key>many</key>
|
||||
<string>%ld إشعارًا غيرَ مقروء</string>
|
||||
<key>other</key>
|
||||
@ -277,7 +277,7 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>zero</key>
|
||||
<string>%ld followers</string>
|
||||
<string>لا مُتابِع</string>
|
||||
<key>one</key>
|
||||
<string>مُتابِعٌ واحد</string>
|
||||
<key>two</key>
|
||||
|
@ -28,6 +28,7 @@ Comprova la teva connexió a Internet.";
|
||||
"Common.Controls.Actions.Back" = "Enrere";
|
||||
"Common.Controls.Actions.BlockDomain" = "Bloqueja %@";
|
||||
"Common.Controls.Actions.Cancel" = "Cancel·la";
|
||||
"Common.Controls.Actions.Compose" = "Composa";
|
||||
"Common.Controls.Actions.Confirm" = "Confirma";
|
||||
"Common.Controls.Actions.Continue" = "Continua";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Copia la foto";
|
||||
@ -190,6 +191,8 @@ carregat a Mastodon.";
|
||||
toca l'enllaç per a confirmar el teu compte.";
|
||||
"Scene.ConfirmEmail.Title" = "Una última cosa.";
|
||||
"Scene.Favorite.Title" = "Els teus Favorits";
|
||||
"Scene.Follower.Footer" = "Els seguidors d'altres servidors no son mostrats.";
|
||||
"Scene.Following.Footer" = "Els seguits d'altres servidors no son mostrats.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Veure noves publicacions";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Fora de línia";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Publicat!";
|
||||
|
@ -28,6 +28,7 @@ Bitte überprüfe deine Internetverbindung.";
|
||||
"Common.Controls.Actions.Back" = "Zurück";
|
||||
"Common.Controls.Actions.BlockDomain" = "%@ blockieren";
|
||||
"Common.Controls.Actions.Cancel" = "Abbrechen";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "Bestätigen";
|
||||
"Common.Controls.Actions.Continue" = "Fortfahren";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Foto kopieren";
|
||||
@ -190,6 +191,8 @@ kann nicht auf Mastodon hochgeladen werden.";
|
||||
tippe darin auf den Link, um Dein Konto zu bestätigen.";
|
||||
"Scene.ConfirmEmail.Title" = "Noch eine letzte Sache.";
|
||||
"Scene.Favorite.Title" = "Deine Favoriten";
|
||||
"Scene.Follower.Footer" = "Followers from other servers are not displayed.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Neue Beiträge anzeigen";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Offline";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Veröffentlicht!";
|
||||
|
@ -28,6 +28,7 @@ Please check your internet connection.";
|
||||
"Common.Controls.Actions.Back" = "Back";
|
||||
"Common.Controls.Actions.BlockDomain" = "Block %@";
|
||||
"Common.Controls.Actions.Cancel" = "Cancel";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "Confirm";
|
||||
"Common.Controls.Actions.Continue" = "Continue";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Copy Photo";
|
||||
@ -190,6 +191,8 @@ uploaded to Mastodon.";
|
||||
tap the link to confirm your account.";
|
||||
"Scene.ConfirmEmail.Title" = "One last thing.";
|
||||
"Scene.Favorite.Title" = "Your Favorites";
|
||||
"Scene.Follower.Footer" = "Followers from other servers are not displayed.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "See new posts";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Offline";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Published!";
|
||||
|
@ -28,6 +28,7 @@ Por favor, revisá tu conexión a Internet.";
|
||||
"Common.Controls.Actions.Back" = "Volver";
|
||||
"Common.Controls.Actions.BlockDomain" = "Bloquear a %@";
|
||||
"Common.Controls.Actions.Cancel" = "Cancelar";
|
||||
"Common.Controls.Actions.Compose" = "Redactar";
|
||||
"Common.Controls.Actions.Confirm" = "Confirmar";
|
||||
"Common.Controls.Actions.Continue" = "Continuar";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Copiar foto";
|
||||
@ -190,6 +191,8 @@ y no se puede subir a Mastodon.";
|
||||
pulsá en el enlace para confirmar tu cuenta.";
|
||||
"Scene.ConfirmEmail.Title" = "Una última cosa.";
|
||||
"Scene.Favorite.Title" = "Tus favoritos";
|
||||
"Scene.Follower.Footer" = "No se muestran los seguidores de otros servidores.";
|
||||
"Scene.Following.Footer" = "No se muestran las cuentas de otros servidores que seguís.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Ver nuevos mensajes";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Desconectado";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "¡Enviado!";
|
||||
|
@ -28,6 +28,7 @@ Por favor, revise su conexión a internet.";
|
||||
"Common.Controls.Actions.Back" = "Atrás";
|
||||
"Common.Controls.Actions.BlockDomain" = "Bloquear %@";
|
||||
"Common.Controls.Actions.Cancel" = "Cancelar";
|
||||
"Common.Controls.Actions.Compose" = "Redactar";
|
||||
"Common.Controls.Actions.Confirm" = "Confirmar";
|
||||
"Common.Controls.Actions.Continue" = "Continuar";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Copiar foto";
|
||||
@ -190,6 +191,8 @@ subirse a Mastodon.";
|
||||
pulsa en el enlace para confirmar tu cuenta.";
|
||||
"Scene.ConfirmEmail.Title" = "Una última cosa.";
|
||||
"Scene.Favorite.Title" = "Tus Favoritos";
|
||||
"Scene.Follower.Footer" = "No se muestran los seguidores de otros servidores.";
|
||||
"Scene.Following.Footer" = "No se muestran los seguidos de otros servidores.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Ver nuevas publicaciones";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Sin Conexión";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "¡Publicado!";
|
||||
|
@ -28,6 +28,7 @@ Veuillez vérifier votre accès à Internet.";
|
||||
"Common.Controls.Actions.Back" = "Retour";
|
||||
"Common.Controls.Actions.BlockDomain" = "Bloquer %@";
|
||||
"Common.Controls.Actions.Cancel" = "Annuler";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "Confirmer";
|
||||
"Common.Controls.Actions.Continue" = "Continuer";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Copier la photo";
|
||||
@ -133,7 +134,7 @@ Votre profil ressemble à ça pour lui.";
|
||||
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Chargement des publications manquantes...";
|
||||
"Common.Controls.Timeline.Loader.ShowMoreReplies" = "Charger plus de réponses";
|
||||
"Common.Controls.Timeline.Timestamp.Now" = "À l’instant";
|
||||
"Scene.AccountList.AddAccount" = "Add Account";
|
||||
"Scene.AccountList.AddAccount" = "Ajouter un compte";
|
||||
"Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher";
|
||||
"Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher";
|
||||
"Scene.Compose.Accessibility.AppendAttachment" = "Joindre un document";
|
||||
@ -190,6 +191,8 @@ téléversé sur Mastodon.";
|
||||
tapotez le lien pour confirmer votre compte.";
|
||||
"Scene.ConfirmEmail.Title" = "Une dernière chose.";
|
||||
"Scene.Favorite.Title" = "Vos favoris";
|
||||
"Scene.Follower.Footer" = "Les abonné·e·s issus des autres serveurs ne sont pas affiché·e·s.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Voir les nouvelles publications";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Hors ligne";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Publié!";
|
||||
@ -340,6 +343,6 @@ n'importe quel serveur.";
|
||||
"Scene.Thread.BackTitle" = "Publication";
|
||||
"Scene.Thread.Title" = "Publication de %@";
|
||||
"Scene.Welcome.Slogan" = "Le réseau social qui vous rend le contrôle.";
|
||||
"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button.";
|
||||
"Scene.Wizard.NewInMastodon" = "New in Mastodon";
|
||||
"Scene.Wizard.AccessibilityHint" = "Tapotez deux fois pour fermer cet assistant";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Basculez entre plusieurs comptes en appuyant de maniere prolongée sur le bouton profil.";
|
||||
"Scene.Wizard.NewInMastodon" = "Nouveau dans Mastodon";
|
@ -13,15 +13,15 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>1 unread notification</string>
|
||||
<string>1 notification non lue</string>
|
||||
<key>other</key>
|
||||
<string>%ld unread notification</string>
|
||||
<string>%ld notifications non lues</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>a11y.plural.count.input_limit_exceeds</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Input limit exceeds %#@character_count@</string>
|
||||
<string>La limite d’entrée dépasse %#@character_count@</string>
|
||||
<key>character_count</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
|
@ -28,6 +28,7 @@ Thoir sùil air a’ cheangal agad ris an eadar-lìon.";
|
||||
"Common.Controls.Actions.Back" = "Air ais";
|
||||
"Common.Controls.Actions.BlockDomain" = "Bac %@";
|
||||
"Common.Controls.Actions.Cancel" = "Sguir dheth";
|
||||
"Common.Controls.Actions.Compose" = "Sgrìobh";
|
||||
"Common.Controls.Actions.Confirm" = "Dearbh";
|
||||
"Common.Controls.Actions.Continue" = "Lean air adhart";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Dèan lethbhreac dhen dealbh";
|
||||
@ -190,6 +191,8 @@ a luchdadh suas gu Mastodon.";
|
||||
thoir gnogag air a’ chunntas a dhearbhadh a’ chunntais agad.";
|
||||
"Scene.ConfirmEmail.Title" = "Aon rud eile.";
|
||||
"Scene.Favorite.Title" = "Na h-annsachdan agad";
|
||||
"Scene.Follower.Footer" = "Cha dèid luchd-leantainn o fhrithealaichean eile a shealltainn.";
|
||||
"Scene.Following.Footer" = "Cha dèid cò air a leanas tu air frithealaichean eile a shealltainn.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Seall na postaichean ùra";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Far loidhne";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Chaidh fhoillseachadh!";
|
||||
|
@ -28,6 +28,7 @@
|
||||
"Common.Controls.Actions.Back" = "戻る";
|
||||
"Common.Controls.Actions.BlockDomain" = "%@をブロック";
|
||||
"Common.Controls.Actions.Cancel" = "キャンセル";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "確認";
|
||||
"Common.Controls.Actions.Continue" = "続ける";
|
||||
"Common.Controls.Actions.CopyPhoto" = "写真をコピー";
|
||||
@ -184,6 +185,8 @@
|
||||
"Scene.ConfirmEmail.Subtitle" = "先程 %@ にメールを送信しました。リンクをタップしてアカウントを確認してください。";
|
||||
"Scene.ConfirmEmail.Title" = "さいごにもうひとつ。";
|
||||
"Scene.Favorite.Title" = "お気に入り";
|
||||
"Scene.Follower.Footer" = "Followers from other servers are not displayed.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "新しい投稿を見る";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "オフライン";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "投稿しました!";
|
||||
|
@ -1,21 +1,21 @@
|
||||
"Common.Alerts.BlockDomain.BlockEntireDomain" = "Navperê asteng bike";
|
||||
"Common.Alerts.BlockDomain.Title" = "Tu ji xwe bawerî, bi rastî tu dixwazî hemû %@ asteng bikî? Di gelek rewşan de asteng kirin an jî bêdeng kirin têrê dike û tê tercîh kirin. Tu nikarî naveroka vê navperê di demnameyê an jî agahdariyên xwe de bibînî. Şopînerên te yê di vê navperê were jêbirin.";
|
||||
"Common.Alerts.CleanCache.Message" = "Pêşbîra %@ biserketî hate paqijkirin.";
|
||||
"Common.Alerts.CleanCache.Title" = "Pêşbîrê paqij bike";
|
||||
"Common.Alerts.CleanCache.Message" = "Pêşbîra %@ biserketî hate pakkirin.";
|
||||
"Common.Alerts.CleanCache.Title" = "Pêşbîrê pak bike";
|
||||
"Common.Alerts.Common.PleaseTryAgain" = "Ji kerema xwe dîsa biceribîne.";
|
||||
"Common.Alerts.Common.PleaseTryAgainLater" = "Ji kerema xwe paşê dîsa biceribîne.";
|
||||
"Common.Alerts.DeletePost.Delete" = "Jê bibe";
|
||||
"Common.Alerts.DeletePost.Title" = "Ma tu dixwazî vê şandiyê jê bibî?";
|
||||
"Common.Alerts.DiscardPostContent.Message" = "Piştrast bikin ku naveroka posteyê ya hatîye nivîsandin jê bibin.";
|
||||
"Common.Alerts.DiscardPostContent.Title" = "Reşnivîs jêbibe";
|
||||
"Common.Alerts.DiscardPostContent.Message" = "Bipejrîne ku naveroka şandiyê ya hatiye nivîsandin paşguh bikî.";
|
||||
"Common.Alerts.DiscardPostContent.Title" = "Reşnivîsê paşguh bike";
|
||||
"Common.Alerts.EditProfileFailure.Message" = "Nikare profîlê serrast bike. Jkx dîsa biceribîne.";
|
||||
"Common.Alerts.EditProfileFailure.Title" = "Çewtiya profîlê biguherîne";
|
||||
"Common.Alerts.EditProfileFailure.Title" = "Di serrastkirina profîlê çewtî";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.MoreThanOneVideo" = "Nikare ji bêtirî yek vîdyoyekê tevlî şandiyê bike.";
|
||||
"Common.Alerts.PublishPostFailure.AttachmentsMessage.VideoAttachWithPhoto" = "Nikare vîdyoyekê tevlî şandiyê ku berê wêne tê de heye bike.";
|
||||
"Common.Alerts.PublishPostFailure.Message" = "Weşandina şandiyê têkçû.
|
||||
Jkx girêdana înternetê xwe kontrol bike.";
|
||||
"Common.Alerts.PublishPostFailure.Title" = "Weşandin têkçû";
|
||||
"Common.Alerts.SavePhotoFailure.Message" = "Ji kerema xwe destûra gihîştina pirtûkxaneya wêneyê çalak bikin da ku wêneyê hilînin.";
|
||||
"Common.Alerts.SavePhotoFailure.Message" = "Ji kerema xwe mafê bide gihîştina wênegehê çalak bike da ku wêne werin tomarkirin.";
|
||||
"Common.Alerts.SavePhotoFailure.Title" = "Tomarkirina wêneyê têkçû";
|
||||
"Common.Alerts.ServerError.Title" = "Çewtiya rajekar";
|
||||
"Common.Alerts.SignOut.Confirm" = "Derkeve";
|
||||
@ -28,15 +28,16 @@ Jkx girêdana înternetê xwe kontrol bike.";
|
||||
"Common.Controls.Actions.Back" = "Vegere";
|
||||
"Common.Controls.Actions.BlockDomain" = "%@ asteng bike";
|
||||
"Common.Controls.Actions.Cancel" = "Dev jê berde";
|
||||
"Common.Controls.Actions.Compose" = "Binivîsîne";
|
||||
"Common.Controls.Actions.Confirm" = "Bipejirîne";
|
||||
"Common.Controls.Actions.Continue" = "Bidomîne";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Wêne kopî bikin";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Wêneyê jê bigire";
|
||||
"Common.Controls.Actions.Delete" = "Jê bibe";
|
||||
"Common.Controls.Actions.Discard" = "Biavêje";
|
||||
"Common.Controls.Actions.Done" = "Qediya";
|
||||
"Common.Controls.Actions.Edit" = "Serrast bike";
|
||||
"Common.Controls.Actions.FindPeople" = "Kesên ku bişopînin bibînin";
|
||||
"Common.Controls.Actions.ManuallySearch" = "Ji devlê i destan lêgerînê bike";
|
||||
"Common.Controls.Actions.FindPeople" = "Mirovan bo şopandinê bibîne";
|
||||
"Common.Controls.Actions.ManuallySearch" = "Ji devlê bi destan lêgerînê bike";
|
||||
"Common.Controls.Actions.Next" = "Pêş";
|
||||
"Common.Controls.Actions.Ok" = "BAŞ E";
|
||||
"Common.Controls.Actions.Open" = "Veke";
|
||||
@ -47,7 +48,7 @@ Jkx girêdana înternetê xwe kontrol bike.";
|
||||
"Common.Controls.Actions.Reply" = "Bersivê bide";
|
||||
"Common.Controls.Actions.ReportUser" = "%@ ragihîne";
|
||||
"Common.Controls.Actions.Save" = "Tomar bike";
|
||||
"Common.Controls.Actions.SavePhoto" = "Wêneyê hilîne";
|
||||
"Common.Controls.Actions.SavePhoto" = "Wêneyê tomar bike";
|
||||
"Common.Controls.Actions.SeeMore" = "Bêtir bibîne";
|
||||
"Common.Controls.Actions.Settings" = "Sazkarî";
|
||||
"Common.Controls.Actions.Share" = "Parve bike";
|
||||
@ -70,7 +71,7 @@ Jkx girêdana înternetê xwe kontrol bike.";
|
||||
"Common.Controls.Friendship.MuteUser" = "%@ bêdeng bike";
|
||||
"Common.Controls.Friendship.Muted" = "Bêdengkirî";
|
||||
"Common.Controls.Friendship.Pending" = "Tê nirxandin";
|
||||
"Common.Controls.Friendship.Request" = "Daxwazên şopandinê";
|
||||
"Common.Controls.Friendship.Request" = "Daxwaz bike";
|
||||
"Common.Controls.Friendship.Unblock" = "Astengiyê rake";
|
||||
"Common.Controls.Friendship.UnblockUser" = "%@ asteng neke";
|
||||
"Common.Controls.Friendship.Unmute" = "Bêdeng neke";
|
||||
@ -79,86 +80,86 @@ Jkx girêdana înternetê xwe kontrol bike.";
|
||||
"Common.Controls.Keyboard.Common.OpenSettings" = "Sazkariyan Veke";
|
||||
"Common.Controls.Keyboard.Common.ShowFavorites" = "Bijarteyan nîşan bide";
|
||||
"Common.Controls.Keyboard.Common.SwitchToTab" = "Biguherîne bo %@";
|
||||
"Common.Controls.Keyboard.SegmentedControl.NextSection" = "Beşa paşê";
|
||||
"Common.Controls.Keyboard.SegmentedControl.PreviousSection" = "Beşa berê";
|
||||
"Common.Controls.Keyboard.SegmentedControl.NextSection" = "Beşa pêş";
|
||||
"Common.Controls.Keyboard.SegmentedControl.PreviousSection" = "Beşa paş";
|
||||
"Common.Controls.Keyboard.Timeline.NextStatus" = "Şandiya pêş";
|
||||
"Common.Controls.Keyboard.Timeline.OpenAuthorProfile" = "Profîla nivîskaran veke";
|
||||
"Common.Controls.Keyboard.Timeline.OpenRebloggerProfile" = "Profîla nivîskaran veke";
|
||||
"Common.Controls.Keyboard.Timeline.OpenStatus" = "Şandiyê veke";
|
||||
"Common.Controls.Keyboard.Timeline.PreviewImage" = "Wêneya pêşdîtinê";
|
||||
"Common.Controls.Keyboard.Timeline.PreviewImage" = "Pêşdîtina wêneyê";
|
||||
"Common.Controls.Keyboard.Timeline.PreviousStatus" = "Şandeya paş";
|
||||
"Common.Controls.Keyboard.Timeline.ReplyStatus" = "Bersivê bide şandiyê";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleContentWarning" = "Hişyariya naverokê veke/bigire";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleFavorite" = "Di postê da Bijartin veke/bigire";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "Toggle Reblog on Post";
|
||||
"Common.Controls.Status.Actions.Favorite" = "Bijartî";
|
||||
"Common.Controls.Status.Actions.Menu" = "Menû";
|
||||
"Common.Controls.Status.Actions.Reblog" = "Ji nû ve blog";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleContentWarning" = "Hişyariya naverokê biguherîne";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleFavorite" = "Li ser şandiyê bijarte biguherîne";
|
||||
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "Ji vû nivîsandin di şandiyê de biguherîne";
|
||||
"Common.Controls.Status.Actions.Favorite" = "Bijarte";
|
||||
"Common.Controls.Status.Actions.Menu" = "Kulîn";
|
||||
"Common.Controls.Status.Actions.Reblog" = "Ji nû ve nivîsandin";
|
||||
"Common.Controls.Status.Actions.Reply" = "Bersivê bide";
|
||||
"Common.Controls.Status.Actions.Unfavorite" = "Nebijare";
|
||||
"Common.Controls.Status.Actions.Unreblog" = "Ji nû ve blogkirin betal bikin";
|
||||
"Common.Controls.Status.Actions.Unfavorite" = "Nebijarte";
|
||||
"Common.Controls.Status.Actions.Unreblog" = "Ji nû ve nivîsandinê vegere";
|
||||
"Common.Controls.Status.ContentWarning" = "Hişyariya naverokê";
|
||||
"Common.Controls.Status.MediaContentWarning" = "Ji bo aşkerakirinê derekî bitikîne";
|
||||
"Common.Controls.Status.MediaContentWarning" = "Ji bo eşkerekirinê li derekî bitikîne";
|
||||
"Common.Controls.Status.Poll.Closed" = "Girtî";
|
||||
"Common.Controls.Status.Poll.Vote" = "Deng";
|
||||
"Common.Controls.Status.Poll.Vote" = "Deng bide";
|
||||
"Common.Controls.Status.ShowPost" = "Şandiyê nîşan bide";
|
||||
"Common.Controls.Status.ShowUserProfile" = "Profîla bikarhêner nîşan bide";
|
||||
"Common.Controls.Status.Tag.Email" = "E-name";
|
||||
"Common.Controls.Status.Tag.Emoji" = "E-name";
|
||||
"Common.Controls.Status.Tag.Hashtag" = "Etîket";
|
||||
"Common.Controls.Status.Tag.Emoji" = "Emojî";
|
||||
"Common.Controls.Status.Tag.Hashtag" = "Hashtag";
|
||||
"Common.Controls.Status.Tag.Link" = "Girêdan";
|
||||
"Common.Controls.Status.Tag.Mention" = "Behs";
|
||||
"Common.Controls.Status.Tag.Mention" = "Qalkirin";
|
||||
"Common.Controls.Status.Tag.Url" = "URL";
|
||||
"Common.Controls.Status.UserReblogged" = "%@ ji nû ve hat blogkirin";
|
||||
"Common.Controls.Status.UserReblogged" = "%@ ji nû ve hate nivîsandin";
|
||||
"Common.Controls.Status.UserRepliedTo" = "Bersiv da %@";
|
||||
"Common.Controls.Tabs.Home" = "Serrûpel";
|
||||
"Common.Controls.Tabs.Notification" = "Agahdarî";
|
||||
"Common.Controls.Tabs.Profile" = "Profîl";
|
||||
"Common.Controls.Tabs.Search" = "Bigere";
|
||||
"Common.Controls.Timeline.Filtered" = "Parzûnkirî";
|
||||
"Common.Controls.Timeline.Header.BlockedWarning" = "Tu nikarî profîla vî bikarhênerî bibînî
|
||||
heta ku astengîya te rakin.";
|
||||
"Common.Controls.Timeline.Header.BlockingWarning" = "Tu nikarî profîla vî bikarhênerî bibînî
|
||||
Heta ku tu wan asteng bikî.
|
||||
"Common.Controls.Timeline.Header.BlockedWarning" = "Tu nikarî profîla vî/ê bikarhênerî bibînî
|
||||
heya ku ew astengiyê li ser te rakin.";
|
||||
"Common.Controls.Timeline.Header.BlockingWarning" = "Tu nikarî profîla vî/ê bikarhênerî bibînî
|
||||
Heya ku tu astengiyê li ser wî/ê ranekî.
|
||||
Profîla te ji wan ra wiha xuya dike.";
|
||||
"Common.Controls.Timeline.Header.NoStatusFound" = "Şandî nehate dîtin";
|
||||
"Common.Controls.Timeline.Header.SuspendedWarning" = "Ev bikarhêner hat sekinandin.";
|
||||
"Common.Controls.Timeline.Header.NoStatusFound" = "Tu şandî nehate dîtin";
|
||||
"Common.Controls.Timeline.Header.SuspendedWarning" = "Ev bikarhêner hatiye rawestandin.";
|
||||
"Common.Controls.Timeline.Header.UserBlockedWarning" = "Tu nikarî profîla %@ bibînî
|
||||
Heta ku astengîya te rakin.";
|
||||
"Common.Controls.Timeline.Header.UserBlockingWarning" = "Tu nikarî profîla %@ bibînî
|
||||
Heta ku tu wan asteng bikî.
|
||||
Heya ku tu astengiyê li ser wî/ê ranekî.
|
||||
Profîla te ji wan ra wiha xuya dike.";
|
||||
"Common.Controls.Timeline.Header.UserSuspendedWarning" = "Hesaba %@ hat sekinandin.";
|
||||
"Common.Controls.Timeline.Loader.LoadMissingPosts" = "Barkirina posteyên kêm";
|
||||
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Barkirina posteyên kêm...";
|
||||
"Common.Controls.Timeline.Header.UserSuspendedWarning" = "Ajimêra %@ hatiye rawestandin.";
|
||||
"Common.Controls.Timeline.Loader.LoadMissingPosts" = "Şandiyên wendayî bar bike";
|
||||
"Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Şandiyên wendayî tên barkirin...";
|
||||
"Common.Controls.Timeline.Loader.ShowMoreReplies" = "Bêtir bersivan nîşan bide";
|
||||
"Common.Controls.Timeline.Timestamp.Now" = "Niha";
|
||||
"Scene.AccountList.AddAccount" = "Ajimêr tevlî bike";
|
||||
"Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher";
|
||||
"Scene.AccountList.DismissAccountSwitcher" = "Guherkera ajimêrê paş guh bike";
|
||||
"Scene.AccountList.TabBarHint" = "Profîla hilbijartî ya niha: %@. Du caran bitikîne û paşê dest bide ser da ku guhêrbara ajimêr were nîşandan";
|
||||
"Scene.Compose.Accessibility.AppendAttachment" = "Pêvek tevlî bike";
|
||||
"Scene.Compose.Accessibility.AppendPoll" = "Rapirsî tevlî bike";
|
||||
"Scene.Compose.Accessibility.CustomEmojiPicker" = "Custom Emoji Picker";
|
||||
"Scene.Compose.Accessibility.CustomEmojiPicker" = "Hilbijêrê emojî yên kesanekirî";
|
||||
"Scene.Compose.Accessibility.DisableContentWarning" = "Hişyariya naverokê neçalak bike";
|
||||
"Scene.Compose.Accessibility.EnableContentWarning" = "Enable Content Warning";
|
||||
"Scene.Compose.Accessibility.PostVisibilityMenu" = "Menuya Xuyabûna Şandiyê";
|
||||
"Scene.Compose.Accessibility.EnableContentWarning" = "Hişyariya naverokê çalak bike";
|
||||
"Scene.Compose.Accessibility.PostVisibilityMenu" = "Kulîna xuyabûna şandiyê";
|
||||
"Scene.Compose.Accessibility.RemovePoll" = "Rapirsî rake";
|
||||
"Scene.Compose.Attachment.AttachmentBroken" = "Ev %@ naxebite û nayê barkirin
|
||||
li ser Mastodon.";
|
||||
"Scene.Compose.Attachment.DescriptionPhoto" = "Describe the photo for the visually-impaired...";
|
||||
"Scene.Compose.Attachment.DescriptionVideo" = "Describe the video for the visually-impaired...";
|
||||
"Scene.Compose.Attachment.DescriptionPhoto" = "Wêneyê ji bo kêmbînên dîtbar bide nasîn...";
|
||||
"Scene.Compose.Attachment.DescriptionVideo" = "Vîdyoyê ji bo kêmbînên dîtbar bide nasîn...";
|
||||
"Scene.Compose.Attachment.Photo" = "wêne";
|
||||
"Scene.Compose.Attachment.Video" = "vîdyo";
|
||||
"Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add";
|
||||
"Scene.Compose.AutoComplete.SpaceToAdd" = "Bicîhkirinê tevlî bike";
|
||||
"Scene.Compose.ComposeAction" = "Biweşîne";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Type or paste what’s on your mind";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here...";
|
||||
"Scene.Compose.Keyboard.AppendAttachmentEntry" = "Pêvek lê zêde bike - %@";
|
||||
"Scene.Compose.Keyboard.DiscardPost" = "Şandî bihelîne";
|
||||
"Scene.Compose.Keyboard.PublishPost" = "Şandiye bide weşan";
|
||||
"Scene.Compose.Keyboard.SelectVisibilityEntry" = "Xuyanîbûn hilbijêre - %@";
|
||||
"Scene.Compose.Keyboard.ToggleContentWarning" = "Hişyariya naverokê veke/bigire";
|
||||
"Scene.Compose.Keyboard.TogglePoll" = "Anketê veke/bigire";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Tiştê ku di hişê te de ye binivîsin an jî pêve bike";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Li vir hişyariyek hûrgilî binivîsine...";
|
||||
"Scene.Compose.Keyboard.AppendAttachmentEntry" = "Pêvek tevlî bike - %@";
|
||||
"Scene.Compose.Keyboard.DiscardPost" = "Şandî paşguh bike";
|
||||
"Scene.Compose.Keyboard.PublishPost" = "Şandiyê biweşîne";
|
||||
"Scene.Compose.Keyboard.SelectVisibilityEntry" = "Xuyabûnê hilbijêre - %@";
|
||||
"Scene.Compose.Keyboard.ToggleContentWarning" = "Hişyariya naverokê biguherîne";
|
||||
"Scene.Compose.Keyboard.TogglePoll" = "Rapirsiyê biguherîne";
|
||||
"Scene.Compose.MediaSelection.Browse" = "Bigere";
|
||||
"Scene.Compose.MediaSelection.Camera" = "Wêne bikişîne";
|
||||
"Scene.Compose.MediaSelection.PhotoLibrary" = "Wênegeh";
|
||||
@ -182,98 +183,100 @@ Profîla te ji wan ra wiha xuya dike.";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Kontrol bike ka navnîşana e-nameya te rast e û her wiha peldanka xwe ya spam.";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.ResendEmail" = "E-namyê yê dîsa bişîne";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Title" = "E-nameyê xwe kontrol bike";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Description" = "We just sent you an email. Check your junk folder if you haven’t.";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Description" = "Me tenê ji te re e-nameyek şand. Heke nehatiye peldanka xwe ya spamê kontrol bike.";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Mail" = "E-name";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.OpenEmailClient" = "Rajegirê e-nameyê veke";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Title" = "Check your inbox.";
|
||||
"Scene.ConfirmEmail.Subtitle" = "We just sent an email to %@,
|
||||
tap the link to confirm your account.";
|
||||
"Scene.ConfirmEmail.OpenEmailApp.Title" = "Nameyên xwe yên wergirtî kontrol bike.";
|
||||
"Scene.ConfirmEmail.Subtitle" = "Me tenê e-nameyek ji %@ re şand,
|
||||
girêdanê bitikne da ku ajimêra xwe bidî piştrastkirin.";
|
||||
"Scene.ConfirmEmail.Title" = "Tiştekî dawî.";
|
||||
"Scene.Favorite.Title" = "Bijareyên te";
|
||||
"Scene.Favorite.Title" = "Bijarteyên te";
|
||||
"Scene.Follower.Footer" = "Şopîner ji rajekerên din nayê dîtin.";
|
||||
"Scene.Following.Footer" = "Şopandin ji rajekerên din nayê dîtin.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Şandiyên nû bibîne";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Derhêl";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Hate weşandin!";
|
||||
"Scene.HomeTimeline.NavigationBarState.Publishing" = "Şandî tê weşandin...";
|
||||
"Scene.HomeTimeline.Title" = "Serrûpel";
|
||||
"Scene.Notification.Keyobard.ShowEverything" = "Her tiştî nîşan bide";
|
||||
"Scene.Notification.Keyobard.ShowMentions" = "Behskirîya nîşan bike";
|
||||
"Scene.Notification.Keyobard.ShowMentions" = "Qalkirinan nîşan bike";
|
||||
"Scene.Notification.Title.Everything" = "Her tişt";
|
||||
"Scene.Notification.Title.Mentions" = "Behs";
|
||||
"Scene.Notification.UserFavorited Your Post" = "%@ posta we bijarte";
|
||||
"Scene.Notification.Title.Mentions" = "Qalkirin";
|
||||
"Scene.Notification.UserFavorited Your Post" = "%@ şandiya te hez kir";
|
||||
"Scene.Notification.UserFollowedYou" = "%@ te şopand";
|
||||
"Scene.Notification.UserMentionedYou" = "%@ behsa te kir";
|
||||
"Scene.Notification.UserMentionedYou" = "%@ qale te kir";
|
||||
"Scene.Notification.UserRebloggedYourPost" = "%@ posta we ji nû ve tomar kir";
|
||||
"Scene.Notification.UserRequestedToFollowYou" = "%@ daxwaza şopandina te kir";
|
||||
"Scene.Notification.UserYourPollHasEnded" = "%@ Anketa te qediya";
|
||||
"Scene.Notification.UserRequestedToFollowYou" = "%@ dixwazê te bişopîne";
|
||||
"Scene.Notification.UserYourPollHasEnded" = "Rapirsîya te qediya";
|
||||
"Scene.Preview.Keyboard.ClosePreview" = "Pêşdîtin bigire";
|
||||
"Scene.Preview.Keyboard.ShowNext" = "A pêş nîşan bide";
|
||||
"Scene.Preview.Keyboard.ShowPrevious" = "A paş nîşan bide";
|
||||
"Scene.Profile.Dashboard.Followers" = "şopîneran";
|
||||
"Scene.Profile.Dashboard.Followers" = "şopîner";
|
||||
"Scene.Profile.Dashboard.Following" = "dişopîne";
|
||||
"Scene.Profile.Dashboard.Posts" = "şandîyan";
|
||||
"Scene.Profile.Fields.AddRow" = "Rêzê lê zêde bike";
|
||||
"Scene.Profile.Dashboard.Posts" = "şandî";
|
||||
"Scene.Profile.Fields.AddRow" = "Rêzê tevlî bike";
|
||||
"Scene.Profile.Fields.Placeholder.Content" = "Naverok";
|
||||
"Scene.Profile.Fields.Placeholder.Label" = "Nîşan";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Message" = "Ji bo rakirina blokê bipejirin %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Title" = "Hesabê ji bloke rake";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Ji bo vekirina bê dengkirinê bipejirin %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Title" = "Hesabê ji bê deng rake";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Message" = "Ji bo rakirina astengkirinê bipejirîne %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Title" = "Astengiyê li ser ajimêr rake";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Ji bo vekirina bêdengkirinê bipejirîne %@";
|
||||
"Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Title" = "Ajimêrê bêdeng neke";
|
||||
"Scene.Profile.SegmentedControl.Media" = "Medya";
|
||||
"Scene.Profile.SegmentedControl.Posts" = "Şandîyan";
|
||||
"Scene.Profile.SegmentedControl.Replies" = "Bersivan";
|
||||
"Scene.Register.Error.Item.Agreement" = "Lihevhatin";
|
||||
"Scene.Profile.SegmentedControl.Posts" = "Şandî";
|
||||
"Scene.Profile.SegmentedControl.Replies" = "Bersiv";
|
||||
"Scene.Register.Error.Item.Agreement" = "Peyman";
|
||||
"Scene.Register.Error.Item.Email" = "E-name";
|
||||
"Scene.Register.Error.Item.Locale" = "Herêm";
|
||||
"Scene.Register.Error.Item.Password" = "Şîfre";
|
||||
"Scene.Register.Error.Item.Locale" = "Zimanê navrûyê";
|
||||
"Scene.Register.Error.Item.Password" = "Pêborîn";
|
||||
"Scene.Register.Error.Item.Reason" = "Sedem";
|
||||
"Scene.Register.Error.Item.Username" = "Navê bikarhêner";
|
||||
"Scene.Register.Error.Reason.Accepted" = "%@ divê were qebûlkirin";
|
||||
"Scene.Register.Error.Reason.Accepted" = "%@ divê were pejirandin";
|
||||
"Scene.Register.Error.Reason.Blank" = "%@ pêwist e";
|
||||
"Scene.Register.Error.Reason.Blocked" = "%@ peydekerê e-nameya bêdestûr dihewîne";
|
||||
"Scene.Register.Error.Reason.Inclusion" = "%@ nirxeke ku tê destekirin nîn e";
|
||||
"Scene.Register.Error.Reason.Blocked" = "%@ peydekerê e-peyamê yê qedexekirî dihewîne";
|
||||
"Scene.Register.Error.Reason.Inclusion" = "%@ ne nirxek piştgirî ye";
|
||||
"Scene.Register.Error.Reason.Invalid" = "%@ ne derbasdar e";
|
||||
"Scene.Register.Error.Reason.Reserved" = "%@ peyveke mifteya veqetandî ye";
|
||||
"Scene.Register.Error.Reason.Reserved" = "%@ peyveke parastî ye";
|
||||
"Scene.Register.Error.Reason.Taken" = "%@ jixwe tê bikaranîn";
|
||||
"Scene.Register.Error.Reason.TooLong" = "%@ gelekî dirêj e";
|
||||
"Scene.Register.Error.Reason.TooLong" = "%@ pir dirêj e";
|
||||
"Scene.Register.Error.Reason.TooShort" = "%@ pir kurt e";
|
||||
"Scene.Register.Error.Reason.Unreachable" = "%@ xuya nake";
|
||||
"Scene.Register.Error.Special.EmailInvalid" = "Ev ne navnîşana e-nameyek derbasdar e";
|
||||
"Scene.Register.Error.Special.PasswordTooShort" = "Şîfre pir kurt e (divê herî kêm 8 tîpan be)";
|
||||
"Scene.Register.Error.Special.UsernameInvalid" = "Navê bikarhêner divê tenê tîpên alfanumerîk û binxet hebe";
|
||||
"Scene.Register.Error.Reason.Unreachable" = "%@ xuya ye ku tune ye";
|
||||
"Scene.Register.Error.Special.EmailInvalid" = "Ev navnîşaneke e-nameyê ne derbasdar e";
|
||||
"Scene.Register.Error.Special.PasswordTooShort" = "Pêborîn pir kurt e (divê herî kêm 8 tîp be)";
|
||||
"Scene.Register.Error.Special.UsernameInvalid" = "Navê bikarhêner divê tenê ji tîpên alfajimarî û binxêz pêk be";
|
||||
"Scene.Register.Error.Special.UsernameTooLong" = "Navê bikarhêner pir dirêj e (ji 30 tîpan dirêjtir nabe)";
|
||||
"Scene.Register.Input.Avatar.Delete" = "Jê bibe";
|
||||
"Scene.Register.Input.DisplayName.Placeholder" = "navê nîşanê";
|
||||
"Scene.Register.Input.Email.Placeholder" = "e-name";
|
||||
"Scene.Register.Input.Invite.RegistrationUserInviteRequest" = "Tu çima dixwazî beşdar bibî?";
|
||||
"Scene.Register.Input.Password.Hint" = "Şîfreya we herî kêm heşt tîpan hewce dike";
|
||||
"Scene.Register.Input.Password.Placeholder" = "şîfre";
|
||||
"Scene.Register.Input.Password.Hint" = "Pêborîna te herî kêm divê ji 8 tîpan pêk bê";
|
||||
"Scene.Register.Input.Password.Placeholder" = "pêborîn";
|
||||
"Scene.Register.Input.Username.DuplicatePrompt" = "Navê vê bikarhêner tê girtin.";
|
||||
"Scene.Register.Input.Username.Placeholder" = "navê bikarhêner";
|
||||
"Scene.Register.Title" = "Ji me re hinekî qala xwe bike.";
|
||||
"Scene.Report.Content1" = "Are there any other posts you’d like to add to the report?";
|
||||
"Scene.Report.Content2" = "Is there anything the moderators should know about this report?";
|
||||
"Scene.Report.Content1" = "Şandiyên din hene ku tu dixwazî tevlî ragihandinê bikî?";
|
||||
"Scene.Report.Content2" = "Derbarê vê ragihandinê de tiştek heye ku divê çavdêr bizanin?";
|
||||
"Scene.Report.Send" = "Ragihandinê bişîne";
|
||||
"Scene.Report.SkipToSend" = "Bêyî şirove bişîne";
|
||||
"Scene.Report.Step1" = "Gav 1 ji 2";
|
||||
"Scene.Report.Step2" = "Gav 2 ji 2";
|
||||
"Scene.Report.TextPlaceholder" = "Type or paste additional comments";
|
||||
"Scene.Report.TextPlaceholder" = "Şiroveyên daxwazkirê binivîsine an jî pê ve bike";
|
||||
"Scene.Report.Title" = "%@ ragihîne";
|
||||
"Scene.Search.Recommend.Accounts.Description" = "Dibe ku tu bixwazî van hesaban bişopînî";
|
||||
"Scene.Search.Recommend.Accounts.Description" = "Dibe ku tu bixwazî van ajimêran bişopînî";
|
||||
"Scene.Search.Recommend.Accounts.Follow" = "Bişopîne";
|
||||
"Scene.Search.Recommend.Accounts.Title" = "Hesabên ku hûn dikarin hez bikin";
|
||||
"Scene.Search.Recommend.ButtonText" = "Hemûyé bibîne";
|
||||
"Scene.Search.Recommend.HashTag.Description" = "Etîketên ku pir balê dikişînin";
|
||||
"Scene.Search.Recommend.Accounts.Title" = "Ajimêrên ku belkî tu jê hez bikî";
|
||||
"Scene.Search.Recommend.ButtonText" = "Hemûyan bibîne";
|
||||
"Scene.Search.Recommend.HashTag.Description" = "Hashtag ên ku pir balê dikişînin";
|
||||
"Scene.Search.Recommend.HashTag.PeopleTalking" = "%@ kes diaxivin";
|
||||
"Scene.Search.Recommend.HashTag.Title" = "Trend li ser Mastodon";
|
||||
"Scene.Search.SearchBar.Cancel" = "Betal kirin";
|
||||
"Scene.Search.SearchBar.Placeholder" = "Li etîketan û bikarhêneran bigerin";
|
||||
"Scene.Search.Searching.Clear" = "Paqij bike";
|
||||
"Scene.Search.Recommend.HashTag.Title" = "Rojev li ser Mastodon";
|
||||
"Scene.Search.SearchBar.Cancel" = "Dev jê berde";
|
||||
"Scene.Search.SearchBar.Placeholder" = "Li hashtag û bikarhêneran bigere";
|
||||
"Scene.Search.Searching.Clear" = "Pak bike";
|
||||
"Scene.Search.Searching.EmptyState.NoResults" = "Encam tune";
|
||||
"Scene.Search.Searching.RecentSearch" = "Lêgerînên dawî";
|
||||
"Scene.Search.Searching.Segment.All" = "Hemû";
|
||||
"Scene.Search.Searching.Segment.Hashtags" = "Etîketan";
|
||||
"Scene.Search.Searching.Segment.Hashtags" = "Hashtag";
|
||||
"Scene.Search.Searching.Segment.People" = "Mirov";
|
||||
"Scene.Search.Searching.Segment.Posts" = "Şandîyan";
|
||||
"Scene.Search.Searching.Segment.Posts" = "Şandî";
|
||||
"Scene.Search.Title" = "Bigere";
|
||||
"Scene.ServerPicker.Button.Category.Academia" = "akademî";
|
||||
"Scene.ServerPicker.Button.Category.Activism" = "çalakî";
|
||||
@ -291,34 +294,34 @@ tap the link to confirm your account.";
|
||||
"Scene.ServerPicker.Button.Category.Tech" = "teknolojî";
|
||||
"Scene.ServerPicker.Button.SeeLess" = "Kêmtir bibîne";
|
||||
"Scene.ServerPicker.Button.SeeMore" = "Bêtir bibîne";
|
||||
"Scene.ServerPicker.EmptyState.BadNetwork" = "Di dema barkirina daneyan da tiştek xelet derket. Girêdana xwe ya înternetê kontrol bike.";
|
||||
"Scene.ServerPicker.EmptyState.FindingServers" = "Dîtina serverên berdest...";
|
||||
"Scene.ServerPicker.EmptyState.NoResults" = "Encam nade";
|
||||
"Scene.ServerPicker.Input.Placeholder" = "Serverek bibînin an jî beşdarî ya xwe bibin...";
|
||||
"Scene.ServerPicker.Label.Category" = "KATEGORÎ";
|
||||
"Scene.ServerPicker.EmptyState.BadNetwork" = "Di dema barkirina daneyan da çewtî derket. Girêdana xwe ya înternetê kontrol bike.";
|
||||
"Scene.ServerPicker.EmptyState.FindingServers" = "Peydakirina rajekarên berdest...";
|
||||
"Scene.ServerPicker.EmptyState.NoResults" = "Encam tune";
|
||||
"Scene.ServerPicker.Input.Placeholder" = "Rajekarekî bibîne an jî beşdarî ya xwe bibe...";
|
||||
"Scene.ServerPicker.Label.Category" = "BEŞ";
|
||||
"Scene.ServerPicker.Label.Language" = "ZIMAN";
|
||||
"Scene.ServerPicker.Label.Users" = "BIKARHÊNER";
|
||||
"Scene.ServerPicker.Title" = "Rajekarekê hilbijêre,
|
||||
Her kîjan rajekar be.";
|
||||
"Scene.ServerRules.Button.Confirm" = "Ez tev dibim";
|
||||
"Scene.ServerRules.PrivacyPolicy" = "polîtîkaya nepenîtiyê";
|
||||
"Scene.ServerRules.Prompt" = "Bi berdewamî, hûn ji bo %@ di bin şertên polîtîkaya xizmet û nepenîtiyê da ne.";
|
||||
"Scene.ServerRules.Button.Confirm" = "Ez dipejirînim";
|
||||
"Scene.ServerRules.PrivacyPolicy" = "polîtikaya nihêniyê";
|
||||
"Scene.ServerRules.Prompt" = "Bi domandinê, tu ji bo %@ di bin mercên bikaranînê û polîtîkaya nepenîtiyê dipejirînî.";
|
||||
"Scene.ServerRules.Subtitle" = "Ev rêzik ji aliyê rêvebirên %@ ve tên sazkirin.";
|
||||
"Scene.ServerRules.TermsOfService" = "şert û mercên xizmetê";
|
||||
"Scene.ServerRules.Title" = "Hin qaîdeyên bingehîn.";
|
||||
"Scene.Settings.Footer.MastodonDescription" = "Mastodon is open source software. You can report issues on GitHub at %@ (%@)";
|
||||
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Close Settings Window";
|
||||
"Scene.ServerRules.TermsOfService" = "mercên bikaranînê";
|
||||
"Scene.ServerRules.Title" = "Hinek rêzikên bingehîn.";
|
||||
"Scene.Settings.Footer.MastodonDescription" = "Mastodon nermalava çavkaniya vekirî ye. Tu dikarî pirsgirêkan li ser GitHub-ê ragihînî di %@ (%@) de";
|
||||
"Scene.Settings.Keyboard.CloseSettingsWindow" = "Sazkariyên çarçoveyê bigire";
|
||||
"Scene.Settings.Section.Appearance.Automatic" = "Xweber";
|
||||
"Scene.Settings.Section.Appearance.Dark" = "Her dem tarî";
|
||||
"Scene.Settings.Section.Appearance.Light" = "Her dem ronî";
|
||||
"Scene.Settings.Section.Appearance.Title" = "Xuyang";
|
||||
"Scene.Settings.Section.BoringZone.AccountSettings" = "Account Settings";
|
||||
"Scene.Settings.Section.BoringZone.Privacy" = "Privacy Policy";
|
||||
"Scene.Settings.Section.BoringZone.Terms" = "Terms of Service";
|
||||
"Scene.Settings.Section.BoringZone.Title" = "The Boring Zone";
|
||||
"Scene.Settings.Section.Notifications.Boosts" = "Reblogs my post";
|
||||
"Scene.Settings.Section.BoringZone.AccountSettings" = "Sazkariyên ajimêr";
|
||||
"Scene.Settings.Section.BoringZone.Privacy" = "Polîtikaya nihêniyê";
|
||||
"Scene.Settings.Section.BoringZone.Terms" = "Mercên bikaranînê";
|
||||
"Scene.Settings.Section.BoringZone.Title" = "Devera acizker";
|
||||
"Scene.Settings.Section.Notifications.Boosts" = "Şandiya min ji nû ve nivîsand";
|
||||
"Scene.Settings.Section.Notifications.Favorites" = "Şandiyên min hez kir";
|
||||
"Scene.Settings.Section.Notifications.Follows" = "Min şopand";
|
||||
"Scene.Settings.Section.Notifications.Follows" = "Min dişopîne";
|
||||
"Scene.Settings.Section.Notifications.Mentions" = "Qale min kir";
|
||||
"Scene.Settings.Section.Notifications.Title" = "Agahdarî";
|
||||
"Scene.Settings.Section.Notifications.Trigger.Anyone" = "her kes";
|
||||
@ -326,21 +329,21 @@ Her kîjan rajekar be.";
|
||||
"Scene.Settings.Section.Notifications.Trigger.Follower" = "şopînerek";
|
||||
"Scene.Settings.Section.Notifications.Trigger.Noone" = "ne yek";
|
||||
"Scene.Settings.Section.Notifications.Trigger.Title" = "Min agahdar bike gava";
|
||||
"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "Disable animated avatars";
|
||||
"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "Disable animated emojis";
|
||||
"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "Avatarên anîmasyonî neçalak bike";
|
||||
"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "Emojiyên anîmasyonî neçalak bike";
|
||||
"Scene.Settings.Section.Preference.Title" = "Hilbijarte";
|
||||
"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "True black dark mode";
|
||||
"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Use default browser to open links";
|
||||
"Scene.Settings.Section.SpicyZone.Clear" = "Clear Media Cache";
|
||||
"Scene.Settings.Section.SpicyZone.Signout" = "Sign Out";
|
||||
"Scene.Settings.Section.SpicyZone.Title" = "The Spicy Zone";
|
||||
"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "Moda tarî ya reş a rastîn";
|
||||
"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Ji bo vekirina girêdanan geroka berdest bi kar bîne";
|
||||
"Scene.Settings.Section.SpicyZone.Clear" = "Pêşbîra medyayê pak bike";
|
||||
"Scene.Settings.Section.SpicyZone.Signout" = "Derkeve";
|
||||
"Scene.Settings.Section.SpicyZone.Title" = "Devera germ";
|
||||
"Scene.Settings.Title" = "Sazkarî";
|
||||
"Scene.SuggestionAccount.FollowExplain" = "Gava tu kesekî dişopînî, tu yê şandiyê wan di serrûpelê de bibîne.";
|
||||
"Scene.SuggestionAccount.Title" = "Kesên bo ku bişopînî bibîne";
|
||||
"Scene.Thread.BackTitle" = "Şandî";
|
||||
"Scene.Thread.Title" = "Post from %@";
|
||||
"Scene.Thread.Title" = "Şandî ji %@";
|
||||
"Scene.Welcome.Slogan" = "Torên civakî
|
||||
di destên te de.";
|
||||
"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard";
|
||||
"Scene.Wizard.AccessibilityHint" = "Du caran bitikîne da ku çarçoveyahilpekok ji holê rakî";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Dest bide ser bişkoja profîlê da ku di navbera gelek ajimêrann de biguherînî.";
|
||||
"Scene.Wizard.NewInMastodon" = "Nû di Mastodon de";
|
@ -109,9 +109,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>ld</string>
|
||||
<key>one</key>
|
||||
<string>1 reblog</string>
|
||||
<string>1 ji nû ve nivîsandin</string>
|
||||
<key>other</key>
|
||||
<string>%ld reblogs</string>
|
||||
<string>%ld ji nû ve nivîsandin</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>plural.count.vote</key>
|
||||
|
80
Mastodon/Resources/local-codes.json
Normal file
80
Mastodon/Resources/local-codes.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"af": "Afrikaans",
|
||||
"ar": "العربية",
|
||||
"ast": "Asturianu",
|
||||
"bg": "Български",
|
||||
"bn": "বাংলা",
|
||||
"br": "Breton",
|
||||
"ca": "Català",
|
||||
"co": "Corsu",
|
||||
"cs": "Čeština",
|
||||
"cy": "Cymraeg",
|
||||
"da": "Dansk",
|
||||
"de": "Deutsch",
|
||||
"el": "Ελληνικά",
|
||||
"en": "English",
|
||||
"eo": "Esperanto",
|
||||
"es-AR": "Español (Argentina)",
|
||||
"es": "Español",
|
||||
"et": "Eesti",
|
||||
"eu": "Euskara",
|
||||
"fa": "فارسی",
|
||||
"fi": "Suomi",
|
||||
"fr": "Français",
|
||||
"ga": "Gaeilge",
|
||||
"gd": "Gàidhlig",
|
||||
"gl": "Galego",
|
||||
"he": "עברית",
|
||||
"hi": "हिन्दी",
|
||||
"hr": "Hrvatski",
|
||||
"hu": "Magyar",
|
||||
"hy": "Հայերեն",
|
||||
"id": "Bahasa Indonesia",
|
||||
"io": "Ido",
|
||||
"is": "Íslenska",
|
||||
"it": "Italiano",
|
||||
"ja": "日本語",
|
||||
"ka": "ქართული",
|
||||
"kab": "Taqbaylit",
|
||||
"kk": "Қазақша",
|
||||
"kn": "ಕನ್ನಡ",
|
||||
"ko": "한국어",
|
||||
"ku": "سۆرانی",
|
||||
"lt": "Lietuvių",
|
||||
"lv": "Latviešu",
|
||||
"mk": "Македонски",
|
||||
"ml": "മലയാളം",
|
||||
"mr": "मराठी",
|
||||
"ms": "Bahasa Melayu",
|
||||
"nl": "Nederlands",
|
||||
"nn": "Nynorsk",
|
||||
"no": "Norsk",
|
||||
"oc": "Occitan",
|
||||
"pl": "Polski",
|
||||
"pt-BR": "Português (Brasil)",
|
||||
"pt-PT": "Português (Portugal)",
|
||||
"pt": "Português",
|
||||
"ro": "Română",
|
||||
"ru": "Русский",
|
||||
"sa": "संस्कृतम्",
|
||||
"sc": "Sardu",
|
||||
"si": "සිංහල",
|
||||
"sk": "Slovenčina",
|
||||
"sl": "Slovenščina",
|
||||
"sq": "Shqip",
|
||||
"sr-Latn": "Srpski (latinica)",
|
||||
"sr": "Српски",
|
||||
"sv": "Svenska",
|
||||
"ta": "தமிழ்",
|
||||
"te": "తెలుగు",
|
||||
"th": "ไทย",
|
||||
"tr": "Türkçe",
|
||||
"uk": "Українська",
|
||||
"ur": "اُردُو",
|
||||
"vi": "Tiếng Việt",
|
||||
"zgh": "ⵜⴰⵎⴰⵣⵉⵖⵜ",
|
||||
"zh-CN": "简体中文",
|
||||
"zh-HK": "繁體中文(香港)",
|
||||
"zh-TW": "繁體中文(臺灣)",
|
||||
"zh": "中文"
|
||||
}
|
@ -27,6 +27,7 @@
|
||||
"Common.Controls.Actions.Back" = "Terug";
|
||||
"Common.Controls.Actions.BlockDomain" = "Blokkeer %@";
|
||||
"Common.Controls.Actions.Cancel" = "Annuleren";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "Bevestigen";
|
||||
"Common.Controls.Actions.Continue" = "Doorgaan";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Foto kopiëren";
|
||||
@ -184,6 +185,8 @@ Uw profiel ziet er zo uit voor hen.";
|
||||
klik op de link om uw account te bevestigen.";
|
||||
"Scene.ConfirmEmail.Title" = "Nog één ding.";
|
||||
"Scene.Favorite.Title" = "Uw favorieten";
|
||||
"Scene.Follower.Footer" = "Followers from other servers are not displayed.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Bekijk nieuwe berichten";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Offline";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Gepubliceerd!";
|
||||
|
@ -28,6 +28,7 @@
|
||||
"Common.Controls.Actions.Back" = "Назад";
|
||||
"Common.Controls.Actions.BlockDomain" = "Заблокировать %@";
|
||||
"Common.Controls.Actions.Cancel" = "Отмена";
|
||||
"Common.Controls.Actions.Compose" = "Compose";
|
||||
"Common.Controls.Actions.Confirm" = "Подтвердить";
|
||||
"Common.Controls.Actions.Continue" = "Продолжить";
|
||||
"Common.Controls.Actions.CopyPhoto" = "Скопировать изображение";
|
||||
@ -200,6 +201,8 @@
|
||||
подтвердить свою учётную запись.";
|
||||
"Scene.ConfirmEmail.Title" = "И ещё кое-что.";
|
||||
"Scene.Favorite.Title" = "Ваше избранное";
|
||||
"Scene.Follower.Footer" = "Followers from other servers are not displayed.";
|
||||
"Scene.Following.Footer" = "Follows from other servers are not displayed.";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "Показать новые";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "Не в сети";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "Опубликовано!";
|
||||
|
@ -28,6 +28,7 @@
|
||||
"Common.Controls.Actions.Back" = "ย้อนกลับ";
|
||||
"Common.Controls.Actions.BlockDomain" = "ปิดกั้น %@";
|
||||
"Common.Controls.Actions.Cancel" = "ยกเลิก";
|
||||
"Common.Controls.Actions.Compose" = "เขียน";
|
||||
"Common.Controls.Actions.Confirm" = "ยืนยัน";
|
||||
"Common.Controls.Actions.Continue" = "ดำเนินการต่อ";
|
||||
"Common.Controls.Actions.CopyPhoto" = "คัดลอกรูปภาพ";
|
||||
@ -190,6 +191,8 @@
|
||||
แตะที่ลิงก์เพื่อยืนยันบัญชีของคุณ";
|
||||
"Scene.ConfirmEmail.Title" = "หนึ่งสิ่งสุดท้าย";
|
||||
"Scene.Favorite.Title" = "รายการโปรดของคุณ";
|
||||
"Scene.Follower.Footer" = "ไม่ได้แสดงผู้ติดตามจากเซิร์ฟเวอร์อื่น ๆ";
|
||||
"Scene.Following.Footer" = "ไม่ได้แสดงการติดตามจากเซิร์ฟเวอร์อื่น ๆ";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "ดูโพสต์ใหม่";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "ออฟไลน์";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "เผยแพร่แล้ว!";
|
||||
|
@ -28,6 +28,7 @@
|
||||
"Common.Controls.Actions.Back" = "返回";
|
||||
"Common.Controls.Actions.BlockDomain" = "屏蔽 %@";
|
||||
"Common.Controls.Actions.Cancel" = "取消";
|
||||
"Common.Controls.Actions.Compose" = "撰写";
|
||||
"Common.Controls.Actions.Confirm" = "确认";
|
||||
"Common.Controls.Actions.Continue" = "继续";
|
||||
"Common.Controls.Actions.CopyPhoto" = "拷贝照片";
|
||||
@ -190,6 +191,8 @@
|
||||
点击链接确认你的帐户。";
|
||||
"Scene.ConfirmEmail.Title" = "最后一件事。";
|
||||
"Scene.Favorite.Title" = "你的喜欢";
|
||||
"Scene.Follower.Footer" = "不会显示来自其它服务器的关注者";
|
||||
"Scene.Following.Footer" = "不会显示来自其它服务器的关注";
|
||||
"Scene.HomeTimeline.NavigationBarState.NewPosts" = "查看新帖子";
|
||||
"Scene.HomeTimeline.NavigationBarState.Offline" = "离线";
|
||||
"Scene.HomeTimeline.NavigationBarState.Published" = "已发送";
|
||||
|
@ -75,7 +75,7 @@ extension AccountListViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = ThemeService.shared.currentTheme.value.systemBackgroundColor.withAlphaComponent(0.9)
|
||||
setupBackgroundColor(theme: ThemeService.shared.currentTheme.value)
|
||||
ThemeService.shared.currentTheme
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] theme in
|
||||
@ -131,7 +131,15 @@ extension AccountListViewController {
|
||||
}
|
||||
|
||||
private func setupBackgroundColor(theme: Theme) {
|
||||
view.backgroundColor = theme.systemBackgroundColor.withAlphaComponent(0.9)
|
||||
let backgroundColor = UIColor { traitCollection in
|
||||
switch traitCollection.userInterfaceLevel {
|
||||
case .elevated where traitCollection.userInterfaceStyle == .dark:
|
||||
return theme.systemElevatedBackgroundColor
|
||||
default:
|
||||
return theme.systemBackgroundColor.withAlphaComponent(0.9)
|
||||
}
|
||||
}
|
||||
view.backgroundColor = backgroundColor
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import MetaTextKit
|
||||
|
||||
final class AccountListTableViewCell: UITableViewCell {
|
||||
|
||||
private var _disposeBag = Set<AnyCancellable>()
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
let avatarButton = CircleAvatarButton(frame: .zero)
|
||||
@ -47,6 +48,15 @@ final class AccountListTableViewCell: UITableViewCell {
|
||||
extension AccountListTableViewCell {
|
||||
|
||||
private func _init() {
|
||||
backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemGroupedBackgroundColor
|
||||
ThemeService.shared.currentTheme
|
||||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] theme in
|
||||
guard let self = self else { return }
|
||||
self.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemGroupedBackgroundColor
|
||||
}
|
||||
.store(in: &_disposeBag)
|
||||
|
||||
avatarButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(avatarButton)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -6,10 +6,13 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
import MetaTextKit
|
||||
|
||||
final class AddAccountTableViewCell: UITableViewCell {
|
||||
|
||||
private var _disposeBag = Set<AnyCancellable>()
|
||||
|
||||
let iconImageView: UIImageView = {
|
||||
let image = UIImage(systemName: "plus.circle.fill")!
|
||||
let imageView = UIImageView(image: image)
|
||||
@ -41,6 +44,15 @@ final class AddAccountTableViewCell: UITableViewCell {
|
||||
extension AddAccountTableViewCell {
|
||||
|
||||
private func _init() {
|
||||
backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemGroupedBackgroundColor
|
||||
ThemeService.shared.currentTheme
|
||||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] theme in
|
||||
guard let self = self else { return }
|
||||
self.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemGroupedBackgroundColor
|
||||
}
|
||||
.store(in: &_disposeBag)
|
||||
|
||||
iconImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(iconImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -15,6 +15,7 @@ import FLEX
|
||||
import SwiftUI
|
||||
import MastodonUI
|
||||
import MastodonSDK
|
||||
import StoreKit
|
||||
|
||||
extension HomeTimelineViewController {
|
||||
var debugMenu: UIMenu {
|
||||
@ -77,6 +78,11 @@ extension HomeTimelineViewController {
|
||||
guard let self = self else { return }
|
||||
self.showThreadAction(action)
|
||||
},
|
||||
UIAction(title: "Store Rating", image: UIImage(systemName: "star.fill"), attributes: []) { [weak self] action in
|
||||
guard let self = self else { return }
|
||||
guard let windowScene = self.view.window?.windowScene else { return }
|
||||
SKStoreReviewController.requestReview(in: windowScene)
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import CoreDataStack
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
import AlamofireImage
|
||||
import StoreKit
|
||||
|
||||
final class HomeTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
|
||||
|
||||
@ -144,6 +145,21 @@ extension HomeTimelineViewController {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
viewModel.homeTimelineNavigationBarTitleViewModel.state
|
||||
.removeDuplicates()
|
||||
.filter { $0 == .publishedButton }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard UserDefaults.shared.lastVersionPromptedForReview == nil else { return }
|
||||
guard UserDefaults.shared.processCompletedCount > 3 else { return }
|
||||
guard let windowScene = self.view.window?.windowScene else { return }
|
||||
let version = UIApplication.appVersion()
|
||||
UserDefaults.shared.lastVersionPromptedForReview = version
|
||||
SKStoreReviewController.requestReview(in: windowScene)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
tableView.refreshControl = refreshControl
|
||||
refreshControl.addTarget(self, action: #selector(HomeTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
||||
|
||||
|
@ -19,21 +19,25 @@ extension NotificationViewController: StatusProvider {
|
||||
|
||||
func status(for cell: UITableViewCell?, indexPath: IndexPath?) -> Future<Status?, Never> {
|
||||
return Future<Status?, Never> { promise in
|
||||
guard let cell = cell,
|
||||
let diffableDataSource = self.viewModel.diffableDataSource,
|
||||
let indexPath = self.tableView.indexPath(for: cell),
|
||||
guard let diffableDataSource = self.viewModel.diffableDataSource else {
|
||||
assertionFailure()
|
||||
promise(.success(nil))
|
||||
return
|
||||
}
|
||||
guard let indexPath = indexPath ?? cell.flatMap({ self.tableView.indexPath(for: $0) }),
|
||||
let item = diffableDataSource.itemIdentifier(for: indexPath) else {
|
||||
promise(.success(nil))
|
||||
return
|
||||
}
|
||||
|
||||
switch item {
|
||||
case .notification(let objectID, _):
|
||||
case .notification(let objectID, _),
|
||||
.notificationStatus(let objectID, _):
|
||||
self.viewModel.fetchedResultsController.managedObjectContext.perform {
|
||||
let notification = self.viewModel.fetchedResultsController.managedObjectContext.object(with: objectID) as! MastodonNotification
|
||||
promise(.success(notification.status))
|
||||
}
|
||||
default:
|
||||
case .bottomLoader:
|
||||
promise(.success(nil))
|
||||
}
|
||||
}
|
||||
@ -68,3 +72,6 @@ extension NotificationViewController: StatusProvider {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UserProvider
|
||||
extension NotificationViewController: UserProvider { }
|
||||
|
@ -14,8 +14,10 @@ import OSLog
|
||||
import UIKit
|
||||
import Meta
|
||||
import MetaTextKit
|
||||
import AVKit
|
||||
|
||||
final class NotificationViewController: UIViewController, NeedsDependency {
|
||||
final class NotificationViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
@ -23,15 +25,18 @@ final class NotificationViewController: UIViewController, NeedsDependency {
|
||||
var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
private(set) lazy var viewModel = NotificationViewModel(context: context)
|
||||
|
||||
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
||||
|
||||
let segmentControl: UISegmentedControl = {
|
||||
let control = UISegmentedControl(items: [L10n.Scene.Notification.Title.everything, L10n.Scene.Notification.Title.mentions])
|
||||
control.selectedSegmentIndex = NotificationViewModel.NotificationSegment.EveryThing.rawValue
|
||||
control.selectedSegmentIndex = NotificationViewModel.NotificationSegment.everyThing.rawValue
|
||||
return control
|
||||
}()
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.register(StatusTableViewCell.self, forCellReuseIdentifier: String(describing: StatusTableViewCell.self))
|
||||
tableView.register(NotificationStatusTableViewCell.self, forCellReuseIdentifier: String(describing: NotificationStatusTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.estimatedRowHeight = UITableView.automaticDimension
|
||||
@ -82,7 +87,12 @@ extension NotificationViewController {
|
||||
tableView.delegate = self
|
||||
viewModel.tableView = tableView
|
||||
viewModel.contentOffsetAdjustableTimelineViewControllerDelegate = self
|
||||
viewModel.setupDiffableDataSource(for: tableView, delegate: self, dependency: self)
|
||||
viewModel.setupDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: self,
|
||||
delegate: self,
|
||||
statusTableViewCellDelegate: self
|
||||
)
|
||||
viewModel.viewDidLoad.send()
|
||||
|
||||
// bind refresh control
|
||||
@ -128,9 +138,9 @@ extension NotificationViewController {
|
||||
self.viewModel.needsScrollToTopAfterDataSourceUpdate = true
|
||||
|
||||
switch segment {
|
||||
case .EveryThing:
|
||||
case .everyThing:
|
||||
self.viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain, userID: userID)
|
||||
case .Mentions:
|
||||
case .mentions:
|
||||
self.viewModel.notificationPredicate.value = MastodonNotification.predicate(domain: domain, userID: userID, typeRaw: Mastodon.Entity.Notification.NotificationType.mention.rawValue)
|
||||
}
|
||||
}
|
||||
@ -148,8 +158,8 @@ extension NotificationViewController {
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
tableView.deselectRow(with: transitionCoordinator, animated: animated)
|
||||
|
||||
aspectViewWillAppear(animated)
|
||||
|
||||
// fetch latest notification when scroll position is within half screen height to prevent list reload
|
||||
if tableView.contentOffset.y < view.frame.height * 0.5 {
|
||||
@ -181,6 +191,12 @@ extension NotificationViewController {
|
||||
// reset notification count
|
||||
context.notificationService.clearNotificationCountForActiveUser()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
aspectViewDidDisappear(animated)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
@ -208,33 +224,34 @@ extension NotificationViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StatusTableViewControllerAspect
|
||||
extension NotificationViewController: StatusTableViewControllerAspect { }
|
||||
|
||||
extension NotificationViewController {
|
||||
|
||||
// MARK: - TableViewCellHeightCacheableContainer
|
||||
extension NotificationViewController: TableViewCellHeightCacheableContainer {
|
||||
var cellFrameCache: NSCache<NSNumber, NSValue> { return viewModel.cellFrameCache }
|
||||
|
||||
func cacheTableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
switch item {
|
||||
case .notification(let objectID, _):
|
||||
case .notification(let objectID, _),
|
||||
.notificationStatus(let objectID, _):
|
||||
guard let object = try? viewModel.fetchedResultsController.managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { return }
|
||||
let key = object.id as NSString
|
||||
let key = object.objectID.hashValue
|
||||
let frame = cell.frame
|
||||
viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: key)
|
||||
viewModel.cellFrameCache.setObject(NSValue(cgRect: frame), forKey: NSNumber(value: key))
|
||||
case .bottomLoader:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func handleTableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return UITableView.automaticDimension }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return UITableView.automaticDimension }
|
||||
switch item {
|
||||
case .notification(let objectID, _):
|
||||
case .notification(let objectID, _),
|
||||
.notificationStatus(let objectID, _):
|
||||
guard let object = try? viewModel.fetchedResultsController.managedObjectContext.existingObject(with: objectID) as? MastodonNotification else { return UITableView.automaticDimension }
|
||||
let key = object.id as NSString
|
||||
guard let frame = viewModel.cellFrameCache.object(forKey: key)?.cgRectValue else { return UITableView.automaticDimension }
|
||||
let key = object.objectID.hashValue
|
||||
guard let frame = viewModel.cellFrameCache.object(forKey: NSNumber(value: key))?.cgRectValue else { return UITableView.automaticDimension }
|
||||
return frame.height
|
||||
case .bottomLoader:
|
||||
return TimelineLoaderTableViewCell.cellHeight
|
||||
@ -242,22 +259,55 @@ extension NotificationViewController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - StatusTableViewControllerAspect
|
||||
extension NotificationViewController: StatusTableViewControllerAspect { }
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension NotificationViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
aspectTableView(tableView, estimatedHeightForRowAt: indexPath)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
open(item: item)
|
||||
switch item {
|
||||
case .notificationStatus:
|
||||
aspectTableView(tableView, willDisplay: cell, forRowAt: indexPath)
|
||||
case .bottomLoader:
|
||||
if !tableView.isDragging, !tableView.isDecelerating {
|
||||
viewModel.loadOldestStateMachine.enter(NotificationViewModel.LoadOldestState.Loading.self)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
cacheTableView(tableView, didEndDisplaying: cell, forRowAt: indexPath)
|
||||
aspectTableView(tableView, didEndDisplaying: cell, forRowAt: indexPath)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
aspectTableView(tableView, didSelectRowAt: indexPath)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return handleTableView(tableView, estimatedHeightForRowAt: indexPath)
|
||||
func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||
return aspectTableView(tableView, contextMenuConfigurationForRowAt: indexPath, point: point)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
||||
return aspectTableView(tableView, previewForHighlightingContextMenuWithConfiguration: configuration)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
|
||||
return aspectTableView(tableView, previewForDismissingContextMenuWithConfiguration: configuration)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
||||
aspectTableView(tableView, willPerformPreviewActionForMenuWith: configuration, animator: animator)
|
||||
}
|
||||
|
||||
}
|
||||
@ -278,19 +328,6 @@ extension NotificationViewController {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
switch item {
|
||||
case .bottomLoader:
|
||||
if !tableView.isDragging, !tableView.isDecelerating {
|
||||
viewModel.loadOldestStateMachine.enter(NotificationViewModel.LoadOldestState.Loading.self)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ContentOffsetAdjustableTimelineViewControllerDelegate
|
||||
@ -388,6 +425,7 @@ extension NotificationViewController: ScrollViewContainer {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LoadMoreConfigurableTableViewContainer
|
||||
extension NotificationViewController: LoadMoreConfigurableTableViewContainer {
|
||||
typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
||||
typealias LoadingState = NotificationViewModel.LoadOldestState.Loading
|
||||
@ -395,6 +433,24 @@ extension NotificationViewController: LoadMoreConfigurableTableViewContainer {
|
||||
var loadMoreConfigurableStateMachine: GKStateMachine { viewModel.loadOldestStateMachine }
|
||||
}
|
||||
|
||||
// MARK: - AVPlayerViewControllerDelegate
|
||||
extension NotificationViewController: AVPlayerViewControllerDelegate {
|
||||
func playerViewController(_ playerViewController: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
||||
handlePlayerViewController(playerViewController, willBeginFullScreenPresentationWithAnimationCoordinator: coordinator)
|
||||
}
|
||||
|
||||
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
||||
handlePlayerViewController(playerViewController, willEndFullScreenPresentationWithAnimationCoordinator: coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - statusTableViewCellDelegate
|
||||
extension NotificationViewController: StatusTableViewCellDelegate {
|
||||
var playerViewControllerDelegate: AVPlayerViewControllerDelegate? {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
extension NotificationViewController {
|
||||
|
||||
enum CategorySwitch: String, CaseIterable {
|
||||
@ -452,9 +508,9 @@ extension NotificationViewController {
|
||||
|
||||
switch category {
|
||||
case .showEverything:
|
||||
viewModel.selectedIndex.value = .EveryThing
|
||||
viewModel.selectedIndex.value = .everyThing
|
||||
case .showMentions:
|
||||
viewModel.selectedIndex.value = .Mentions
|
||||
viewModel.selectedIndex.value = .mentions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,14 +14,16 @@ import MastodonSDK
|
||||
extension NotificationViewModel {
|
||||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency,
|
||||
delegate: NotificationTableViewCellDelegate,
|
||||
dependency: NeedsDependency
|
||||
statusTableViewCellDelegate: StatusTableViewCellDelegate
|
||||
) {
|
||||
diffableDataSource = NotificationSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: fetchedResultsController.managedObjectContext,
|
||||
delegate: delegate,
|
||||
dependency: dependency
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate
|
||||
)
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<NotificationSection, NotificationItem>()
|
||||
@ -81,11 +83,23 @@ extension NotificationViewModel: NSFetchedResultsControllerDelegate {
|
||||
}
|
||||
var newSnapshot = NSDiffableDataSourceSnapshot<NotificationSection, NotificationItem>()
|
||||
newSnapshot.appendSections([.main])
|
||||
let items: [NotificationItem] = notifications.map { notification in
|
||||
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
|
||||
return NotificationItem.notification(objectID: notification.objectID, attribute: attribute)
|
||||
|
||||
let segment = self.selectedIndex.value
|
||||
switch segment {
|
||||
case .everyThing:
|
||||
let items: [NotificationItem] = notifications.map { notification in
|
||||
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
|
||||
return NotificationItem.notification(objectID: notification.objectID, attribute: attribute)
|
||||
}
|
||||
newSnapshot.appendItems(items, toSection: .main)
|
||||
case .mentions:
|
||||
let items: [NotificationItem] = notifications.map { notification in
|
||||
let attribute: Item.StatusAttribute = oldSnapshotAttributeDict[notification.objectID] ?? Item.StatusAttribute()
|
||||
return NotificationItem.notificationStatus(objectID: notification.objectID, attribute: attribute)
|
||||
}
|
||||
newSnapshot.appendItems(items, toSection: .main)
|
||||
}
|
||||
newSnapshot.appendItems(items, toSection: .main)
|
||||
|
||||
if !notifications.isEmpty, self.noMoreNotification.value == false {
|
||||
newSnapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
}
|
||||
|
@ -92,13 +92,13 @@ extension NotificationViewModel.LoadOldestState {
|
||||
} receiveValue: { [weak viewModel] response in
|
||||
guard let viewModel = viewModel else { return }
|
||||
switch viewModel.selectedIndex.value {
|
||||
case .EveryThing:
|
||||
case .everyThing:
|
||||
if response.value.isEmpty {
|
||||
stateMachine.enter(NoMore.self)
|
||||
} else {
|
||||
stateMachine.enter(Idle.self)
|
||||
}
|
||||
case .Mentions:
|
||||
case .mentions:
|
||||
viewModel.noMoreNotification.value = response.value.isEmpty
|
||||
let list = response.value.filter { $0.type == Mastodon.Entity.Notification.NotificationType.mention }
|
||||
if list.isEmpty {
|
||||
|
@ -23,13 +23,13 @@ final class NotificationViewModel: NSObject {
|
||||
weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
|
||||
|
||||
let viewDidLoad = PassthroughSubject<Void, Never>()
|
||||
let selectedIndex = CurrentValueSubject<NotificationSegment, Never>(.EveryThing)
|
||||
let selectedIndex = CurrentValueSubject<NotificationSegment, Never>(.everyThing)
|
||||
let noMoreNotification = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
let activeMastodonAuthenticationBox: CurrentValueSubject<MastodonAuthenticationBox?, Never>
|
||||
let fetchedResultsController: NSFetchedResultsController<MastodonNotification>!
|
||||
let notificationPredicate = CurrentValueSubject<NSPredicate?, Never>(nil)
|
||||
let cellFrameCache = NSCache<NSString, NSValue>()
|
||||
let cellFrameCache = NSCache<NSNumber, NSValue>()
|
||||
|
||||
var needsScrollToTopAfterDataSourceUpdate = false
|
||||
let dataSourceDidUpdated = PassthroughSubject<Void, Never>()
|
||||
@ -161,7 +161,7 @@ final class NotificationViewModel: NSObject {
|
||||
|
||||
extension NotificationViewModel {
|
||||
enum NotificationSegment: Int {
|
||||
case EveryThing
|
||||
case Mentions
|
||||
case everyThing
|
||||
case mentions
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ extension MastodonRegisterViewController {
|
||||
view.addGestureRecognizer(tapGestureRecognizer)
|
||||
tapGestureRecognizer.addTarget(self, action: #selector(tapGestureRecognizerHandler))
|
||||
|
||||
// stackview
|
||||
// stackView
|
||||
stackView.axis = .vertical
|
||||
stackView.distribution = .fill
|
||||
stackView.spacing = 40
|
||||
@ -370,7 +370,7 @@ extension MastodonRegisterViewController {
|
||||
scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),
|
||||
])
|
||||
|
||||
// stackview
|
||||
// stackView
|
||||
scrollView.addSubview(stackView)
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
@ -802,23 +802,48 @@ extension MastodonRegisterViewController {
|
||||
let password = viewModel.password.value
|
||||
|
||||
let locale: String = {
|
||||
let fallbackLanguageCode = Locale.current.languageCode ?? "en"
|
||||
guard let url = Bundle.main.url(forResource: "local-codes", withExtension: "json"),
|
||||
let data = try? Data(contentsOf: url),
|
||||
let localCode = try? JSONDecoder().decode(MastodonLocalCode.self, from: data)
|
||||
else {
|
||||
assertionFailure()
|
||||
return "en"
|
||||
}
|
||||
let fallbackLanguageCode: String = {
|
||||
let code = Locale.current.languageCode ?? "en"
|
||||
guard localCode[code] != nil else { return "en" }
|
||||
return code
|
||||
}()
|
||||
|
||||
// pick device preferred language
|
||||
guard let identifier = Locale.preferredLanguages.first else {
|
||||
return fallbackLanguageCode
|
||||
}
|
||||
// prepare languageCode and validate then return fallback if needs
|
||||
let local = Locale(identifier: identifier)
|
||||
guard let languageCode = local.languageCode else {
|
||||
guard let languageCode = local.languageCode,
|
||||
localCode[languageCode] != nil
|
||||
else {
|
||||
return fallbackLanguageCode
|
||||
}
|
||||
switch languageCode {
|
||||
case "zh":
|
||||
// Check Simplified Chinese / Traditional Chinese
|
||||
// https://github.com/gunchleoc/mastodon/blob/ed6153b8f24d3a8f5a124cc95683bd1f20aec882/app/helpers/settings_helper.rb
|
||||
guard let regionCode = local.regionCode else { return languageCode }
|
||||
return "zh" + "-" + regionCode
|
||||
default:
|
||||
// prepare extendCode and validate then return fallback if needs
|
||||
let extendCodes: [String] = {
|
||||
let locales = Locale.preferredLanguages.map { Locale(identifier: $0) }
|
||||
return locales.compactMap { locale in
|
||||
guard let languageCode = locale.languageCode,
|
||||
let regionCode = locale.regionCode
|
||||
else { return nil }
|
||||
return languageCode + "-" + regionCode
|
||||
}
|
||||
}()
|
||||
let _firstMatchExtendCode = extendCodes.first { code in
|
||||
localCode[code] != nil
|
||||
}
|
||||
guard let firstMatchExtendCode = _firstMatchExtendCode else {
|
||||
return languageCode
|
||||
}
|
||||
return firstMatchExtendCode
|
||||
|
||||
}()
|
||||
let query = Mastodon.API.Account.RegisterQuery(
|
||||
reason: viewModel.reason.value,
|
||||
@ -828,6 +853,8 @@ extension MastodonRegisterViewController {
|
||||
agreement: true, // user confirmed in the server rules scene
|
||||
locale: locale
|
||||
)
|
||||
|
||||
var retryCount = 0
|
||||
|
||||
// register without show server rules
|
||||
context.apiService.accountRegister(
|
||||
@ -835,6 +862,32 @@ extension MastodonRegisterViewController {
|
||||
query: query,
|
||||
authorization: viewModel.applicationAuthorization
|
||||
)
|
||||
.tryCatch { [weak self] error -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> in
|
||||
guard let self = self else { throw error }
|
||||
guard let error = self.viewModel.error.value as? Mastodon.API.Error,
|
||||
case let .generic(errorEntity) = error.mastodonError,
|
||||
errorEntity.error == "Validation failed: Locale is not included in the list"
|
||||
else {
|
||||
throw error
|
||||
}
|
||||
guard retryCount == 0 else {
|
||||
throw error
|
||||
}
|
||||
let retryQuery = Mastodon.API.Account.RegisterQuery(
|
||||
reason: query.reason,
|
||||
username: query.username,
|
||||
email: query.email,
|
||||
password: query.password,
|
||||
agreement: query.agreement,
|
||||
locale: self.viewModel.instance.languages?.first ?? "en"
|
||||
)
|
||||
retryCount += 1
|
||||
return self.context.apiService.accountRegister(
|
||||
domain: self.viewModel.domain,
|
||||
query: retryQuery,
|
||||
authorization: self.viewModel.applicationAuthorization
|
||||
)
|
||||
}
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] completion in
|
||||
guard let self = self else { return }
|
||||
|
@ -80,8 +80,8 @@ extension OnboardingViewControllerAppearance {
|
||||
extension OnboardingViewControllerAppearance {
|
||||
static var viewEdgeMargin: CGFloat {
|
||||
guard UIDevice.current.userInterfaceIdiom == .pad else { return .zero }
|
||||
|
||||
let shortEdgeWidth = min(UIScreen.main.bounds.height, UIScreen.main.bounds.width)
|
||||
return shortEdgeWidth * 0.17 // magic
|
||||
return 20
|
||||
// let shortEdgeWidth = min(UIScreen.main.bounds.height, UIScreen.main.bounds.width)
|
||||
// return shortEdgeWidth * 0.17 // magic
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,13 @@ extension WizardCardView {
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: -.pi / 2, endAngle: -.pi, clockwise: false)
|
||||
path.addLine(to: CGPoint(x: rect.minX - radius, y: rect.maxY + radius + WizardCardView.bubbleArrowHeight))
|
||||
path.close()
|
||||
case .allCorners: // no arrow
|
||||
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY + radius))
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: .pi / 2 * 3, endAngle: .pi * 2, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: .pi * 2, endAngle: .pi / 2 * 5, clockwise: true)
|
||||
path.close()
|
||||
default:
|
||||
assertionFailure("FIXME")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
var observations = Set<NSKeyValueObservation>()
|
||||
private(set) lazy var viewModel = WelcomeViewModel(context: context)
|
||||
|
||||
let welcomeIllustrationView = WelcomeIllustrationView()
|
||||
@ -23,7 +24,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||
private(set) lazy var dismissBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(WelcomeViewController.dismissBarButtonItemDidPressed(_:)))
|
||||
|
||||
private(set) lazy var logoImageView: UIImageView = {
|
||||
let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Scene.Welcome.mastodonLogo.image : Asset.Scene.Welcome.mastodonLogoBlackLarge.image
|
||||
let image = Asset.Scene.Welcome.mastodonLogo.image
|
||||
let imageView = UIImageView(image: image)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return imageView
|
||||
@ -40,15 +41,15 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||
return label
|
||||
}()
|
||||
|
||||
private(set) lazy var signUpButton: PrimaryActionButton = {
|
||||
private(set) lazy var signUpButton: PrimaryActionButton = {
|
||||
let button = PrimaryActionButton()
|
||||
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
|
||||
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
|
||||
let backgroundImageColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? .white : Asset.Colors.brandBlue.color
|
||||
let backgroundImageHighlightedColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? UIColor(white: 0.8, alpha: 1.0) : Asset.Colors.brandBlueDarken20.color
|
||||
let backgroundImageColor: UIColor = .white
|
||||
let backgroundImageHighlightedColor: UIColor = UIColor(white: 0.8, alpha: 1.0)
|
||||
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
|
||||
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
|
||||
let titleColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? Asset.Colors.brandBlue.color : UIColor.white
|
||||
let titleColor: UIColor = Asset.Colors.brandBlue.color
|
||||
button.setTitleColor(titleColor, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
@ -58,7 +59,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
||||
let button = UIButton(type: .system)
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
||||
button.setTitle(L10n.Common.Controls.Actions.signIn, for: .normal)
|
||||
let titleColor: UIColor = traitCollection.userInterfaceIdiom == .phone ? UIColor.white.withAlphaComponent(0.8) : Asset.Colors.brandBlue.color
|
||||
let titleColor: UIColor = UIColor.white.withAlphaComponent(0.8)
|
||||
button.setTitleColor(titleColor, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
@ -75,6 +76,8 @@ extension WelcomeViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// preferredContentSize = CGSize(width: 547, height: 678)
|
||||
|
||||
navigationController?.navigationBar.prefersLargeTitles = true
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
view.overrideUserInterfaceStyle = .light
|
||||
@ -106,16 +109,31 @@ extension WelcomeViewController {
|
||||
self.navigationItem.leftBarButtonItem = needsShowDismissEntry ? self.dismissBarButtonItem : nil
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
view.observe(\.frame, options: [.initial, .new]) { [weak self] view, _ in
|
||||
guard let self = self else { return }
|
||||
switch view.traitCollection.userInterfaceIdiom {
|
||||
case .phone:
|
||||
break
|
||||
default:
|
||||
self.welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.isHidden = view.frame.height < 800
|
||||
}
|
||||
}
|
||||
.store(in: &observations)
|
||||
}
|
||||
|
||||
override func viewSafeAreaInsetsDidChange() {
|
||||
super.viewSafeAreaInsetsDidChange()
|
||||
|
||||
// shift illustration down for non-notch phone
|
||||
var overlap: CGFloat = 5
|
||||
// shift illustration down for non-notch phone
|
||||
if view.safeAreaInsets.bottom == 0 {
|
||||
overlap += 56
|
||||
}
|
||||
// shift illustration down for iPad modal
|
||||
if UIDevice.current.userInterfaceIdiom != .phone {
|
||||
overlap += 20
|
||||
}
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint?.constant = overlap
|
||||
}
|
||||
|
||||
@ -137,85 +155,80 @@ extension WelcomeViewController {
|
||||
}
|
||||
|
||||
// set illustration for phone
|
||||
if traitCollection.userInterfaceIdiom == .phone {
|
||||
guard welcomeIllustrationView.superview == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5)
|
||||
|
||||
view.addSubview(welcomeIllustrationView)
|
||||
NSLayoutConstraint.activate([
|
||||
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.leftAnchor, constant: 15),
|
||||
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 15),
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint!
|
||||
])
|
||||
|
||||
welcomeIllustrationView.cloudBaseImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -5, maxX: 5, minY: -5, maxY: 5)
|
||||
)
|
||||
welcomeIllustrationView.rightHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -15, maxX: 25, minY: -10, maxY: 10)
|
||||
)
|
||||
welcomeIllustrationView.leftHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -25, maxX: 15, minY: -15, maxY: 15)
|
||||
)
|
||||
welcomeIllustrationView.centerHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -14, maxX: 14, minY: -5, maxY: 25)
|
||||
)
|
||||
|
||||
let topPaddingView = UIView()
|
||||
topPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(topPaddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
topPaddingView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor),
|
||||
topPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
|
||||
topPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
|
||||
])
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(welcomeIllustrationView.elephantOnAirplaneWithContrailImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.leftAnchor, constant: 12), // add 12pt bleeding
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.topAnchor.constraint(equalTo: topPaddingView.bottomAnchor),
|
||||
// make a little bit large
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.84),
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.heightAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor, multiplier: 105.0/318.0),
|
||||
])
|
||||
let bottomPaddingView = UIView()
|
||||
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(bottomPaddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
bottomPaddingView.topAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.bottomAnchor),
|
||||
bottomPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
|
||||
bottomPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
|
||||
bottomPaddingView.bottomAnchor.constraint(equalTo: view.centerYAnchor),
|
||||
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 4),
|
||||
])
|
||||
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -20, maxX: 12, minY: -20, maxY: 12) // maxX should not larger then the bleeding (12pt)
|
||||
)
|
||||
|
||||
view.bringSubviewToFront(logoImageView)
|
||||
view.bringSubviewToFront(sloganLabel)
|
||||
}
|
||||
|
||||
// set slogan for non-phone
|
||||
if traitCollection.userInterfaceIdiom != .phone {
|
||||
guard sloganLabel.superview == nil else {
|
||||
return
|
||||
}
|
||||
view.addSubview(sloganLabel)
|
||||
NSLayoutConstraint.activate([
|
||||
sloganLabel.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 16),
|
||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: sloganLabel.trailingAnchor, constant: 16),
|
||||
sloganLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 168),
|
||||
])
|
||||
guard welcomeIllustrationView.superview == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
view.bringSubviewToFront(sloganLabel)
|
||||
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5)
|
||||
|
||||
view.addSubview(welcomeIllustrationView)
|
||||
NSLayoutConstraint.activate([
|
||||
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.leftAnchor, constant: 15),
|
||||
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 15),
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint!
|
||||
])
|
||||
|
||||
welcomeIllustrationView.cloudBaseImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -5, maxX: 5, minY: -5, maxY: 5)
|
||||
)
|
||||
welcomeIllustrationView.rightHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -15, maxX: 25, minY: -10, maxY: 10)
|
||||
)
|
||||
welcomeIllustrationView.leftHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -25, maxX: 15, minY: -15, maxY: 15)
|
||||
)
|
||||
welcomeIllustrationView.centerHillImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -14, maxX: 14, minY: -5, maxY: 25)
|
||||
)
|
||||
|
||||
let topPaddingView = UIView()
|
||||
topPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(topPaddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
topPaddingView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor),
|
||||
topPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
|
||||
topPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
|
||||
])
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(welcomeIllustrationView.elephantOnAirplaneWithContrailImageView)
|
||||
NSLayoutConstraint.activate([
|
||||
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.leftAnchor, constant: 12), // add 12pt bleeding
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.topAnchor.constraint(equalTo: topPaddingView.bottomAnchor),
|
||||
// make a little bit large
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.84),
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.heightAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor, multiplier: 105.0/318.0),
|
||||
])
|
||||
let bottomPaddingView = UIView()
|
||||
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(bottomPaddingView)
|
||||
NSLayoutConstraint.activate([
|
||||
bottomPaddingView.topAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.bottomAnchor),
|
||||
bottomPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
|
||||
bottomPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
|
||||
bottomPaddingView.bottomAnchor.constraint(equalTo: view.centerYAnchor),
|
||||
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 4),
|
||||
])
|
||||
|
||||
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.addMotionEffect(
|
||||
UIInterpolatingMotionEffect.motionEffect(minX: -20, maxX: 12, minY: -20, maxY: 12) // maxX should not larger then the bleeding (12pt)
|
||||
)
|
||||
|
||||
view.bringSubviewToFront(logoImageView)
|
||||
view.bringSubviewToFront(sloganLabel)
|
||||
|
||||
// set slogan for non-phone
|
||||
// if traitCollection.userInterfaceIdiom != .phone {
|
||||
// guard sloganLabel.superview == nil else {
|
||||
// return
|
||||
// }
|
||||
// view.addSubview(sloganLabel)
|
||||
// NSLayoutConstraint.activate([
|
||||
// sloganLabel.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 16),
|
||||
// view.readableContentGuide.trailingAnchor.constraint(equalTo: sloganLabel.trailingAnchor, constant: 16),
|
||||
// sloganLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 168),
|
||||
// ])
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,12 +274,12 @@ extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
|
||||
// make underneath view controller alive to fix layout issue due to view life cycle
|
||||
return .fullScreen
|
||||
default:
|
||||
switch traitCollection.horizontalSizeClass {
|
||||
case .regular:
|
||||
return .pageSheet
|
||||
default:
|
||||
return .fullScreen
|
||||
}
|
||||
return .formSheet
|
||||
// switch traitCollection.horizontalSizeClass {
|
||||
// case .regular:
|
||||
// default:
|
||||
// return .fullScreen
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,8 @@ extension FollowerListViewController: UserProvider {
|
||||
let managedObjectContext = self.viewModel.userFetchedResultsController.fetchedResultsController.managedObjectContext
|
||||
|
||||
switch item {
|
||||
case .follower(let objectID):
|
||||
case .follower(let objectID),
|
||||
.following(let objectID):
|
||||
managedObjectContext.perform {
|
||||
let user = managedObjectContext.object(with: objectID) as? MastodonUser
|
||||
promise(.success(user))
|
||||
|
@ -7,11 +7,10 @@
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import AVKit
|
||||
import GameplayKit
|
||||
import Combine
|
||||
|
||||
final class FollowerListViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
|
||||
final class FollowerListViewController: UIViewController, NeedsDependency {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
@ -19,9 +18,7 @@ final class FollowerListViewController: UIViewController, NeedsDependency, Media
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: FollowerListViewModel!
|
||||
|
||||
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
||||
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
||||
|
@ -18,16 +18,19 @@ extension FollowerListViewModel {
|
||||
managedObjectContext: userFetchedResultsController.fetchedResultsController.managedObjectContext
|
||||
)
|
||||
|
||||
// workaround to append loader wrong animation issue
|
||||
// set empty section to make update animation top-to-bottom style
|
||||
var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>()
|
||||
snapshot.appendSections([.main])
|
||||
diffableDataSource?.apply(snapshot)
|
||||
|
||||
// workaround to append loader wrong animation issue
|
||||
snapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
diffableDataSource?.apply(snapshot)
|
||||
if #available(iOS 15.0, *) {
|
||||
diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
diffableDataSource?.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
||||
userFetchedResultsController.objectIDs.removeDuplicates()
|
||||
userFetchedResultsController.objectIDs
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] objectIDs in
|
||||
guard let self = self else { return }
|
||||
@ -45,7 +48,12 @@ extension FollowerListViewModel {
|
||||
case is State.Idle, is State.Loading, is State.Fail:
|
||||
snapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
case is State.NoMore:
|
||||
break
|
||||
guard let activeMastodonAuthenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value,
|
||||
let userID = self.userID.value,
|
||||
userID != activeMastodonAuthenticationBox.userID
|
||||
else { break }
|
||||
let text = L10n.Scene.Follower.footer
|
||||
snapshot.appendItems([.bottomHeader(text: text)], toSection: .main)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ extension FollowerListViewModel.State {
|
||||
|
||||
let maxID = response.link?.maxID
|
||||
|
||||
if hasNewAppend, maxID != nil {
|
||||
if hasNewAppend && maxID != nil {
|
||||
stateMachine.enter(Idle.self)
|
||||
} else {
|
||||
stateMachine.enter(NoMore.self)
|
||||
@ -179,18 +179,6 @@ extension FollowerListViewModel.State {
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
guard let viewModel = viewModel, let _ = stateMachine else { return }
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
var snapshot = diffableDataSource.snapshot()
|
||||
snapshot.deleteItems([.bottomLoader])
|
||||
let header = UserItem.bottomHeader(text: "Followers from other servers are not displayed")
|
||||
snapshot.appendItems([header], toSection: .main)
|
||||
diffableDataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
//
|
||||
// FollowingListViewController+Provider.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
|
||||
extension FollowingListViewController: UserProvider {
|
||||
|
||||
func mastodonUser() -> Future<MastodonUser?, Never> {
|
||||
Future { promise in
|
||||
promise(.success(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func mastodonUser(for cell: UITableViewCell?) -> Future<MastodonUser?, Never> {
|
||||
Future { [weak self] promise in
|
||||
guard let self = self else { return }
|
||||
guard let diffableDataSource = self.viewModel.diffableDataSource else {
|
||||
assertionFailure()
|
||||
promise(.success(nil))
|
||||
return
|
||||
}
|
||||
guard let cell = cell,
|
||||
let indexPath = self.tableView.indexPath(for: cell),
|
||||
let item = diffableDataSource.itemIdentifier(for: indexPath) else {
|
||||
promise(.success(nil))
|
||||
return
|
||||
}
|
||||
|
||||
let managedObjectContext = self.viewModel.userFetchedResultsController.fetchedResultsController.managedObjectContext
|
||||
|
||||
switch item {
|
||||
case .follower(let objectID),
|
||||
.following(let objectID):
|
||||
managedObjectContext.perform {
|
||||
let user = managedObjectContext.object(with: objectID) as? MastodonUser
|
||||
promise(.success(user))
|
||||
}
|
||||
case .bottomLoader, .bottomHeader:
|
||||
promise(.success(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
//
|
||||
// FollowingListViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import GameplayKit
|
||||
import Combine
|
||||
|
||||
final class FollowingListViewController: UIViewController, NeedsDependency {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
var viewModel: FollowingListViewModel!
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.register(UserTableViewCell.self, forCellReuseIdentifier: String(describing: UserTableViewCell.self))
|
||||
tableView.register(TimelineBottomLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self))
|
||||
tableView.register(TimelineFooterTableViewCell.self, forCellReuseIdentifier: String(describing: TimelineFooterTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.separatorStyle = .none
|
||||
tableView.backgroundColor = .clear
|
||||
return tableView
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FollowingListViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
|
||||
ThemeService.shared.currentTheme
|
||||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] theme in
|
||||
guard let self = self else { return }
|
||||
self.view.backgroundColor = theme.secondarySystemBackgroundColor
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
NSLayoutConstraint.activate([
|
||||
tableView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
||||
tableView.delegate = self
|
||||
viewModel.setupDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: self
|
||||
)
|
||||
// TODO: add UserTableViewCellDelegate
|
||||
|
||||
// trigger user timeline loading
|
||||
Publishers.CombineLatest(
|
||||
viewModel.domain.removeDuplicates().eraseToAnyPublisher(),
|
||||
viewModel.userID.removeDuplicates().eraseToAnyPublisher()
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.stateMachine.enter(FollowingListViewModel.State.Reloading.self)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - LoadMoreConfigurableTableViewContainer
|
||||
extension FollowingListViewController: LoadMoreConfigurableTableViewContainer {
|
||||
typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
||||
typealias LoadingState = FollowingListViewModel.State.Loading
|
||||
var loadMoreConfigurableTableView: UITableView { tableView }
|
||||
var loadMoreConfigurableStateMachine: GKStateMachine { viewModel.stateMachine }
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
extension FollowingListViewController {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
handleScrollViewDidScroll(scrollView)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension FollowingListViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
handleTableView(tableView, didSelectRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserTableViewCellDelegate
|
||||
extension FollowingListViewController: UserTableViewCellDelegate { }
|
@ -0,0 +1,66 @@
|
||||
//
|
||||
// FollowingListViewModel+Diffable.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension FollowingListViewModel {
|
||||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
dependency: NeedsDependency
|
||||
) {
|
||||
diffableDataSource = UserSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: userFetchedResultsController.fetchedResultsController.managedObjectContext
|
||||
)
|
||||
|
||||
// workaround to append loader wrong animation issue
|
||||
// set empty section to make update animation top-to-bottom style
|
||||
var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>()
|
||||
snapshot.appendSections([.main])
|
||||
snapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
if #available(iOS 15.0, *) {
|
||||
diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
diffableDataSource?.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
||||
userFetchedResultsController.objectIDs
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] objectIDs in
|
||||
guard let self = self else { return }
|
||||
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<UserSection, UserItem>()
|
||||
snapshot.appendSections([.main])
|
||||
let items: [UserItem] = objectIDs.map {
|
||||
UserItem.following(objectID: $0)
|
||||
}
|
||||
snapshot.appendItems(items, toSection: .main)
|
||||
|
||||
if let currentState = self.stateMachine.currentState {
|
||||
switch currentState {
|
||||
case is State.Idle, is State.Loading, is State.Fail:
|
||||
snapshot.appendItems([.bottomLoader], toSection: .main)
|
||||
case is State.NoMore:
|
||||
guard let activeMastodonAuthenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value,
|
||||
let userID = self.userID.value,
|
||||
userID != activeMastodonAuthenticationBox.userID
|
||||
else { break }
|
||||
let text = L10n.Scene.Following.footer
|
||||
snapshot.appendItems([.bottomHeader(text: text)], toSection: .main)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
diffableDataSource.apply(snapshot)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
//
|
||||
// FollowingListViewModel+State.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import Foundation
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
|
||||
extension FollowingListViewModel {
|
||||
class State: GKState {
|
||||
weak var viewModel: FollowingListViewModel?
|
||||
|
||||
init(viewModel: FollowingListViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
os_log("%{public}s[%{public}ld], %{public}s: enter %s, previous: %s", ((#file as NSString).lastPathComponent), #line, #function, self.debugDescription, previousState.debugDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FollowingListViewModel.State {
|
||||
class Initial: FollowingListViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
guard let viewModel = viewModel else { return false }
|
||||
switch stateClass {
|
||||
case is Reloading.Type:
|
||||
return viewModel.userID.value != nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Reloading: FollowingListViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Loading.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
||||
|
||||
// reset
|
||||
viewModel.userFetchedResultsController.userIDs.value = []
|
||||
|
||||
stateMachine.enter(Loading.self)
|
||||
}
|
||||
}
|
||||
|
||||
class Fail: FollowingListViewModel.State {
|
||||
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Loading.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
guard let _ = viewModel, let stateMachine = stateMachine else { return }
|
||||
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: retry loading 3s later…", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: retry loading", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
stateMachine.enter(Loading.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Idle: FollowingListViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Reloading.Type, is Loading.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Loading: FollowingListViewModel.State {
|
||||
|
||||
var maxID: String?
|
||||
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Fail.Type:
|
||||
return true
|
||||
case is Idle.Type:
|
||||
return true
|
||||
case is NoMore.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
|
||||
if previousState is Reloading {
|
||||
maxID = nil
|
||||
}
|
||||
|
||||
guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
|
||||
|
||||
guard let userID = viewModel.userID.value, !userID.isEmpty else {
|
||||
stateMachine.enter(Fail.self)
|
||||
return
|
||||
}
|
||||
|
||||
guard let activeMastodonAuthenticationBox = viewModel.context.authenticationService.activeMastodonAuthenticationBox.value else {
|
||||
stateMachine.enter(Fail.self)
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.context.apiService.following(
|
||||
userID: userID,
|
||||
maxID: maxID,
|
||||
authorizationBox: activeMastodonAuthenticationBox
|
||||
)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { completion in
|
||||
switch completion {
|
||||
case .failure(let error):
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch user timeline fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
||||
stateMachine.enter(Fail.self)
|
||||
case .finished:
|
||||
break
|
||||
}
|
||||
} receiveValue: { response in
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
var hasNewAppend = false
|
||||
var userIDs = viewModel.userFetchedResultsController.userIDs.value
|
||||
for user in response.value {
|
||||
guard !userIDs.contains(user.id) else { continue }
|
||||
userIDs.append(user.id)
|
||||
hasNewAppend = true
|
||||
}
|
||||
|
||||
let maxID = response.link?.maxID
|
||||
|
||||
if hasNewAppend, maxID != nil {
|
||||
stateMachine.enter(Idle.self)
|
||||
} else {
|
||||
stateMachine.enter(NoMore.self)
|
||||
}
|
||||
self.maxID = maxID
|
||||
viewModel.userFetchedResultsController.userIDs.value = userIDs
|
||||
}
|
||||
.store(in: &viewModel.disposeBag)
|
||||
} // end func didEnter
|
||||
}
|
||||
|
||||
class NoMore: FollowingListViewModel.State {
|
||||
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
|
||||
switch stateClass {
|
||||
case is Reloading.Type:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override func didEnter(from previousState: GKState?) {
|
||||
super.didEnter(from: previousState)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
//
|
||||
// FollowingListViewModel.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import Combine
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import GameplayKit
|
||||
import MastodonSDK
|
||||
|
||||
final class FollowingListViewModel {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
// input
|
||||
let context: AppContext
|
||||
let domain: CurrentValueSubject<String?, Never>
|
||||
let userID: CurrentValueSubject<String?, Never>
|
||||
let userFetchedResultsController: UserFetchedResultsController
|
||||
|
||||
// output
|
||||
var diffableDataSource: UITableViewDiffableDataSource<UserSection, UserItem>?
|
||||
private(set) lazy var stateMachine: GKStateMachine = {
|
||||
let stateMachine = GKStateMachine(states: [
|
||||
State.Initial(viewModel: self),
|
||||
State.Reloading(viewModel: self),
|
||||
State.Fail(viewModel: self),
|
||||
State.Idle(viewModel: self),
|
||||
State.Loading(viewModel: self),
|
||||
State.NoMore(viewModel: self),
|
||||
])
|
||||
stateMachine.enter(State.Initial.self)
|
||||
return stateMachine
|
||||
}()
|
||||
|
||||
init(context: AppContext, domain: String?, userID: String?) {
|
||||
self.context = context
|
||||
self.userFetchedResultsController = UserFetchedResultsController(
|
||||
managedObjectContext: context.managedObjectContext,
|
||||
domain: domain,
|
||||
additionalTweetPredicate: nil
|
||||
)
|
||||
self.domain = CurrentValueSubject(domain)
|
||||
self.userID = CurrentValueSubject(userID)
|
||||
// super.init()
|
||||
|
||||
}
|
||||
}
|
@ -1001,8 +1001,19 @@ extension ProfileViewController: ProfileHeaderViewDelegate {
|
||||
transition: .show
|
||||
)
|
||||
case .following:
|
||||
// TODO:
|
||||
break
|
||||
guard let domain = viewModel.domain.value,
|
||||
let userID = viewModel.userID.value
|
||||
else { return }
|
||||
let followingListViewModel = FollowingListViewModel(
|
||||
context: context,
|
||||
domain: domain,
|
||||
userID: userID
|
||||
)
|
||||
coordinator.present(
|
||||
scene: .following(viewModel: followingListViewModel),
|
||||
from: self,
|
||||
transition: .show
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,16 @@ extension UserTimelineViewController {
|
||||
statusTableViewCellDelegate: self
|
||||
)
|
||||
|
||||
// setup batch fetch
|
||||
viewModel.listBatchFetchViewModel.setup(scrollView: tableView)
|
||||
viewModel.listBatchFetchViewModel.shouldFetch
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.stateMachine.enter(UserTimelineViewModel.State.Loading.self)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// trigger user timeline loading
|
||||
Publishers.CombineLatest(
|
||||
viewModel.domain.removeDuplicates().eraseToAnyPublisher(),
|
||||
@ -104,11 +114,11 @@ extension UserTimelineViewController {
|
||||
extension UserTimelineViewController: StatusTableViewControllerAspect { }
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
extension UserTimelineViewController {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
aspectScrollViewDidScroll(scrollView)
|
||||
}
|
||||
}
|
||||
//extension UserTimelineViewController {
|
||||
// func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
// aspectScrollViewDidScroll(scrollView)
|
||||
// }
|
||||
//}
|
||||
|
||||
// MARK: - TableViewCellHeightCacheableContainer
|
||||
extension UserTimelineViewController: TableViewCellHeightCacheableContainer {
|
||||
@ -186,13 +196,13 @@ extension UserTimelineViewController: ScrollViewContainer {
|
||||
}
|
||||
|
||||
// MARK: - LoadMoreConfigurableTableViewContainer
|
||||
extension UserTimelineViewController: LoadMoreConfigurableTableViewContainer {
|
||||
typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
||||
typealias LoadingState = UserTimelineViewModel.State.Loading
|
||||
|
||||
var loadMoreConfigurableTableView: UITableView { return tableView }
|
||||
var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.stateMachine }
|
||||
}
|
||||
//extension UserTimelineViewController: LoadMoreConfigurableTableViewContainer {
|
||||
// typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell
|
||||
// typealias LoadingState = UserTimelineViewModel.State.Loading
|
||||
//
|
||||
// var loadMoreConfigurableTableView: UITable``````View { return tableView }
|
||||
// var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.stateMachine }
|
||||
//}
|
||||
|
||||
extension UserTimelineViewController {
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
|
@ -23,6 +23,7 @@ final class UserTimelineViewModel {
|
||||
let userID: CurrentValueSubject<String?, Never>
|
||||
let queryFilter: CurrentValueSubject<QueryFilter, Never>
|
||||
let statusFetchedResultsController: StatusFetchedResultsController
|
||||
let listBatchFetchViewModel = ListBatchFetchViewModel()
|
||||
var cellFrameCache = NSCache<NSNumber, NSValue>()
|
||||
|
||||
let isBlocking = CurrentValueSubject<Bool, Never>(false)
|
||||
|
@ -51,7 +51,7 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var stackview: UIStackView = {
|
||||
lazy var stackView: UIStackView = {
|
||||
let view = UIStackView()
|
||||
view.axis = .vertical
|
||||
view.alignment = .fill
|
||||
@ -122,19 +122,19 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||
|
||||
setupNavigation()
|
||||
|
||||
stackview.addArrangedSubview(header)
|
||||
stackview.addArrangedSubview(contentView)
|
||||
stackview.addArrangedSubview(footer)
|
||||
stackview.addArrangedSubview(bottomSpacing)
|
||||
stackView.addArrangedSubview(header)
|
||||
stackView.addArrangedSubview(contentView)
|
||||
stackView.addArrangedSubview(footer)
|
||||
stackView.addArrangedSubview(bottomSpacing)
|
||||
|
||||
contentView.addSubview(tableView)
|
||||
|
||||
view.addSubview(stackview)
|
||||
view.addSubview(stackView)
|
||||
NSLayoutConstraint.activate([
|
||||
stackview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
stackview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
stackview.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
stackview.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
||||
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
tableView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
@ -273,8 +273,8 @@ class ReportViewController: UIViewController, NeedsDependency {
|
||||
navigationItem.titleView = titleView
|
||||
if let user = beReportedUser {
|
||||
do {
|
||||
let mastodonConent = MastodonContent(content: user.displayNameWithFallback, emojis: user.emojiMeta)
|
||||
let metaContent = try MastodonMetaContent.convert(document: mastodonConent)
|
||||
let mastodonContent = MastodonContent(content: user.displayNameWithFallback, emojis: user.emojiMeta)
|
||||
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
||||
titleView.update(titleMetaContent: metaContent, subtitle: nil)
|
||||
} catch {
|
||||
let metaContent = PlaintextMetaContent(string: user.displayNameWithFallback)
|
||||
|
@ -19,6 +19,7 @@ final class ReportedStatusTableViewCell: UITableViewCell, StatusCell {
|
||||
static let bottomPaddingHeight: CGFloat = 10
|
||||
|
||||
weak var dependency: ReportViewController?
|
||||
private var _disposeBag = Set<AnyCancellable>()
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
var observations = Set<NSKeyValueObservation>()
|
||||
|
||||
@ -98,7 +99,7 @@ extension ReportedStatusTableViewCell {
|
||||
guard let self = self else { return }
|
||||
self.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemGroupedBackgroundColor
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
.store(in: &_disposeBag)
|
||||
|
||||
checkbox.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(checkbox)
|
||||
|
@ -101,7 +101,8 @@ extension ContentSplitViewController: SidebarViewControllerDelegate {
|
||||
|
||||
let accountListViewController = coordinator.present(scene: .accountList, from: nil, transition: .popover(sourceView: sourceView)) as! AccountListViewController
|
||||
accountListViewController.dragIndicatorView.barView.isHidden = true
|
||||
accountListViewController.preferredContentSize = CGSize(width: 300, height: 320)
|
||||
// content width needs > 300 to make checkmark display
|
||||
accountListViewController.preferredContentSize = CGSize(width: 375, height: 400)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,137 +0,0 @@
|
||||
//
|
||||
// MainTabBarController+Wizard.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-9-15.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
|
||||
protocol WizardDelegate: AnyObject {
|
||||
func spotlight(item: MainTabBarController.Wizard.Item) -> UIBezierPath
|
||||
func layoutWizardCard(_ wizard: MainTabBarController.Wizard, item: MainTabBarController.Wizard.Item)
|
||||
}
|
||||
|
||||
extension MainTabBarController {
|
||||
class Wizard {
|
||||
|
||||
let logger = Logger(subsystem: "Wizard", category: "UI")
|
||||
|
||||
weak var delegate: WizardDelegate?
|
||||
|
||||
private(set) var items: [Item]
|
||||
|
||||
let backgroundView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
|
||||
return view
|
||||
}()
|
||||
|
||||
init() {
|
||||
var items: [Item] = []
|
||||
if !UserDefaults.shared.didShowMultipleAccountSwitchWizard {
|
||||
items.append(.multipleAccountSwitch)
|
||||
}
|
||||
self.items = items
|
||||
|
||||
let backgroundTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||
backgroundTapGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.Wizard.backgroundTapGestureRecognizerHandler(_:)))
|
||||
backgroundView.addGestureRecognizer(backgroundTapGestureRecognizer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MainTabBarController.Wizard {
|
||||
enum Item {
|
||||
case multipleAccountSwitch
|
||||
|
||||
var title: String {
|
||||
return L10n.Scene.Wizard.newInMastodon
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .multipleAccountSwitch:
|
||||
return L10n.Scene.Wizard.multipleAccountSwitchIntroDescription
|
||||
}
|
||||
}
|
||||
|
||||
func markAsRead() {
|
||||
switch self {
|
||||
case .multipleAccountSwitch:
|
||||
UserDefaults.shared.didShowMultipleAccountSwitchWizard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension MainTabBarController.Wizard {
|
||||
|
||||
func setup(in view: UIView) {
|
||||
assert(delegate != nil, "need set delegate before use")
|
||||
|
||||
guard !items.isEmpty else { return }
|
||||
|
||||
backgroundView.frame = view.bounds
|
||||
backgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(backgroundView)
|
||||
NSLayoutConstraint.activate([
|
||||
backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
func consume() {
|
||||
guard !items.isEmpty else {
|
||||
backgroundView.removeFromSuperview()
|
||||
return
|
||||
}
|
||||
let item = items.removeFirst()
|
||||
perform(item: item)
|
||||
}
|
||||
|
||||
private func perform(item: Item) {
|
||||
guard let delegate = delegate else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
// prepare for reuse
|
||||
prepareForReuse()
|
||||
|
||||
// set wizard item read
|
||||
item.markAsRead()
|
||||
|
||||
// add spotlight
|
||||
let spotlight = delegate.spotlight(item: item)
|
||||
let maskLayer = CAShapeLayer()
|
||||
let path = UIBezierPath(rect: backgroundView.bounds)
|
||||
path.append(spotlight)
|
||||
maskLayer.fillRule = .evenOdd
|
||||
maskLayer.path = path.cgPath
|
||||
backgroundView.layer.mask = maskLayer
|
||||
|
||||
// layout wizard card
|
||||
delegate.layoutWizardCard(self, item: item)
|
||||
}
|
||||
|
||||
private func prepareForReuse() {
|
||||
backgroundView.subviews.forEach { subview in
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
backgroundView.mask = nil
|
||||
backgroundView.layer.mask = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension MainTabBarController.Wizard {
|
||||
@objc private func backgroundTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
consume()
|
||||
}
|
||||
}
|
@ -21,8 +21,6 @@ class MainTabBarController: UITabBarController {
|
||||
|
||||
static let avatarButtonSize = CGSize(width: 25, height: 25)
|
||||
let avatarButton = CircleAvatarButton()
|
||||
|
||||
let wizard = Wizard()
|
||||
|
||||
var currentTab = CurrentValueSubject<Tab, Never>(.home)
|
||||
|
||||
@ -108,6 +106,8 @@ class MainTabBarController: UITabBarController {
|
||||
|
||||
var _viewControllers: [UIViewController] = []
|
||||
|
||||
private(set) var isReadyForWizardAvatarButton = false
|
||||
|
||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
||||
self.context = context
|
||||
self.coordinator = coordinator
|
||||
@ -247,14 +247,27 @@ extension MainTabBarController {
|
||||
profileTabItem.accessibilityHint = L10n.Scene.AccountList.tabBarHint(currentUserDisplayName)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
wizard.delegate = self
|
||||
wizard.setup(in: view)
|
||||
|
||||
let tabBarLongPressGestureRecognizer = UILongPressGestureRecognizer()
|
||||
tabBarLongPressGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.tabBarLongPressGestureRecognizerHandler(_:)))
|
||||
tabBar.addGestureRecognizer(tabBarLongPressGestureRecognizer)
|
||||
|
||||
context.authenticationService.activeMastodonAuthenticationBox
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] authenticationBox in
|
||||
guard let self = self else { return }
|
||||
self.isReadyForWizardAvatarButton = authenticationBox != nil
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
currentTab
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] tab in
|
||||
guard let self = self else { return }
|
||||
self.updateAvatarButtonAppearance()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
updateTabBarDisplay()
|
||||
|
||||
#if DEBUG
|
||||
@ -262,16 +275,11 @@ extension MainTabBarController {
|
||||
#endif
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
wizard.consume()
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
updateTabBarDisplay()
|
||||
updateAvatarButtonAppearance()
|
||||
}
|
||||
|
||||
}
|
||||
@ -343,6 +351,11 @@ extension MainTabBarController {
|
||||
self.avatarButton.setContentHuggingPriority(.required - 1, for: .vertical)
|
||||
self.avatarButton.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
private func updateAvatarButtonAppearance() {
|
||||
avatarButton.borderColor = currentTab.value == .me ? .label : .systemFill
|
||||
avatarButton.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
extension MainTabBarController {
|
||||
@ -351,6 +364,10 @@ extension MainTabBarController {
|
||||
return viewController(of: NotificationViewController.self)
|
||||
}
|
||||
|
||||
var searchViewController: SearchViewController? {
|
||||
return viewController(of: SearchViewController.self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITabBarControllerDelegate
|
||||
@ -373,51 +390,57 @@ extension MainTabBarController: UITabBarControllerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WizardDataSource
|
||||
extension MainTabBarController: WizardDelegate {
|
||||
func spotlight(item: Wizard.Item) -> UIBezierPath {
|
||||
// MARK: - WizardViewControllerDelegate
|
||||
extension MainTabBarController: WizardViewControllerDelegate {
|
||||
func readyToLayoutItem(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> Bool {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInView() else {
|
||||
return UIBezierPath()
|
||||
}
|
||||
return UIBezierPath(ovalIn: avatarButtonFrameInView)
|
||||
|
||||
return isReadyForWizardAvatarButton
|
||||
}
|
||||
}
|
||||
|
||||
func layoutWizardCard(_ wizard: MainTabBarController.Wizard, item: Wizard.Item) {
|
||||
func layoutSpotlight(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> UIBezierPath {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInView() else {
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInWizardView(wizardView: wizardViewController.view) else {
|
||||
return UIBezierPath()
|
||||
}
|
||||
return UIBezierPath(ovalIn: avatarButtonFrameInView)
|
||||
}
|
||||
}
|
||||
|
||||
func layoutWizardCard(_ wizardViewController: WizardViewController, item: WizardViewController.Item) {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInWizardView(wizardView: wizardViewController.view) else {
|
||||
return
|
||||
}
|
||||
let anchorView = UIView()
|
||||
anchorView.frame = avatarButtonFrameInView
|
||||
wizard.backgroundView.addSubview(anchorView)
|
||||
wizardViewController.backgroundView.addSubview(anchorView)
|
||||
|
||||
let wizardCardView = WizardCardView()
|
||||
wizardCardView.arrowRectCorner = view.traitCollection.layoutDirection == .leftToRight ? .bottomRight : .bottomLeft
|
||||
wizardCardView.titleLabel.text = item.title
|
||||
wizardCardView.descriptionLabel.text = item.description
|
||||
|
||||
|
||||
wizardCardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
wizard.backgroundView.addSubview(wizardCardView)
|
||||
wizardViewController.backgroundView.addSubview(wizardCardView)
|
||||
NSLayoutConstraint.activate([
|
||||
anchorView.topAnchor.constraint(equalTo: wizardCardView.bottomAnchor, constant: 13), // 13pt spacing
|
||||
wizardCardView.trailingAnchor.constraint(equalTo: anchorView.centerXAnchor),
|
||||
wizardCardView.widthAnchor.constraint(equalTo: wizard.backgroundView.widthAnchor, multiplier: 2.0/3.0).priority(.required - 1),
|
||||
wizardCardView.widthAnchor.constraint(equalTo: wizardViewController.view.widthAnchor, multiplier: 2.0/3.0).priority(.required - 1),
|
||||
])
|
||||
wizardCardView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||
}
|
||||
}
|
||||
|
||||
private func avatarButtonFrameInView() -> CGRect? {
|
||||
private func avatarButtonFrameInWizardView(wizardView: UIView) -> CGRect? {
|
||||
guard let superview = avatarButton.superview else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
return superview.convert(avatarButton.frame, to: view)
|
||||
return superview.convert(avatarButton.frame, to: wizardView)
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user