From 5c6b5b56a89a85338ceef5f2ad239f357eaa4fa7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 18 Sep 2021 08:51:45 +0200 Subject: [PATCH 01/95] New translations app.json (Thai) --- Localization/StringsConvertor/input/th_TH/app.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Localization/StringsConvertor/input/th_TH/app.json b/Localization/StringsConvertor/input/th_TH/app.json index 707add6f7..49e4324cc 100644 --- a/Localization/StringsConvertor/input/th_TH/app.json +++ b/Localization/StringsConvertor/input/th_TH/app.json @@ -537,13 +537,13 @@ }, "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" + "dismiss_account_switcher": "ปิดตัวสลับบัญชี", + "add_account": "เพิ่มบัญชี" }, "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" + "accessibility_hint": "แตะสองครั้งเพื่อปิดตัวช่วยสร้างนี้" } } } \ No newline at end of file From 9bfa5bb79514a88389a4bda93877869f10a7175e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 18 Sep 2021 08:51:46 +0200 Subject: [PATCH 02/95] New translations Localizable.stringsdict (Thai) --- .../StringsConvertor/input/th_TH/Localizable.stringsdict | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Localization/StringsConvertor/input/th_TH/Localizable.stringsdict b/Localization/StringsConvertor/input/th_TH/Localizable.stringsdict index 1d6ff10bc..8971821f6 100644 --- a/Localization/StringsConvertor/input/th_TH/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/th_TH/Localizable.stringsdict @@ -13,7 +13,7 @@ NSStringFormatValueTypeKey ld other - %ld unread notification + %ld การแจ้งเตือนที่ยังไม่ได้อ่าน a11y.plural.count.input_limit_exceeds From 1dad97946e60cc0c5553cbe574815e6f59feec74 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 18 Sep 2021 10:01:15 +0200 Subject: [PATCH 03/95] New translations app.json (Thai) --- Localization/StringsConvertor/input/th_TH/app.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Localization/StringsConvertor/input/th_TH/app.json b/Localization/StringsConvertor/input/th_TH/app.json index 49e4324cc..fb3024f2b 100644 --- a/Localization/StringsConvertor/input/th_TH/app.json +++ b/Localization/StringsConvertor/input/th_TH/app.json @@ -536,13 +536,13 @@ } }, "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": "New in Mastodon", - "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.", + "new_in_mastodon": "มาใหม่ใน Mastodon", + "multiple_account_switch_intro_description": "สลับระหว่างหลายบัญชีโดยกดปุ่มโปรไฟล์ค้างไว้", "accessibility_hint": "แตะสองครั้งเพื่อปิดตัวช่วยสร้างนี้" } } From 4bb9f66e41ec50b77579a1776f40549541839cd5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 27 Sep 2021 03:34:41 +0200 Subject: [PATCH 04/95] New translations Localizable.stringsdict (German) --- .../StringsConvertor/input/de_DE/Localizable.stringsdict | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localization/StringsConvertor/input/de_DE/Localizable.stringsdict b/Localization/StringsConvertor/input/de_DE/Localizable.stringsdict index c868bdc0f..66b7f2a2d 100644 --- a/Localization/StringsConvertor/input/de_DE/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/de_DE/Localizable.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey ld one - 1 unread notification + 1 ungelesene Benachrichtigung other - %ld unread notification + %ld ungelesene Benachrichtigungen a11y.plural.count.input_limit_exceeds From 2619dacc6d4674ccaa85f8c0c221d26beb8ff428 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 27 Sep 2021 03:34:42 +0200 Subject: [PATCH 05/95] New translations app.json (German) --- Localization/StringsConvertor/input/de_DE/app.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Localization/StringsConvertor/input/de_DE/app.json b/Localization/StringsConvertor/input/de_DE/app.json index 47e57498c..ff29e8d64 100644 --- a/Localization/StringsConvertor/input/de_DE/app.json +++ b/Localization/StringsConvertor/input/de_DE/app.json @@ -538,12 +538,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": "Konto hinzufügen" }, "wizard": { - "new_in_mastodon": "New in Mastodon", + "new_in_mastodon": "Neu in Mastodon", "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.", - "accessibility_hint": "Double tap to dismiss this wizard" + "accessibility_hint": "Doppeltippen, um diesen Assistenten zu schließen" } } } \ No newline at end of file From 1245f99d67d2b9d440ea07aa388b1ce9f2276360 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Oct 2021 18:46:13 +0200 Subject: [PATCH 06/95] New translations app.json (Arabic) --- Localization/StringsConvertor/input/ar_SA/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Localization/StringsConvertor/input/ar_SA/app.json b/Localization/StringsConvertor/input/ar_SA/app.json index db8de394d..1dec5b408 100644 --- a/Localization/StringsConvertor/input/ar_SA/app.json +++ b/Localization/StringsConvertor/input/ar_SA/app.json @@ -308,7 +308,7 @@ "title": "Check your inbox.", "description": "We just sent you an email. Check your junk folder if you haven’t.", "mail": "البريد", - "open_email_client": "Open Email Client" + "open_email_client": "فتح عميل البريد الإلكتروني" } }, "home_timeline": { From b24c699cd0e10ea181693f782c302ca27bfde88a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Oct 2021 20:12:50 +0200 Subject: [PATCH 07/95] New translations app.json (Arabic) --- .../StringsConvertor/input/ar_SA/app.json | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/app.json b/Localization/StringsConvertor/input/ar_SA/app.json index 1dec5b408..ef009776c 100644 --- a/Localization/StringsConvertor/input/ar_SA/app.json +++ b/Localization/StringsConvertor/input/ar_SA/app.json @@ -2,8 +2,8 @@ "common": { "alerts": { "common": { - "please_try_again": "الرجاء المحاولة مرة أخرى.", - "please_try_again_later": "الرجاء المحاولة مرة أخرى لاحقاً." + "please_try_again": "يُرجى المحاولة مرة أُخرى.", + "please_try_again_later": "يُرجى المحاولة مرة أُخرى لاحقاً." }, "sign_up_failure": { "title": "فشل التسجيل" @@ -29,7 +29,7 @@ }, "edit_profile_failure": { "title": "Edit Profile Error", - "message": "لا يمكن تعديل الملف الشخصي. الرجاء المحاولة مرة أخرى." + "message": "لا يمكن تعديل الملف الشخصي. يُرجى المحاولة مرة أُخرى." }, "sign_out": { "title": "تسجيل الخروج", @@ -49,8 +49,8 @@ "delete": "احذف" }, "clean_cache": { - "title": "تنظيف ذاكرة التخزين المؤقت", - "message": "تم تنظيف ذاكرة التخزين المؤقت %s بنجاح." + "title": "مَحو ذاكرة التخزين المؤقت", + "message": "تمَّ مَحو ذاكرة التخزين المؤقت %s بنجاح." } }, "controls": { @@ -64,17 +64,17 @@ "edit": "تعديل", "save": "حفظ", "ok": "حسنًا", - "done": "تم", + "done": "تمّ", "confirm": "تأكيد", "continue": "واصل", "cancel": "إلغاء", "discard": "تجاهل", - "try_again": "حاول مرة أخرى", + "try_again": "المُحاولة مرة أُخرى", "take_photo": "التقط صورة", "save_photo": "حفظ الصورة", "copy_photo": "نسخ الصورة", - "sign_in": "لِج", - "sign_up": "انشئ حسابًا", + "sign_in": "تسجيل الدخول", + "sign_up": "إنشاء حِساب", "see_more": "عرض المزيد", "preview": "معاينة", "share": "شارك", @@ -122,7 +122,7 @@ } }, "status": { - "user_reblogged": "%s reblogged", + "user_reblogged": "أعادَ %s تدوينها", "user_replied_to": "رد على %s", "show_post": "اظهر المنشور", "show_user_profile": "اظهر الملف التعريفي للمستخدم", @@ -152,8 +152,8 @@ "friendship": { "follow": "اتبع", "following": "مُتابَع", - "request": "Request", - "pending": "Pending", + "request": "إرسال طَلَب", + "pending": "قيد المُراجعة", "block": "حظر", "block_user": "حظر %s", "block_domain": "حظر %s", @@ -168,12 +168,12 @@ "edit_info": "تعديل المعلومات" }, "timeline": { - "filtered": "Filtered", + "filtered": "مُصفَّى", "timestamp": { "now": "الأن" }, "loader": { - "load_missing_posts": "Load missing posts", + "load_missing_posts": "تحميل المنشورات المَفقودة", "loading_missing_posts": "تحميل المزيد من المنشورات...", "show_more_replies": "إظهار المزيد من الردود" }, @@ -206,7 +206,7 @@ "games": "ألعاب", "general": "عام", "journalism": "صحافة", - "lgbt": "lgbt", + "lgbt": "مجتمع الشواذ", "regional": "اقليمي", "art": "فن", "music": "موسيقى", @@ -302,10 +302,10 @@ "dont_receive_email": { "title": "تحقق من بريدك الإلكتروني", "description": "Check if your email address is correct as well as your junk folder if you haven’t.", - "resend_email": "Resend Email" + "resend_email": "إعادة إرسال البريد الإلكتروني" }, "open_email_app": { - "title": "Check your inbox.", + "title": "تحقَّق من بريدك الوارِد.", "description": "We just sent you an email. Check your junk folder if you haven’t.", "mail": "البريد", "open_email_client": "فتح عميل البريد الإلكتروني" @@ -315,7 +315,7 @@ "title": "الخيط الرئيسي", "navigation_bar_state": { "offline": "غير متصل", - "new_posts": "See new posts", + "new_posts": "إظهار منشورات جديدة", "published": "تم نشره!", "Publishing": "جارٍ نشر المشاركة…" } @@ -334,7 +334,7 @@ "photo_library": "مكتبة الصور", "browse": "تصفح" }, - "content_input_placeholder": "ما الذي يجول ببالك", + "content_input_placeholder": "أخبِرنا بِما يَجُولُ فِي ذِهنَك", "compose_action": "انشر", "replying_to_user": "رد على %s", "attachment": { @@ -367,7 +367,7 @@ "space_to_add": "Space to add" }, "accessibility": { - "append_attachment": "Add Attachment", + "append_attachment": "إضافة مُرفَق", "append_poll": "اضافة استطلاع رأي", "remove_poll": "إزالة الاستطلاع", "custom_emoji_picker": "منتقي مخصص للإيموجي", @@ -376,11 +376,11 @@ "post_visibility_menu": "Post Visibility Menu" }, "keyboard": { - "discard_post": "Discard Post", - "publish_post": "Publish Post", - "toggle_poll": "Toggle Poll", - "toggle_content_warning": "Toggle Content Warning", - "append_attachment_entry": "Add Attachment - %s", + "discard_post": "تجاهُل المنشور", + "publish_post": "نَشر المَنشُور", + "toggle_poll": "تبديل الاستطلاع", + "toggle_content_warning": "تبديل تحذير المُحتوى", + "append_attachment_entry": "إضافة مُرفَق - %s", "select_visibility_entry": "اختر مدى الظهور - %s" } }, @@ -393,7 +393,7 @@ "fields": { "add_row": "إضافة صف", "placeholder": { - "label": "Label", + "label": "التسمية", "content": "المحتوى" } }, @@ -424,7 +424,7 @@ "hash_tag": { "title": "ذات شعبية على ماستدون", "description": "Hashtags that are getting quite a bit of attention", - "people_talking": "%s people are talking" + "people_talking": "%s أشخاص يتحدَّثوا" }, "accounts": { "title": "حسابات قد تعجبك", @@ -459,15 +459,15 @@ "user_reblogged_your_post": "أعاد %s تدوين مشاركتك", "user_mentioned_you": "أشار إليك %s", "user_requested_to_follow_you": "طلب %s متابعتك", - "user_your_poll_has_ended": "%s Your poll has ended", + "user_your_poll_has_ended": "%s اِنتهى استطلاعُكَ للرأي", "keyobard": { "show_everything": "إظهار كل شيء", - "show_mentions": "Show Mentions" + "show_mentions": "إظهار الإشارات" } }, "thread": { - "back_title": "Post", - "title": "Post from %s" + "back_title": "منشور", + "title": "مَنشور مِن %s" }, "settings": { "title": "الإعدادات", @@ -475,29 +475,29 @@ "appearance": { "title": "المظهر", "automatic": "تلقائي", - "light": "Always Light", - "dark": "Always Dark" + "light": "مضيءٌ دائمًا", + "dark": "مظلمٌ دائِمًا" }, "notifications": { "title": "الإشعارات", - "favorites": "Favorites my post", + "favorites": "الإعجاب بِمنشوراتي", "follows": "يتابعني", - "boosts": "Reblogs my post", - "mentions": "Mentions me", + "boosts": "إعادة تدوين منشوراتي", + "mentions": "الإشارة لي", "trigger": { - "anyone": "anyone", + "anyone": "أي شخص", "follower": "مشترِك", - "follow": "anyone I follow", - "noone": "no one", - "title": "Notify me when" + "follow": "أي شخص أُتابِعُه", + "noone": "لا أحد", + "title": "إشعاري عِندَ" } }, "preference": { "title": "التفضيلات", - "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": "النمط الأسود الداكِن الحقيقي", + "disable_avatar_animation": "تعطيل الصور الرمزية المتحرِّكة", + "disable_emoji_animation": "تعطيل الرموز التعبيرية المتحرِّكَة", + "using_default_browser": "اِستخدام المتصفح الافتراضي لفتح الروابط" }, "boring_zone": { "title": "المنطقة المملة", @@ -537,13 +537,13 @@ }, "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" + "dismiss_account_switcher": "تجاهُل مبدِّل الحساب", + "add_account": "إضافة حساب" }, "wizard": { - "new_in_mastodon": "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" + "accessibility_hint": "انقر نقرًا مزدوجًا لتجاهل النافذة المنبثقة" } } } \ No newline at end of file From 956c0cf0e1a9cd0779b1b939d6f0423ecbd674e7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Oct 2021 20:12:51 +0200 Subject: [PATCH 08/95] New translations ios-infoPlist.json (Arabic) --- Localization/StringsConvertor/input/ar_SA/ios-infoPlist.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/ios-infoPlist.json b/Localization/StringsConvertor/input/ar_SA/ios-infoPlist.json index 54fb1aacc..22fb2868e 100644 --- a/Localization/StringsConvertor/input/ar_SA/ios-infoPlist.json +++ b/Localization/StringsConvertor/input/ar_SA/ios-infoPlist.json @@ -1,6 +1,6 @@ { - "NSCameraUsageDescription": "Used to take photo for post status", - "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library", + "NSCameraUsageDescription": "يُستخدم لالتقاط الصورة عِندَ نشر الحالات", + "NSPhotoLibraryAddUsageDescription": "يُستخدم لحِفظ الصورة في مكتبة الصور", "NewPostShortcutItemTitle": "منشور جديد", "SearchShortcutItemTitle": "البحث" } From 45d212e2f0ce7363cf267a31ae8cc0047ecb9500 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 1 Oct 2021 20:12:52 +0200 Subject: [PATCH 09/95] New translations Localizable.stringsdict (Arabic) --- .../input/ar_SA/Localizable.stringsdict | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict index e6b0d5f95..be6601b3a 100644 --- a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict @@ -15,21 +15,21 @@ zero %ld unread notification one - 1 unread notification + إشعار واحِد غير مقروء two - %ld unread notification + إشعاران غير مقروءان few %ld unread notification many - %ld unread notification + %ld إشعارًا غيرَ مقروء other - %ld unread notification + %ld إشعار غير مقروء a11y.plural.count.input_limit_exceeds NSStringLocalizedFormatKey - Input limit exceeds %#@character_count@ + تمَّ تجاوز حدّ الإدخال %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -37,23 +37,23 @@ NSStringFormatValueTypeKey ld zero - %ld characters + لا حرف one - 1 character + حرفٌ واحِد two - %ld characters + حرفان اثنان few - %ld characters + %ld حُرُوف many - %ld characters + %ld حرفًا other - %ld characters + %ld حَرف a11y.plural.count.input_limit_remains NSStringLocalizedFormatKey - Input limit remains %#@character_count@ + يتبقَّى على حدّ الإدخال %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -61,17 +61,17 @@ NSStringFormatValueTypeKey ld zero - %ld characters + لا حرف one - 1 character + حرفٌ واحِد two - %ld characters + حرفان اثنان few - %ld characters + %ld حُرُوف many - %ld characters + %ld حرفًا other - %ld characters + %ld حَرف plural.count.metric_formatted.post @@ -85,17 +85,17 @@ NSStringFormatValueTypeKey ld zero - posts + لا منشور one - post + منشور two - posts + منشوران few - posts + منشورات many - posts + منشورًا other - posts + منشور plural.count.post @@ -109,17 +109,17 @@ NSStringFormatValueTypeKey ld zero - %ld posts + لا منشور one - 1 post + منشورٌ واحِد two - %ld posts + منشورانِ اثنان few - %ld posts + %ld منشورات many - %ld posts + %ld منشورًا other - %ld posts + %ld منشور plural.count.favorite @@ -133,17 +133,17 @@ NSStringFormatValueTypeKey ld zero - %ld favorites + لا إعجاب one - 1 favorite + إعجابٌ واحِد two - %ld favorites + إعجابانِ اثنان few - %ld favorites + %ld إعجابات many - %ld favorites + %ld إعجابًا other - %ld favorites + %ld إعجاب plural.count.reblog @@ -157,17 +157,17 @@ NSStringFormatValueTypeKey ld zero - %ld reblogs + لا إعاد تدوين one - 1 reblog + إعادةُ تدوينٍ واحِدة two - %ld reblogs + إعادتا تدوين few - %ld reblogs + %ld إعاداتِ تدوين many - %ld reblogs + %ld إعادةٍ للتدوين other - %ld reblogs + %ld إعادة تدوين plural.count.vote @@ -181,17 +181,17 @@ NSStringFormatValueTypeKey ld zero - %ld votes + لا صوت one - 1 vote + صوتٌ واحِد two - %ld votes + صوتانِ اثنان few - %ld votes + %ld أصوات many - %ld votes + %ld صوتًا other - %ld votes + %ld صوت plural.count.voter @@ -205,17 +205,17 @@ NSStringFormatValueTypeKey ld zero - %ld voters + لا مُصوِّتون one - 1 voter + مُصوِّتٌ واحِد two - %ld voters + مُصوِّتانِ اثنان few - %ld voters + %ld مُصوِّتين many - %ld voters + %ld مُصوِّتًا other - %ld voters + %ld مُصوِّت plural.people_talking From 69aa36876c13ce0e51fb8a1e3dcbf840e6e8e8de Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 4 Oct 2021 16:41:42 +0100 Subject: [PATCH 10/95] New translations Localizable.stringsdict (Arabic) --- .../input/ar_SA/Localizable.stringsdict | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict index be6601b3a..e85dc907e 100644 --- a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict @@ -469,17 +469,17 @@ NSStringFormatValueTypeKey ld zero - %ldM ago + مُنذُ لَحظة one - 1M ago + مُنذُ شهر two - %ldM ago + مُنذُ شهرين few - %ldM ago + مُنذُ %ld أشهُر many - %ldM ago + مُنذُ %ld شهرًا other - %ldM ago + مُنذُ %ld شهر date.day.ago.abbr @@ -493,17 +493,17 @@ NSStringFormatValueTypeKey ld zero - %ldd ago + مُنذُ لَحظة one - 1d ago + مُنذُ يوم two - %ldd ago + مُنذُ يومين few - %ldd ago + مُنذُ %ld أيام many - %ldd ago + مُنذُ %ld يومًا other - %ldd ago + مُنذُ %ld يوم date.hour.ago.abbr @@ -517,17 +517,17 @@ NSStringFormatValueTypeKey ld zero - %ldh ago + مُنذُ لَحظة one - 1h ago + مُنذُ ساعة two - %ldh ago + مُنذُ ساعتين few - %ldh ago + مُنذُ %ld ساعات many - %ldh ago + مُنذُ %ld ساعتًا other - %ldh ago + مُنذُ %ld ساعة date.minute.ago.abbr @@ -541,17 +541,17 @@ NSStringFormatValueTypeKey ld zero - %ldm ago + مُنذُ لَحظة one - 1m ago + مُنذُ دقيقة two - %ldm ago + مُنذُ دقيقتان few - %ldm ago + مُنذُ %ld دقائق many - %ldm ago + مُنذُ %ld دقيقتًا other - %ldm ago + مُنذُ %ld دقيقة date.second.ago.abbr @@ -565,17 +565,17 @@ NSStringFormatValueTypeKey ld zero - %lds ago + مُنذُ لَحظة one - 1s ago + مُنذُ ثانية two - %lds ago + مُنذُ ثانيتين few - %lds ago + مُنذُ %ld ثوان many - %lds ago + مُنذُ %ld ثانيتًا other - %lds ago + مُنذُ %ld ثانية From 5318e63ced9db5a2be851e7974f8c19feb2a6d76 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 4 Oct 2021 17:41:52 +0100 Subject: [PATCH 11/95] New translations Localizable.stringsdict (Arabic) --- .../input/ar_SA/Localizable.stringsdict | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict index e85dc907e..e3dee0d80 100644 --- a/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/ar_SA/Localizable.stringsdict @@ -229,17 +229,17 @@ NSStringFormatValueTypeKey ld zero - %ld people talking + لا أحَدَ يتحدَّث one - 1 people talking + شخصٌ واحدٌ يتحدَّث two - %ld people talking + شخصانِ اثنان يتحدَّثا few - %ld people talking + %ld أشخاصٍ يتحدَّثون many - %ld people talking + %ld شخصًا يتحدَّثون other - %ld people talking + %ld شخصٍ يتحدَّثون plural.count.following @@ -253,17 +253,17 @@ NSStringFormatValueTypeKey ld zero - %ld following + لا مُتابَع one - 1 following + مُتابَعٌ واحد two - %ld following + مُتابَعانِ few - %ld following + %ld مُتابَعين many - %ld following + %ld مُتابَعًا other - %ld following + %ld مُتابَع plural.count.follower @@ -279,15 +279,15 @@ zero %ld followers one - 1 follower + مُتابِعٌ واحد two - %ld followers + مُتابِعانِ اثنان few - %ld followers + %ld مُتابِعين many - %ld followers + %ld مُتابِعًا other - %ld followers + %ld مُتابِع date.year.left @@ -301,17 +301,17 @@ NSStringFormatValueTypeKey ld zero - %ld years left + تتبقى لَحظة one - 1 year left + تتبقى سنة two - %ld years left + تتبقى سنتين few - %ld years left + تتبقى %ld سنوات many - %ld years left + تتبقى %ld سنةً other - %ld years left + تتبقى %ld سنة date.month.left @@ -325,17 +325,17 @@ NSStringFormatValueTypeKey ld zero - %ld months left + تتبقى لَحظة one - 1 months left + يتبقى شهر two - %ld months left + يتبقى شهرين few - %ld months left + يتبقى %ld أشهر many - %ld months left + يتبقى %ld شهرًا other - %ld months left + يتبقى %ld شهر date.day.left @@ -349,17 +349,17 @@ NSStringFormatValueTypeKey ld zero - %ld days left + تتبقى لحظة one - 1 day left + يتبقى يوم two - %ld days left + يتبقى يومين few - %ld days left + يتبقى %ld أيام many - %ld days left + يتبقى %ld يومًا other - %ld days left + يتبقى %ld يوم date.hour.left @@ -373,17 +373,17 @@ NSStringFormatValueTypeKey ld zero - %ld hours left + تتبقى لَحظة one - 1 hour left + تتبقى ساعة two - %ld hours left + تتبقى ساعتين few - %ld hours left + تتبقى %ld ساعات many - %ld hours left + تتبقى %ld ساعةً other - %ld hours left + تتبقى %ld ساعة date.minute.left @@ -397,17 +397,17 @@ NSStringFormatValueTypeKey ld zero - %ld minutes left + تتبقى لَحظة one - 1 minute left + تتبقى دقيقة two - %ld minutes left + تتبقى دقيقتين few - %ld minutes left + تتبقى %ld دقائق many - %ld minutes left + تتبقى %ld دقيقةً other - %ld minutes left + تتبقى %ld دقيقة date.second.left @@ -421,17 +421,17 @@ NSStringFormatValueTypeKey ld zero - %ld seconds left + تتبقى لَحظة one - 1 second left + تتبقى ثانية two - %ld seconds left + تتبقى ثانيتين few - %ld seconds left + تتبقى %ld ثوان many - %ld seconds left + تتبقى %ld ثانيةً other - %ld seconds left + تتبقى %ld ثانية date.year.ago.abbr @@ -445,17 +445,17 @@ NSStringFormatValueTypeKey ld zero - %ldy ago + مُنذُ لَحظة one - 1y ago + مُنذُ سنة two - %ldy ago + مُنذُ سنتين few - %ldy ago + مُنذُ %ld سنين many - %ldy ago + مُنذُ %ld سنةً other - %ldy ago + مُنذُ %ld سنة date.month.ago.abbr @@ -525,7 +525,7 @@ few مُنذُ %ld ساعات many - مُنذُ %ld ساعتًا + مُنذُ %ld ساعةًَ other مُنذُ %ld ساعة @@ -549,7 +549,7 @@ few مُنذُ %ld دقائق many - مُنذُ %ld دقيقتًا + مُنذُ %ld دقيقةً other مُنذُ %ld دقيقة @@ -573,7 +573,7 @@ few مُنذُ %ld ثوان many - مُنذُ %ld ثانيتًا + مُنذُ %ld ثانية other مُنذُ %ld ثانية From 6b1d3f8738d0789bbe63cb35cf7dc9b581026803 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 8 Oct 2021 18:10:06 +0800 Subject: [PATCH 12/95] chore: update onboarding layout for iPad --- .../xcschemes/xcschememanagement.plist | 8 +- Mastodon/Coordinator/SceneCoordinator.swift | 8 +- .../MastodonConfirmEmailViewController.swift | 41 +++++- .../MastodonPickServerViewController.swift | 127 +++++++++++------- .../PickServerCategoriesCell.swift | 27 +++- .../TableViewCell/PickServerCell.swift | 23 +++- .../PickServerLoaderTableViewCell.swift | 24 +++- .../TableViewCell/PickServerSearchCell.swift | 23 +++- .../TableViewCell/PickServerTitleCell.swift | 46 ++++++- .../MastodonRegisterViewController.swift | 106 +++++++++++---- .../MastodonServerRulesViewController.swift | 54 +++++++- .../OnboardingViewControllerAppearance.swift | 42 +++++- .../Welcome/WelcomeViewController.swift | 25 +++- .../Root/MainTab/MainTabBarController.swift | 3 +- .../Scene/Root/RootSplitViewController.swift | 7 + 15 files changed, 448 insertions(+), 116 deletions(-) diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 068a73491..e6093a218 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 56 + 35 CoreDataStack.xcscheme_^#shared#^_ orderHint - 54 + 38 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 51 + 36 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 55 + 37 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 83ae61a84..3178a2cb7 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -214,7 +214,13 @@ extension SceneCoordinator { assertionFailure() return nil } - presentingViewController.presentPanModal(panModalPresentable) + + // https://github.com/slackhq/PanModal/issues/74#issuecomment-572426441 + panModalPresentable.modalPresentationStyle = .custom + panModalPresentable.modalPresentationCapturesStatusBarAppearance = true + panModalPresentable.transitioningDelegate = PanModalPresentationDelegate.default + presentingViewController.present(panModalPresentable, animated: true, completion: nil) + //presentingViewController.presentPanModal(panModalPresentable) case .custom(let transitioningDelegate): viewController.modalPresentationStyle = .custom diff --git a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift index 1abb35617..0718938f6 100644 --- a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift +++ b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift @@ -20,6 +20,8 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc var viewModel: MastodonConfirmEmailViewModel! + let stackView = UIStackView() + let largeTitleLabel: UILabel = { let label = UILabel() label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.systemFont(ofSize: 34, weight: .bold)) @@ -72,9 +74,10 @@ extension MastodonConfirmEmailViewController { override func viewDidLoad() { setupOnboardingAppearance() + configureTitleLabel() + configureMargin() // stackView - let stackView = UIStackView() stackView.axis = .vertical stackView.distribution = .fill stackView.spacing = 10 @@ -92,8 +95,8 @@ extension MastodonConfirmEmailViewController { stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor), - stackView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor), - stackView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor), + stackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), stackView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor), ]) NSLayoutConstraint.activate([ @@ -139,6 +142,38 @@ extension MastodonConfirmEmailViewController { .store(in: &self.disposeBag) } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureTitleLabel() + configureMargin() + } + +} + +extension MastodonConfirmEmailViewController { + private func configureTitleLabel() { + switch traitCollection.horizontalSizeClass { + case .regular: + navigationItem.largeTitleDisplayMode = .always + navigationItem.title = L10n.Scene.ConfirmEmail.title.replacingOccurrences(of: "\n", with: " ") + largeTitleLabel.isHidden = true + default: + navigationItem.largeTitleDisplayMode = .never + navigationItem.title = nil + largeTitleLabel.isHidden = false + } + } + + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonConfirmEmailViewController.viewEdgeMargin + stackView.layoutMargins = UIEdgeInsets(top: 18, left: margin, bottom: 23, right: margin) + default: + stackView.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 23, right: 0) + } + } } extension MastodonConfirmEmailViewController { diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift index 3da704f0b..f3570c6c5 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift @@ -29,6 +29,8 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency private var expandServerDomainSet = Set() private let emptyStateView = PickServerEmptyStateView() + private var emptyStateViewLeadingLayoutConstraint: NSLayoutConstraint! + private var emptyStateViewTrailingLayoutConstraint: NSLayoutConstraint! let tableViewTopPaddingView = UIView() // fix empty state view background display when tableView bounce scrolling var tableViewTopPaddingViewHeightLayoutConstraint: NSLayoutConstraint! @@ -52,13 +54,14 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency return tableView }() + let buttonContainer = UIView() let nextStepButton: PrimaryActionButton = { let button = PrimaryActionButton() button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() - var nextStepButtonBottomLayoutConstraint: NSLayoutConstraint! + var buttonContainerBottomLayoutConstraint: NSLayoutConstraint! var mastodonAuthenticationController: MastodonAuthenticationController? @@ -77,6 +80,8 @@ extension MastodonPickServerViewController { setupOnboardingAppearance() defer { setupNavigationBarBackgroundView() } + configureTitleLabel() + configureMargin() #if DEBUG navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), style: .plain, target: nil, action: nil) @@ -89,14 +94,24 @@ extension MastodonPickServerViewController { navigationItem.rightBarButtonItem?.menu = UIMenu(title: "Debug Tool", image: nil, identifier: nil, options: [], children: children) #endif - view.addSubview(nextStepButton) - nextStepButtonBottomLayoutConstraint = view.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: 0).priority(.defaultHigh) + buttonContainer.translatesAutoresizingMaskIntoConstraints = false + buttonContainer.preservesSuperviewLayoutMargins = true + view.addSubview(buttonContainer) + buttonContainerBottomLayoutConstraint = view.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor, constant: 0).priority(.defaultHigh) NSLayoutConstraint.activate([ - nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: MastodonPickServerViewController.actionButtonMargin), - view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: MastodonPickServerViewController.actionButtonMargin), + buttonContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor), + buttonContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor), + view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualTo: buttonContainer.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight), + buttonContainerBottomLayoutConstraint, + ]) + + view.addSubview(nextStepButton) + NSLayoutConstraint.activate([ + nextStepButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor), + nextStepButton.leadingAnchor.constraint(equalTo: buttonContainer.layoutMarginsGuide.leadingAnchor), + buttonContainer.layoutMarginsGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor), + nextStepButton.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor), nextStepButton.heightAnchor.constraint(equalToConstant: MastodonPickServerViewController.actionButtonHeight).priority(.defaultHigh), - view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualTo: nextStepButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight), - nextStepButtonBottomLayoutConstraint, ]) // fix AutoLayout warning when observe before view appear @@ -127,16 +142,18 @@ extension MastodonPickServerViewController { tableView.topAnchor.constraint(equalTo: view.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - nextStepButton.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 7), + buttonContainer.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 7), ]) emptyStateView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(emptyStateView) + emptyStateViewLeadingLayoutConstraint = emptyStateView.leadingAnchor.constraint(equalTo: tableView.leadingAnchor) + emptyStateViewTrailingLayoutConstraint = tableView.trailingAnchor.constraint(equalTo: emptyStateView.trailingAnchor) NSLayoutConstraint.activate([ emptyStateView.topAnchor.constraint(equalTo: view.topAnchor), - emptyStateView.leadingAnchor.constraint(equalTo: tableView.readableContentGuide.leadingAnchor), - emptyStateView.trailingAnchor.constraint(equalTo: tableView.readableContentGuide.trailingAnchor), - nextStepButton.topAnchor.constraint(equalTo: emptyStateView.bottomAnchor, constant: 21), + emptyStateViewLeadingLayoutConstraint, + emptyStateViewTrailingLayoutConstraint, + buttonContainer.topAnchor.constraint(equalTo: emptyStateView.bottomAnchor, constant: 21), ]) view.sendSubviewToBack(emptyStateView) @@ -154,18 +171,18 @@ extension MastodonPickServerViewController { // guard external keyboard connected guard isShow, state == .dock, GCKeyboard.coalesced != nil else { - self.nextStepButtonBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight + self.buttonContainerBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight return } let externalKeyboardToolbarHeight = self.view.frame.maxY - endFrame.minY guard externalKeyboardToolbarHeight > 0 else { - self.nextStepButtonBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight + self.buttonContainerBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight return } UIView.animate(withDuration: 0.3) { - self.nextStepButtonBottomLayoutConstraint.constant = externalKeyboardToolbarHeight + 16 + self.buttonContainerBottomLayoutConstraint.constant = externalKeyboardToolbarHeight + 16 self.view.layoutIfNeeded() } } @@ -274,9 +291,34 @@ extension MastodonPickServerViewController { viewModel.viewWillAppear.send() } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + setupNavigationBarAppearance() + updateEmptyStateViewLayout() + configureTitleLabel() + configureMargin() + } } +extension MastodonPickServerViewController { + private func configureTitleLabel() { + guard UIDevice.current.userInterfaceIdiom == .pad else { + return + } + + switch traitCollection.horizontalSizeClass { + case .regular: + navigationItem.largeTitleDisplayMode = .always + navigationItem.title = L10n.Scene.ServerPicker.title.replacingOccurrences(of: "\n", with: " ") + default: + navigationItem.largeTitleDisplayMode = .never + navigationItem.title = nil + } + } +} + extension MastodonPickServerViewController { @objc @@ -426,43 +468,6 @@ extension MastodonPickServerViewController: UITableViewDelegate { } } - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - let headerView = UIView() - headerView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color - return headerView - } - - func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - guard let diffableDataSource = viewModel.diffableDataSource else { - return .leastNonzeroMagnitude - } - let sections = diffableDataSource.snapshot().sectionIdentifiers - let section = sections[section] - switch section { - case .header: - return 20 - case .category: - // Since category view has a blur shadow effect, its height need to be large than the actual height, - // Thus we reduce the section header's height by 10, and make the category cell height 60+20(10 inset for top and bottom) - return 10 - case .search: - // Same reason as above - return 10 - case .servers: - return .leastNonzeroMagnitude - } - } - - func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - let footerView = UIView() - footerView.backgroundColor = .yellow - return footerView - } - - func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return .leastNonzeroMagnitude - } - func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { guard let diffableDataSource = viewModel.diffableDataSource else { return nil } guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return nil } @@ -521,6 +526,26 @@ extension MastodonPickServerViewController { let rectInTableView = tableView.rectForRow(at: indexPath) emptyStateView.topPaddingViewTopLayoutConstraint.constant = rectInTableView.maxY + + switch traitCollection.horizontalSizeClass { + case .regular: + emptyStateViewLeadingLayoutConstraint.constant = MastodonPickServerViewController.viewEdgeMargin + emptyStateViewTrailingLayoutConstraint.constant = MastodonPickServerViewController.viewEdgeMargin + default: + let margin = tableView.layoutMarginsGuide.layoutFrame.origin.x + emptyStateViewLeadingLayoutConstraint.constant = margin + emptyStateViewTrailingLayoutConstraint.constant = margin + } + } + + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + buttonContainer.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + buttonContainer.layoutMargins = .zero + } } } diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift index 8207ccdb9..659317752 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCategoriesCell.swift @@ -56,12 +56,13 @@ extension PickServerCategoriesCell { private func _init() { selectionStyle = .none backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color + configureMargin() metricView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(metricView) NSLayoutConstraint.activate([ - metricView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - metricView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + metricView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), + metricView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), metricView.topAnchor.constraint(equalTo: contentView.topAnchor), metricView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), metricView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh), @@ -71,14 +72,20 @@ extension PickServerCategoriesCell { NSLayoutConstraint.activate([ collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - collectionView.topAnchor.constraint(equalTo: contentView.topAnchor), - collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + collectionView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), + contentView.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 20), collectionView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh), ]) collectionView.delegate = self } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureMargin() + } + override func layoutSubviews() { super.layoutSubviews() @@ -87,6 +94,18 @@ extension PickServerCategoriesCell { } +extension PickServerCategoriesCell { + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + contentView.layoutMargins = .zero + } + } +} + // MARK: - UICollectionViewDelegateFlowLayout extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout { diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift index ee2471878..2f60a5206 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift @@ -198,6 +198,7 @@ extension PickServerCell { private func _init() { selectionStyle = .none backgroundColor = .clear + configureMargin() contentView.addSubview(containerView) containerView.addSubview(domainLabel) @@ -229,8 +230,8 @@ extension PickServerCell { NSLayoutConstraint.activate([ // Set background view containerView.topAnchor.constraint(equalTo: contentView.topAnchor), - containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), + contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), // Set bottom separator @@ -291,6 +292,12 @@ extension PickServerCell { expandButton.addTarget(self, action: #selector(expandButtonDidPressed(_:)), for: .touchUpInside) } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureMargin() + } + private func makeVerticalInfoStackView(arrangedView: UIView...) -> UIStackView { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false @@ -318,6 +325,18 @@ extension PickServerCell { } } +extension PickServerCell { + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + contentView.layoutMargins = .zero + } + } +} + extension PickServerCell { enum ExpandMode { diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerLoaderTableViewCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerLoaderTableViewCell.swift index 1b8264ec3..945ecac6a 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerLoaderTableViewCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerLoaderTableViewCell.swift @@ -37,14 +37,16 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell { override func _init() { super._init() + configureMargin() + contentView.addSubview(containerView) contentView.addSubview(seperator) NSLayoutConstraint.activate([ // Set background view containerView.topAnchor.constraint(equalTo: contentView.topAnchor), - containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), + contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 1), // Set bottom separator @@ -67,6 +69,24 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell { activityIndicatorView.isHidden = false startAnimating() } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureMargin() + } +} + +extension PickServerLoaderTableViewCell { + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + contentView.layoutMargins = .zero + } + } } #if canImport(SwiftUI) && DEBUG diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift index fa3e3ae27..0a64103d2 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerSearchCell.swift @@ -109,6 +109,7 @@ extension PickServerSearchCell { private func _init() { selectionStyle = .none backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color + configureMargin() searchTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) searchTextField.delegate = self @@ -118,9 +119,9 @@ extension PickServerSearchCell { contentView.addSubview(searchTextField) NSLayoutConstraint.activate([ - bgView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + bgView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), bgView.topAnchor.constraint(equalTo: contentView.topAnchor), - bgView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + bgView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), bgView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), textFieldBgView.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 14), @@ -134,6 +135,24 @@ extension PickServerSearchCell { textFieldBgView.bottomAnchor.constraint(equalTo: searchTextField.bottomAnchor, constant: 4), ]) } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureMargin() + } +} + +extension PickServerSearchCell { + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + contentView.layoutMargins = .zero + } + } } extension PickServerSearchCell { diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift index 682ebbf30..f0d78eb41 100644 --- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift +++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerTitleCell.swift @@ -20,6 +20,8 @@ final class PickServerTitleCell: UITableViewCell { return label }() + var containerHeightLayoutConstraint: NSLayoutConstraint! + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) _init() @@ -36,13 +38,45 @@ extension PickServerTitleCell { private func _init() { selectionStyle = .none backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color - - contentView.addSubview(titleLabel) + + let container = UIStackView() + container.axis = .vertical + container.translatesAutoresizingMaskIntoConstraints = false + containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: .leastNonzeroMagnitude) + contentView.addSubview(container) NSLayoutConstraint.activate([ - titleLabel.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), - contentView.readableContentGuide.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor), - titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor), - titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + container.topAnchor.constraint(equalTo: contentView.topAnchor), + container.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + container.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + container.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), ]) + + container.addArrangedSubview(titleLabel) + + configureTitleLabelDisplay() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureTitleLabelDisplay() + } +} + +extension PickServerTitleCell { + private func configureTitleLabelDisplay() { + guard traitCollection.userInterfaceIdiom == .pad else { + titleLabel.isHidden = false + return + } + + switch traitCollection.horizontalSizeClass { + case .regular: + titleLabel.isHidden = true + containerHeightLayoutConstraint.isActive = true + default: + titleLabel.isHidden = false + containerHeightLayoutConstraint.isActive = false + } } } diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift index ffef3d872..b86c46745 100644 --- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift @@ -61,6 +61,8 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O return scrollview }() + let stackView = UIStackView() + let largeTitleLabel: UILabel = { let label = UILabel() label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) @@ -287,7 +289,11 @@ extension MastodonRegisterViewController { super.viewDidLoad() setupOnboardingAppearance() - defer { setupNavigationBarBackgroundView() } + configureTitleLabel() + defer { + setupNavigationBarBackgroundView() + configureFormLayout() + } avatarButton.menu = createMediaContextMenu() avatarButton.showsMenuAsPrimaryAction = true @@ -307,7 +313,6 @@ extension MastodonRegisterViewController { tapGestureRecognizer.addTarget(self, action: #selector(tapGestureRecognizerHandler)) // stackview - let stackView = UIStackView() stackView.axis = .vertical stackView.distribution = .fill stackView.spacing = 40 @@ -315,17 +320,24 @@ extension MastodonRegisterViewController { stackView.isLayoutMarginsRelativeArrangement = true stackView.addArrangedSubview(largeTitleLabel) stackView.addArrangedSubview(avatarView) - stackView.addArrangedSubview(usernameTextField) - stackView.addArrangedSubview(displayNameTextField) - stackView.addArrangedSubview(emailTextField) - stackView.addArrangedSubview(passwordTextField) - stackView.addArrangedSubview(passwordCheckLabel) + + let formTableStackView = UIStackView() + stackView.addArrangedSubview(formTableStackView) + formTableStackView.axis = .vertical + formTableStackView.distribution = .fill + formTableStackView.spacing = 40 + + formTableStackView.addArrangedSubview(usernameTextField) + formTableStackView.addArrangedSubview(displayNameTextField) + formTableStackView.addArrangedSubview(emailTextField) + formTableStackView.addArrangedSubview(passwordTextField) + formTableStackView.addArrangedSubview(passwordCheckLabel) if viewModel.approvalRequired { - stackView.addArrangedSubview(reasonTextField) + formTableStackView.addArrangedSubview(reasonTextField) } usernameErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false - stackView.addSubview(usernameErrorPromptLabel) + formTableStackView.addSubview(usernameErrorPromptLabel) NSLayoutConstraint.activate([ usernameErrorPromptLabel.topAnchor.constraint(equalTo: usernameTextField.bottomAnchor, constant: 6), usernameErrorPromptLabel.leadingAnchor.constraint(equalTo: usernameTextField.leadingAnchor), @@ -333,7 +345,7 @@ extension MastodonRegisterViewController { ]) emailErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false - stackView.addSubview(emailErrorPromptLabel) + formTableStackView.addSubview(emailErrorPromptLabel) NSLayoutConstraint.activate([ emailErrorPromptLabel.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 6), emailErrorPromptLabel.leadingAnchor.constraint(equalTo: emailTextField.leadingAnchor), @@ -341,7 +353,7 @@ extension MastodonRegisterViewController { ]) passwordErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false - stackView.addSubview(passwordErrorPromptLabel) + formTableStackView.addSubview(passwordErrorPromptLabel) NSLayoutConstraint.activate([ passwordErrorPromptLabel.topAnchor.constraint(equalTo: passwordCheckLabel.bottomAnchor, constant: 2), passwordErrorPromptLabel.leadingAnchor.constraint(equalTo: passwordTextField.leadingAnchor), @@ -373,12 +385,14 @@ extension MastodonRegisterViewController { avatarView.translatesAutoresizingMaskIntoConstraints = false avatarView.addSubview(avatarButton) NSLayoutConstraint.activate([ - avatarView.heightAnchor.constraint(equalToConstant: 90).priority(.defaultHigh), + avatarView.heightAnchor.constraint(equalToConstant: 92).priority(.required - 1), ]) avatarButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - avatarButton.heightAnchor.constraint(equalToConstant: 92).priority(.defaultHigh), - avatarButton.widthAnchor.constraint(equalToConstant: 92).priority(.defaultHigh), + avatarButton.heightAnchor.constraint(equalToConstant: 92).priority(.required - 1), + avatarButton.widthAnchor.constraint(equalToConstant: 92).priority(.required - 1), + avatarButton.leadingAnchor.constraint(greaterThanOrEqualTo: avatarView.leadingAnchor).priority(.required - 1), + avatarView.trailingAnchor.constraint(greaterThanOrEqualTo: avatarButton.trailingAnchor).priority(.required - 1), avatarButton.centerXAnchor.constraint(equalTo: avatarView.centerXAnchor), avatarButton.centerYAnchor.constraint(equalTo: avatarView.centerYAnchor), ]) @@ -392,15 +406,15 @@ extension MastodonRegisterViewController { // textfield NSLayoutConstraint.activate([ - usernameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh), - displayNameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh), - emailTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh), - passwordTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh), + usernameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1), + displayNameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1), + emailTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1), + passwordTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1), ]) // password - stackView.setCustomSpacing(6, after: passwordTextField) - stackView.setCustomSpacing(32, after: passwordCheckLabel) + formTableStackView.setCustomSpacing(6, after: passwordTextField) + formTableStackView.setCustomSpacing(32, after: passwordCheckLabel) // return if viewModel.approvalRequired { @@ -410,16 +424,22 @@ extension MastodonRegisterViewController { } // button - stackView.addArrangedSubview(buttonContainer) + formTableStackView.addArrangedSubview(buttonContainer) signUpButton.translatesAutoresizingMaskIntoConstraints = false buttonContainer.addSubview(signUpButton) NSLayoutConstraint.activate([ signUpButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor), - signUpButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor, constant: MastodonRegisterViewController.actionButtonMargin), - buttonContainer.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: MastodonRegisterViewController.actionButtonMargin), + signUpButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor), + buttonContainer.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor), buttonContainer.bottomAnchor.constraint(equalTo: signUpButton.bottomAnchor), - signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.defaultHigh), + signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.required - 1), + buttonContainer.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.required - 1), ]) + signUpButton.setContentHuggingPriority(.defaultLow, for: .horizontal) + signUpButton.setContentHuggingPriority(.defaultLow, for: .vertical) + signUpButton.setContentCompressionResistancePriority(.required - 1, for: .vertical) + signUpButton.setContentCompressionResistancePriority(.required - 1, for: .horizontal) + buttonContainer.setContentCompressionResistancePriority(.required - 1, for: .vertical) Publishers.CombineLatest( KeyboardResponderService.shared.state.eraseToAnyPublisher(), @@ -645,6 +665,12 @@ extension MastodonRegisterViewController { plusIconImageView.layer.masksToBounds = true } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + configureTitleLabel() + configureFormLayout() + } } extension MastodonRegisterViewController: UITextFieldDelegate { @@ -714,7 +740,7 @@ extension MastodonRegisterViewController: UITextFieldDelegate { textField.layer.shadowRadius = 2.0 textField.layer.shadowOffset = CGSize.zero textField.layer.shadowColor = color.cgColor - textField.layer.shadowPath = UIBezierPath(roundedRect: textField.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)).cgPath + // textField.layer.shadowPath = UIBezierPath(roundedRect: textField.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)).cgPath } private func setTextFieldValidAppearance(_ textField: UITextField, validateState: MastodonRegisterViewModel.ValidateState) { @@ -729,6 +755,36 @@ extension MastodonRegisterViewController: UITextFieldDelegate { } } +extension MastodonRegisterViewController { + private func configureTitleLabel() { + switch traitCollection.horizontalSizeClass { + case .regular: + navigationItem.largeTitleDisplayMode = .always + navigationItem.title = L10n.Scene.ServerPicker.title.replacingOccurrences(of: "\n", with: " ") + largeTitleLabel.isHidden = true + default: + navigationItem.largeTitleDisplayMode = .never + navigationItem.title = nil + largeTitleLabel.isHidden = false + } + } + + private func configureFormLayout() { + switch traitCollection.horizontalSizeClass { + case .regular: + stackView.axis = .horizontal + stackView.distribution = .fillProportionally + default: + stackView.axis = .vertical + stackView.distribution = .fill + } + } + + private func configureMargin() { + + } +} + extension MastodonRegisterViewController { @objc private func tapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) { view.endEditing(true) diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift index b9a332d05..e93d06e19 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift @@ -21,6 +21,8 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency var viewModel: MastodonServerRulesViewModel! + let stackView = UIStackView() + let largeTitleLabel: UILabel = { let label = UILabel() label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold)) @@ -96,6 +98,8 @@ extension MastodonServerRulesViewController { super.viewDidLoad() setupOnboardingAppearance() + configureTitleLabel() + configureMargin() configTextView() defer { setupNavigationBarBackgroundView() } @@ -116,8 +120,8 @@ extension MastodonServerRulesViewController { bottomContainerView.addSubview(confirmButton) NSLayoutConstraint.activate([ bottomContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight), - confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin), - bottomContainerView.readableContentGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin), + confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor), + bottomContainerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor), confirmButton.heightAnchor.constraint(equalToConstant: MastodonServerRulesViewController.actionButtonHeight).priority(.defaultHigh), ]) @@ -125,8 +129,8 @@ extension MastodonServerRulesViewController { bottomContainerView.addSubview(bottomPromptMetaText.textView) NSLayoutConstraint.activate([ bottomPromptMetaText.textView.frameLayoutGuide.topAnchor.constraint(equalTo: bottomContainerView.topAnchor, constant: 20), - bottomPromptMetaText.textView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor), - bottomPromptMetaText.textView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.trailingAnchor), + bottomPromptMetaText.textView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor), + bottomPromptMetaText.textView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.trailingAnchor), confirmButton.topAnchor.constraint(equalTo: bottomPromptMetaText.textView.frameLayoutGuide.bottomAnchor, constant: 20), ]) @@ -140,10 +144,10 @@ extension MastodonServerRulesViewController { scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor), ]) - let stackView = UIStackView() stackView.axis = .vertical stackView.distribution = .fill stackView.spacing = 10 + stackView.isLayoutMarginsRelativeArrangement = true stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) stackView.addArrangedSubview(largeTitleLabel) stackView.addArrangedSubview(subtitleLabel) @@ -178,6 +182,46 @@ extension MastodonServerRulesViewController { updateScrollViewContentInset() } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + setupNavigationBarAppearance() + configureTitleLabel() + configureMargin() + } + +} + +extension MastodonServerRulesViewController { + private func configureTitleLabel() { + guard UIDevice.current.userInterfaceIdiom == .pad else { + return + } + + switch traitCollection.horizontalSizeClass { + case .regular: + navigationItem.largeTitleDisplayMode = .always + navigationItem.title = L10n.Scene.ServerRules.title.replacingOccurrences(of: "\n", with: " ") + largeTitleLabel.isHidden = true + default: + navigationItem.leftBarButtonItem = nil + navigationItem.largeTitleDisplayMode = .never + navigationItem.title = nil + largeTitleLabel.isHidden = false + } + } + + private func configureMargin() { + switch traitCollection.horizontalSizeClass { + case .regular: + let margin = MastodonPickServerViewController.viewEdgeMargin + stackView.layoutMargins = UIEdgeInsets(top: 32, left: margin, bottom: 20, right: margin) + bottomContainerView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin) + default: + stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) + bottomContainerView.layoutMargins = .zero + } + } } extension MastodonServerRulesViewController { diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift index d93c677f8..4a4d04bf6 100644 --- a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift +++ b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift @@ -24,19 +24,38 @@ extension OnboardingViewControllerAppearance { setupNavigationBarAppearance() - let backItem = UIBarButtonItem() - backItem.title = L10n.Common.Controls.Actions.back + let backItem = UIBarButtonItem( + title: L10n.Common.Controls.Actions.back, + style: .plain, + target: nil, + action: nil + ) navigationItem.backBarButtonItem = backItem } func setupNavigationBarAppearance() { // use TransparentBackground so view push / dismiss will be more visual nature // please add opaque background for status bar manually if needs - let barAppearance = UINavigationBarAppearance() - barAppearance.configureWithTransparentBackground() - navigationController?.navigationBar.standardAppearance = barAppearance - navigationController?.navigationBar.compactAppearance = barAppearance - navigationController?.navigationBar.scrollEdgeAppearance = barAppearance + + switch traitCollection.userInterfaceIdiom { + case .pad: + if traitCollection.horizontalSizeClass == .regular { + // do nothing + } else { + fallthrough + } + default: + let barAppearance = UINavigationBarAppearance() + barAppearance.configureWithTransparentBackground() + navigationItem.standardAppearance = barAppearance + navigationItem.compactAppearance = barAppearance + navigationItem.scrollEdgeAppearance = barAppearance + if #available(iOS 15.0, *) { + navigationItem.compactScrollEdgeAppearance = barAppearance + } else { + // Fallback on earlier versions + } + } } func setupNavigationBarBackgroundView() { @@ -57,3 +76,12 @@ 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 + } +} diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index 705ae6132..a2a266f9d 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -75,6 +75,8 @@ extension WelcomeViewController { override func viewDidLoad() { super.viewDidLoad() + navigationController?.navigationBar.prefersLargeTitles = true + navigationItem.largeTitleDisplayMode = .never view.overrideUserInterfaceStyle = .light setupOnboardingAppearance() @@ -235,7 +237,21 @@ extension WelcomeViewController { } // MARK: - OnboardingViewControllerAppearance -extension WelcomeViewController: OnboardingViewControllerAppearance { } +extension WelcomeViewController: OnboardingViewControllerAppearance { + func setupNavigationBarAppearance() { + // always transparent + let barAppearance = UINavigationBarAppearance() + barAppearance.configureWithTransparentBackground() + navigationItem.standardAppearance = barAppearance + navigationItem.compactAppearance = barAppearance + navigationItem.scrollEdgeAppearance = barAppearance + if #available(iOS 15.0, *) { + navigationItem.compactScrollEdgeAppearance = barAppearance + } else { + // Fallback on earlier versions + } + } +} // MARK: - UIAdaptivePresentationControllerDelegate extension WelcomeViewController: UIAdaptivePresentationControllerDelegate { @@ -245,7 +261,12 @@ extension WelcomeViewController: UIAdaptivePresentationControllerDelegate { // make underneath view controller alive to fix layout issue due to view life cycle return .fullScreen default: - return .pageSheet + switch traitCollection.horizontalSizeClass { + case .regular: + return .pageSheet + default: + return .fullScreen + } } } diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift index 1681b6171..d24a67c4a 100644 --- a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift +++ b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift @@ -317,7 +317,7 @@ extension MainTabBarController { switch tab { case .me: - coordinator.present(scene: .accountList, from: nil, transition: .panModal) + coordinator.present(scene: .accountList, from: self, transition: .panModal) default: break } @@ -353,7 +353,6 @@ extension MainTabBarController { self.avatarButton.setContentHuggingPriority(.required - 1, for: .vertical) self.avatarButton.isUserInteractionEnabled = false } - } extension MainTabBarController { diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 2e8beef9e..15464c9db 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -201,6 +201,12 @@ extension RootSplitViewController: UISplitViewControllerDelegate { displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode ) -> UISplitViewController.DisplayMode { let compactNavigationController = mainTabBarController.selectedViewController as? UINavigationController + + if let topMost = compactNavigationController?.topMost, + topMost is AccountListViewController { + topMost.dismiss(animated: false, completion: nil) + } + let viewControllers = compactNavigationController?.popToRootViewController(animated: true) ?? [] var supplementaryViewControllers: [UIViewController] = [] @@ -219,6 +225,7 @@ extension RootSplitViewController: UISplitViewControllerDelegate { if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { secondaryNavigationController.setViewControllers(secondaryNavigationController.viewControllers + secondaryViewControllers, animated: false) } + return proposedDisplayMode } From b5052cca5ec6183bd04bad7a5b963b489bff3216 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 8 Oct 2021 18:47:46 +0800 Subject: [PATCH 13/95] fix: make interface style preference as global setting --- .../CoreData 2.xcdatamodel/contents | 3 +-- CoreDataStack/Entity/Setting.swift | 24 +++++++++++-------- .../Diffiable/Section/SettingsSection.swift | 24 ++++++++----------- .../Extension/CoreDataStack/Setting.swift | 6 ++--- Mastodon/Info.plist | 2 ++ .../Settings/SettingsViewController.swift | 21 +++++++++------- .../SettingsAppearanceTableViewCell.swift | 2 ++ Mastodon/Service/SettingService.swift | 23 +++++++++--------- 8 files changed, 55 insertions(+), 50 deletions(-) diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents index 670241f35..c26a0bdbb 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents @@ -191,7 +191,6 @@ - @@ -289,7 +288,7 @@ - + diff --git a/CoreDataStack/Entity/Setting.swift b/CoreDataStack/Entity/Setting.swift index 27971157f..94ee50959 100644 --- a/CoreDataStack/Entity/Setting.swift +++ b/CoreDataStack/Entity/Setting.swift @@ -13,7 +13,7 @@ public final class Setting: NSManagedObject { @NSManaged public var domain: String @NSManaged public var userID: String - @NSManaged public var appearanceRaw: String +// @NSManaged public var appearanceRaw: String @NSManaged public var preferredTrueBlackDarkMode: Bool @NSManaged public var preferredStaticAvatar: Bool @NSManaged public var preferredStaticEmoji: Bool @@ -41,17 +41,17 @@ extension Setting { property: Property ) -> Setting { let setting: Setting = context.insertObject() - setting.appearanceRaw = property.appearanceRaw +// setting.appearanceRaw = property.appearanceRaw setting.domain = property.domain setting.userID = property.userID return setting } - public func update(appearanceRaw: String) { - guard appearanceRaw != self.appearanceRaw else { return } - self.appearanceRaw = appearanceRaw - didUpdate(at: Date()) - } +// public func update(appearanceRaw: String) { +// guard appearanceRaw != self.appearanceRaw else { return } +// self.appearanceRaw = appearanceRaw +// didUpdate(at: Date()) +// } public func update(preferredTrueBlackDarkMode: Bool) { guard preferredTrueBlackDarkMode != self.preferredTrueBlackDarkMode else { return } @@ -87,12 +87,16 @@ extension Setting { public struct Property { public let domain: String public let userID: String - public let appearanceRaw: String +// public let appearanceRaw: String - public init(domain: String, userID: String, appearanceRaw: String) { + public init( + domain: String, + userID: String +// appearanceRaw: String + ) { self.domain = domain self.userID = userID - self.appearanceRaw = appearanceRaw +// self.appearanceRaw = appearanceRaw } } } diff --git a/Mastodon/Diffiable/Section/SettingsSection.swift b/Mastodon/Diffiable/Section/SettingsSection.swift index 939fd4315..f59c13587 100644 --- a/Mastodon/Diffiable/Section/SettingsSection.swift +++ b/Mastodon/Diffiable/Section/SettingsSection.swift @@ -41,21 +41,17 @@ extension SettingsSection { switch item { case .appearance(let objectID): let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: SettingsAppearanceTableViewCell.self), for: indexPath) as! SettingsAppearanceTableViewCell - managedObjectContext.performAndWait { - let setting = managedObjectContext.object(with: objectID) as! Setting - cell.update(with: setting.appearance) - ManagedObjectObserver.observe(object: setting) - .receive(on: DispatchQueue.main) - .sink(receiveCompletion: { _ in - // do nothing - }, receiveValue: { [weak cell] change in - guard let cell = cell else { return } - guard case .update(let object) = change.changeType, - let setting = object as? Setting else { return } - cell.update(with: setting.appearance) - }) - .store(in: &cell.disposeBag) + UserDefaults.shared.observe(\.customUserInterfaceStyle, options: [.initial, .new]) { [weak cell] defaults, _ in + guard let cell = cell else { return } + switch defaults.customUserInterfaceStyle { + case .unspecified: cell.update(with: .automatic) + case .dark: cell.update(with: .dark) + case .light: cell.update(with: .light) + @unknown default: + assertionFailure() + } } + .store(in: &cell.observations) cell.delegate = settingsAppearanceTableViewCellDelegate return cell case .notification(let objectID, let switchMode): diff --git a/Mastodon/Extension/CoreDataStack/Setting.swift b/Mastodon/Extension/CoreDataStack/Setting.swift index b995b80e3..4d1fc0ca5 100644 --- a/Mastodon/Extension/CoreDataStack/Setting.swift +++ b/Mastodon/Extension/CoreDataStack/Setting.swift @@ -11,9 +11,9 @@ import MastodonSDK extension Setting { - var appearance: SettingsItem.AppearanceMode { - return SettingsItem.AppearanceMode(rawValue: appearanceRaw) ?? .automatic - } +// var appearance: SettingsItem.AppearanceMode { +// return SettingsItem.AppearanceMode(rawValue: appearanceRaw) ?? .automatic +// } var activeSubscription: Subscription? { return (subscriptions ?? Set()) diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 129f6bf10..c144fc6e7 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -2,6 +2,8 @@ + UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift index 3b4e522e6..04c343647 100644 --- a/Mastodon/Scene/Settings/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/SettingsViewController.swift @@ -439,7 +439,7 @@ extension SettingsViewController { .sink { _ in // do nothing } receiveValue: { _ in - // do nohting + // do nothing } .store(in: &disposeBag) } @@ -451,16 +451,19 @@ extension SettingsViewController: SettingsAppearanceTableViewCellDelegate { guard let dataSource = viewModel.dataSource else { return } guard let indexPath = tableView.indexPath(for: cell) else { return } let item = dataSource.itemIdentifier(for: indexPath) - guard case let .appearance(settingObjectID) = item else { return } + guard case .appearance = item else { return } - context.managedObjectContext.performChanges { - let setting = self.context.managedObjectContext.object(with: settingObjectID) as! Setting - setting.update(appearanceRaw: appearanceMode.rawValue) + switch appearanceMode { + case .automatic: + UserDefaults.shared.customUserInterfaceStyle = .unspecified + case .light: + UserDefaults.shared.customUserInterfaceStyle = .light + case .dark: + UserDefaults.shared.customUserInterfaceStyle = .dark } - .sink { _ in - let feedbackGenerator = UIImpactFeedbackGenerator(style: .light) - feedbackGenerator.impactOccurred() - }.store(in: &disposeBag) + + let feedbackGenerator = UIImpactFeedbackGenerator(style: .light) + feedbackGenerator.impactOccurred() } } diff --git a/Mastodon/Scene/Settings/View/Cell/SettingsAppearanceTableViewCell.swift b/Mastodon/Scene/Settings/View/Cell/SettingsAppearanceTableViewCell.swift index c4eb998e4..a4904136b 100644 --- a/Mastodon/Scene/Settings/View/Cell/SettingsAppearanceTableViewCell.swift +++ b/Mastodon/Scene/Settings/View/Cell/SettingsAppearanceTableViewCell.swift @@ -15,6 +15,7 @@ protocol SettingsAppearanceTableViewCellDelegate: AnyObject { class SettingsAppearanceTableViewCell: UITableViewCell { var disposeBag = Set() + var observations = Set() static let spacing: CGFloat = 18 @@ -59,6 +60,7 @@ class SettingsAppearanceTableViewCell: UITableViewCell { super.prepareForReuse() disposeBag.removeAll() + observations.removeAll() } // MARK: - Methods diff --git a/Mastodon/Service/SettingService.swift b/Mastodon/Service/SettingService.swift index 1c030c519..79ed47abf 100644 --- a/Mastodon/Service/SettingService.swift +++ b/Mastodon/Service/SettingService.swift @@ -54,8 +54,7 @@ final class SettingService { into: managedObjectContext, property: Setting.Property( domain: domain, - userID: userID, - appearanceRaw: SettingsItem.AppearanceMode.automatic.rawValue + userID: userID ) ) } // end for @@ -190,16 +189,16 @@ extension SettingService { static func updatePreference(setting: Setting) { // set appearance - let userInterfaceStyle: UIUserInterfaceStyle = { - switch setting.appearance { - case .automatic: return .unspecified - case .light: return .light - case .dark: return .dark - } - }() - if UserDefaults.shared.customUserInterfaceStyle != userInterfaceStyle { - UserDefaults.shared.customUserInterfaceStyle = userInterfaceStyle - } +// let userInterfaceStyle: UIUserInterfaceStyle = { +// switch setting.appearance { +// case .automatic: return .unspecified +// case .light: return .light +// case .dark: return .dark +// } +// }() +// if UserDefaults.shared.customUserInterfaceStyle != userInterfaceStyle { +// UserDefaults.shared.customUserInterfaceStyle = userInterfaceStyle +// } // set theme let themeName: ThemeName = setting.preferredTrueBlackDarkMode ? .system : .mastodon From 58daa930cb8f364ebedffef93b07cd0496c7d2ce Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 8 Oct 2021 19:05:28 +0800 Subject: [PATCH 14/95] feat: set status content direction if possible. resolve #295 --- Mastodon/Diffiable/Section/Status/StatusSection.swift | 9 +++++++++ Mastodon/Scene/Share/View/Content/StatusView.swift | 1 + 2 files changed, 10 insertions(+) diff --git a/Mastodon/Diffiable/Section/Status/StatusSection.swift b/Mastodon/Diffiable/Section/Status/StatusSection.swift index fe95c4c75..682422927 100644 --- a/Mastodon/Diffiable/Section/Status/StatusSection.swift +++ b/Mastodon/Diffiable/Section/Status/StatusSection.swift @@ -720,6 +720,15 @@ extension StatusSection { statusItemAttribute: Item.StatusAttribute ) { // set content + let paragraphStyle = cell.statusView.contentMetaText.paragraphStyle + if let language = (status.reblog ?? status).language { + let direction = Locale.characterDirection(forLanguage: language) + paragraphStyle.alignment = direction == .rightToLeft ? .right : .left + } else { + paragraphStyle.alignment = .natural + } + cell.statusView.contentMetaText.paragraphStyle = paragraphStyle + if let content = content { cell.statusView.contentMetaText.configure(content: content) cell.statusView.contentMetaText.textView.accessibilityLabel = content.trimmed diff --git a/Mastodon/Scene/Share/View/Content/StatusView.swift b/Mastodon/Scene/Share/View/Content/StatusView.swift index 7afabd3a9..9a6907d1c 100644 --- a/Mastodon/Scene/Share/View/Content/StatusView.swift +++ b/Mastodon/Scene/Share/View/Content/StatusView.swift @@ -217,6 +217,7 @@ final class StatusView: UIView { let style = NSMutableParagraphStyle() style.lineSpacing = 5 style.paragraphSpacing = 8 + style.alignment = .natural return style }() metaText.textAttributes = [ From 2ef55bc5ca8db715bd7119d4cc1e5552974ef7d7 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 8 Oct 2021 19:09:34 +0800 Subject: [PATCH 15/95] chore: update version to 1.2.0 (72) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 4 +- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 12 files changed, 47 insertions(+), 47 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 4c76ebcf8..d332d8527 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 4c76ebcf8..d332d8527 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 4c76ebcf8..d332d8527 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 07a833da7..674821cdb 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4760,7 +4760,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4789,7 +4789,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4897,11 +4897,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4928,11 +4928,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4957,11 +4957,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4987,11 +4987,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5054,7 +5054,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5079,7 +5079,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5104,7 +5104,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5129,7 +5129,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5154,7 +5154,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5179,7 +5179,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5204,7 +5204,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5229,7 +5229,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5320,7 +5320,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5387,11 +5387,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5436,7 +5436,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5461,11 +5461,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5557,7 +5557,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5624,11 +5624,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5673,7 +5673,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5698,11 +5698,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 71; + DYLIB_CURRENT_VERSION = 72; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5728,7 +5728,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5752,7 +5752,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 71; + CURRENT_PROJECT_VERSION = 72; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index e6093a218..b0f597fa2 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 35 + 44 CoreDataStack.xcscheme_^#shared#^_ orderHint - 38 + 42 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 36 + 41 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 37 + 43 SuppressBuildableAutocreation diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1fe981b44..e118002a8 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -141,8 +141,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "76dd4b49110b8624317fc128e7fa0d8a252018bc", - "version": "5.11.1" + "revision": "d6367439527663d2038ca445a3c3c4e4bac40d60", + "version": "5.12.0" } }, { diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index c144fc6e7..601a346dd 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 71 + 72 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index e4bdca72f..cfbcd88df 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 4c76ebcf8..d332d8527 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 4c76ebcf8..d332d8527 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index ddb99a597..f5175f013 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 08ddef3f3..03a3f83e5 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 71 + 72 NSExtension NSExtensionAttributes From 714efa852c5d46e8c4f8cade1cff69641e499a86 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 8 Oct 2021 17:46:55 +0100 Subject: [PATCH 16/95] New translations Intents.stringsdict (Arabic) --- .../Intents/input/ar_SA/Intents.stringsdict | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.stringsdict index f273a551d..e44e666ae 100644 --- a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.stringsdict +++ b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.stringsdict @@ -5,7 +5,7 @@ There are ${count} options matching ‘${content}’. - 2 NSStringLocalizedFormatKey - There are %#@count_option@ matching ‘${content}’. + هُناك %#@count_option@ تتطابق مَعَ '${content}'. count_option NSStringFormatSpecTypeKey @@ -13,23 +13,23 @@ NSStringFormatValueTypeKey %ld zero - %ld options + لا خيار one - 1 option + خيار واحد two - %ld options + خياران few - %ld options + %ld خيارات many - %ld options + %ld خيارًا other - %ld options + %ld خيار There are ${count} options matching ‘${visibility}’. NSStringLocalizedFormatKey - There are %#@count_option@ matching ‘${visibility}’. + هُناك %#@count_option@ تتطابق مَعَ '${visibility}'. count_option NSStringFormatSpecTypeKey @@ -37,17 +37,17 @@ NSStringFormatValueTypeKey %ld zero - %ld options + لا خيار one - 1 option + خيار واحد two - %ld options + خياران few - %ld options + %ld خيارات many - %ld options + %ld خيارًا other - %ld options + %ld خيار From 7113fc037ca349a88527ea3b3776cf75ed56a54f Mon Sep 17 00:00:00 2001 From: CMK Date: Sat, 9 Oct 2021 16:16:11 +0800 Subject: [PATCH 17/95] fix: reply crate duplicate mention issue. resolve #293 --- Mastodon/Scene/Compose/ComposeViewModel.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mastodon/Scene/Compose/ComposeViewModel.swift b/Mastodon/Scene/Compose/ComposeViewModel.swift index f91565d38..d61be5fdd 100644 --- a/Mastodon/Scene/Compose/ComposeViewModel.swift +++ b/Mastodon/Scene/Compose/ComposeViewModel.swift @@ -151,7 +151,9 @@ final class ComposeViewModel: NSObject { .sorted(by: { $0.index.intValue < $1.index.intValue }) .filter { $0.id != composeAuthor?.id } for mention in mentions { - mentionAccts.append("@" + mention.acct) + let acct = "@" + mention.acct + guard !mentionAccts.contains(acct) else { continue } + mentionAccts.append(acct) } for acct in mentionAccts { UITextChecker.learnWord(acct) From 1eb981258803e52cba72f50cf302fe6e0e55c5b1 Mon Sep 17 00:00:00 2001 From: CMK Date: Sat, 9 Oct 2021 19:01:08 +0800 Subject: [PATCH 18/95] feat: dynamic set compose post character limit. resolve #222 --- AppShared/UserDefaults+Notification.swift | 40 +++++++ AppShared/UserDefaults.swift | 31 ------ .../CoreData 2.xcdatamodel/contents | 11 +- CoreDataStack/Entity/Instance.swift | 70 ++++++++++++ .../Entity/MastodonAuthentication.swift | 11 +- Mastodon.xcodeproj/project.pbxproj | 24 +++- .../xcschemes/xcschememanagement.plist | 14 +-- .../Extension/CoreDataStack/Instance.swift | 25 +++++ .../NSDiffableDataSourceSnapshot.swift | 24 ---- .../Scene/Compose/ComposeViewController.swift | 6 +- .../Compose/ComposeViewModel+DataSource.swift | 11 +- Mastodon/Scene/Compose/ComposeViewModel.swift | 36 ++++-- .../APIService+CoreData+Instance.swift | 76 +++++++++++++ Mastodon/Service/InstanceService.swift | 103 ++++++++++++++++++ Mastodon/State/AppContext.swift | 6 + .../Entity/Mastodon+Entity+Instance.swift | 65 +++++++++++ 16 files changed, 465 insertions(+), 88 deletions(-) create mode 100644 AppShared/UserDefaults+Notification.swift create mode 100644 CoreDataStack/Entity/Instance.swift create mode 100644 Mastodon/Extension/CoreDataStack/Instance.swift delete mode 100644 Mastodon/Extension/NSDiffableDataSourceSnapshot.swift create mode 100644 Mastodon/Service/APIService/CoreData/APIService+CoreData+Instance.swift create mode 100644 Mastodon/Service/InstanceService.swift diff --git a/AppShared/UserDefaults+Notification.swift b/AppShared/UserDefaults+Notification.swift new file mode 100644 index 000000000..e743e70a0 --- /dev/null +++ b/AppShared/UserDefaults+Notification.swift @@ -0,0 +1,40 @@ +// +// UserDefaults+Notification.swift +// AppShared +// +// Created by Cirno MainasuK on 2021-10-9. +// + +import UIKit +import CryptoKit + +extension UserDefaults { + // always use hash value (SHA256) from accessToken as key + private static func deriveKey(from accessToken: String, prefix: String) -> String { + let digest = SHA256.hash(data: Data(accessToken.utf8)) + let bytes = [UInt8](digest) + let hex = bytes.toHexString() + let key = prefix + "@" + hex + return key + } + + private static let notificationCountKeyPrefix = "notification_count" + + public func getNotificationCountWithAccessToken(accessToken: String) -> Int { + let prefix = UserDefaults.notificationCountKeyPrefix + let key = UserDefaults.deriveKey(from: accessToken, prefix: prefix) + return integer(forKey: key) + } + + public func setNotificationCountWithAccessToken(accessToken: String, value: Int) { + let prefix = UserDefaults.notificationCountKeyPrefix + let key = UserDefaults.deriveKey(from: accessToken, prefix: prefix) + setValue(value, forKey: key) + } + + public func increaseNotificationCount(accessToken: String) { + let count = getNotificationCountWithAccessToken(accessToken: accessToken) + setNotificationCountWithAccessToken(accessToken: accessToken, value: count + 1) + } + +} diff --git a/AppShared/UserDefaults.swift b/AppShared/UserDefaults.swift index 67a3cf685..753a3284f 100644 --- a/AppShared/UserDefaults.swift +++ b/AppShared/UserDefaults.swift @@ -6,39 +6,8 @@ // import UIKit -import CryptoKit extension UserDefaults { public static let shared = UserDefaults(suiteName: AppName.groupID)! } -extension UserDefaults { - // always use hash value (SHA256) from accessToken as key - private static func deriveKey(from accessToken: String, prefix: String) -> String { - let digest = SHA256.hash(data: Data(accessToken.utf8)) - let bytes = [UInt8](digest) - let hex = bytes.toHexString() - let key = prefix + "@" + hex - return key - } - - private static let notificationCountKeyPrefix = "notification_count" - - public func getNotificationCountWithAccessToken(accessToken: String) -> Int { - let prefix = UserDefaults.notificationCountKeyPrefix - let key = UserDefaults.deriveKey(from: accessToken, prefix: prefix) - return integer(forKey: key) - } - - public func setNotificationCountWithAccessToken(accessToken: String, value: Int) { - let prefix = UserDefaults.notificationCountKeyPrefix - let key = UserDefaults.deriveKey(from: accessToken, prefix: prefix) - setValue(value, forKey: key) - } - - public func increaseNotificationCount(accessToken: String) { - let count = getNotificationCountWithAccessToken(accessToken: accessToken) - setNotificationCountWithAccessToken(accessToken: accessToken, value: count + 1) - } - -} diff --git a/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents b/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents index c26a0bdbb..6d576ca15 100644 --- a/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents +++ b/CoreDataStack/CoreData.xcdatamodeld/CoreData 2.xcdatamodel/contents @@ -63,6 +63,13 @@ + + + + + + + @@ -75,6 +82,7 @@ + @@ -280,7 +288,7 @@ - + @@ -293,5 +301,6 @@ + \ No newline at end of file diff --git a/CoreDataStack/Entity/Instance.swift b/CoreDataStack/Entity/Instance.swift new file mode 100644 index 000000000..8976097ef --- /dev/null +++ b/CoreDataStack/Entity/Instance.swift @@ -0,0 +1,70 @@ +// +// Instance.swift +// CoreDataStack +// +// Created by Cirno MainasuK on 2021-10-9. +// + +import Foundation +import CoreData + +public final class Instance: NSManagedObject { + @NSManaged public var domain: String + + @NSManaged public private(set) var createdAt: Date + @NSManaged public private(set) var updatedAt: Date + + @NSManaged public private(set) var configurationRaw: Data? + + // MARK: one-to-many relationships + @NSManaged public var authentications: Set +} + +extension Instance { + public override func awakeFromInsert() { + super.awakeFromInsert() + let now = Date() + setPrimitiveValue(now, forKey: #keyPath(Instance.createdAt)) + setPrimitiveValue(now, forKey: #keyPath(Instance.updatedAt)) + } + + @discardableResult + public static func insert( + into context: NSManagedObjectContext, + property: Property + ) -> Instance { + let instance: Instance = context.insertObject() + instance.domain = property.domain + return instance + } + + public func update(configurationRaw: Data?) { + self.configurationRaw = configurationRaw + } + + public func didUpdate(at networkDate: Date) { + self.updatedAt = networkDate + } +} + +extension Instance { + public struct Property { + public let domain: String + + public init(domain: String) { + self.domain = domain + } + } +} + +extension Instance: Managed { + public static var defaultSortDescriptors: [NSSortDescriptor] { + return [NSSortDescriptor(keyPath: \Instance.createdAt, ascending: false)] + } +} + +extension Instance { + public static func predicate(domain: String) -> NSPredicate { + return NSPredicate(format: "%K == %@", #keyPath(Instance.domain), domain) + } +} diff --git a/CoreDataStack/Entity/MastodonAuthentication.swift b/CoreDataStack/Entity/MastodonAuthentication.swift index 0ee0e343b..66b8ad6a9 100644 --- a/CoreDataStack/Entity/MastodonAuthentication.swift +++ b/CoreDataStack/Entity/MastodonAuthentication.swift @@ -30,6 +30,9 @@ final public class MastodonAuthentication: NSManagedObject { // one-to-one relationship @NSManaged public private(set) var user: MastodonUser + // many-to-one relationship + @NSManaged public private(set) var instance: Instance? + } extension MastodonAuthentication { @@ -97,6 +100,12 @@ extension MastodonAuthentication { } } + public func update(instance: Instance) { + if self.instance != instance { + self.instance = instance + } + } + public func didUpdate(at networkDate: Date) { self.updatedAt = networkDate } @@ -143,7 +152,7 @@ extension MastodonAuthentication: Managed { extension MastodonAuthentication { - static func predicate(domain: String) -> NSPredicate { + public static func predicate(domain: String) -> NSPredicate { return NSPredicate(format: "%K == %@", #keyPath(MastodonAuthentication.domain), domain) } diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 674821cdb..a1283ea0a 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -356,6 +356,11 @@ DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; }; DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.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 */; }; + DB73BF43271192BB00781945 /* InstanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF42271192BB00781945 /* InstanceService.swift */; }; + DB73BF45271195AC00781945 /* APIService+CoreData+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF44271195AC00781945 /* APIService+CoreData+Instance.swift */; }; + DB73BF47271199CA00781945 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF46271199CA00781945 /* Instance.swift */; }; DB75BF1E263C1C1B00EDBF1F /* CustomScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */; }; DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */; }; DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */; }; @@ -454,7 +459,6 @@ DBAC6483267D0B21007FE9FD /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = DBAC6482267D0B21007FE9FD /* DifferenceKit */; }; DBAC6485267D0F9E007FE9FD /* StatusNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC6484267D0F9E007FE9FD /* StatusNode.swift */; }; DBAC6488267D388B007FE9FD /* ASTableNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC6487267D388B007FE9FD /* ASTableNode.swift */; }; - DBAC648A267DC355007FE9FD /* NSDiffableDataSourceSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC6489267DC355007FE9FD /* NSDiffableDataSourceSnapshot.swift */; }; DBAC648F267DC84D007FE9FD /* TableNodeDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC648E267DC84D007FE9FD /* TableNodeDiffableDataSource.swift */; }; DBAC6497267DECCB007FE9FD /* TimelineMiddleLoaderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC6496267DECCB007FE9FD /* TimelineMiddleLoaderNode.swift */; }; DBAC6499267DF2C4007FE9FD /* TimelineBottomLoaderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBAC6498267DF2C4007FE9FD /* TimelineBottomLoaderNode.swift */; }; @@ -1144,6 +1148,11 @@ DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = ""; }; DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = ""; }; DB73B48F261F030A002E9E9F /* SafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariActivity.swift; sourceTree = ""; }; + DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Notification.swift"; sourceTree = ""; }; + DB73BF4027118B6D00781945 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = ""; }; + DB73BF42271192BB00781945 /* InstanceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceService.swift; sourceTree = ""; }; + DB73BF44271195AC00781945 /* APIService+CoreData+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Instance.swift"; sourceTree = ""; }; + DB73BF46271199CA00781945 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = ""; }; DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScheduler.swift; sourceTree = ""; }; DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = ""; }; DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = ""; }; @@ -1270,7 +1279,6 @@ DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeIllustrationView.swift; sourceTree = ""; }; DBAC6484267D0F9E007FE9FD /* StatusNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusNode.swift; sourceTree = ""; }; DBAC6487267D388B007FE9FD /* ASTableNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTableNode.swift; sourceTree = ""; }; - DBAC6489267DC355007FE9FD /* NSDiffableDataSourceSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSDiffableDataSourceSnapshot.swift; sourceTree = ""; }; DBAC648E267DC84D007FE9FD /* TableNodeDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableNodeDiffableDataSource.swift; sourceTree = ""; }; DBAC6496267DECCB007FE9FD /* TimelineMiddleLoaderNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMiddleLoaderNode.swift; sourceTree = ""; }; DBAC6498267DF2C4007FE9FD /* TimelineBottomLoaderNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBottomLoaderNode.swift; sourceTree = ""; }; @@ -1771,6 +1779,7 @@ DB297B1A2679FAE200704C90 /* PlaceholderImageCacheService.swift */, DBAEDE5B267A058D00D25FF5 /* BlurhashImageCacheService.swift */, DB564BD2269F3B35001E39A7 /* StatusFilterService.swift */, + DB73BF42271192BB00781945 /* InstanceService.swift */, ); path = Service; sourceTree = ""; @@ -2079,6 +2088,7 @@ DBAFB7342645463500371D5F /* Emojis.swift */, DBA94439265CC0FC00C537E1 /* Fields.swift */, DBA1DB7F268F84F80052DB59 /* NotificationType.swift */, + DB73BF46271199CA00781945 /* Instance.swift */, ); path = CoreDataStack; sourceTree = ""; @@ -2280,6 +2290,7 @@ 2D79E700261EA5550011E398 /* APIService+CoreData+Tag.swift */, DB6D9F56263577D2008423CD /* APIService+CoreData+Setting.swift */, 5B90C48A26259C120002E742 /* APIService+CoreData+Subscriptions.swift */, + DB73BF44271195AC00781945 /* APIService+CoreData+Instance.swift */, ); path = CoreData; sourceTree = ""; @@ -2454,6 +2465,7 @@ DB6804912637CD8700430867 /* AppName.swift */, DB6804FC2637CFEC00430867 /* AppSecret.swift */, DB6804D02637CE4700430867 /* UserDefaults.swift */, + DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */, ); path = AppShared; sourceTree = ""; @@ -2636,6 +2648,7 @@ 5B90C46D26259B2C0002E742 /* Setting.swift */, 5B90C46C26259B2C0002E742 /* Subscription.swift */, 5B90C47E26259BA90002E742 /* SubscriptionAlerts.swift */, + DB73BF4027118B6D00781945 /* Instance.swift */, ); path = Entity; sourceTree = ""; @@ -2716,7 +2729,6 @@ DB0E91E926A9675100BD2ACC /* MetaLabel.swift */, DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */, DB47229625F9EFAD00DA7F53 /* NSManagedObjectContext.swift */, - DBAC6489267DC355007FE9FD /* NSDiffableDataSourceSnapshot.swift */, DB0140CE25C42AEE00F9F3CF /* OSLog.swift */, 2D939AB425EDD8A90076FA61 /* String.swift */, DB68A06225E905E000CFDF14 /* UIApplication.swift */, @@ -3943,7 +3955,6 @@ DBBC24DC26A54BCB00398BB9 /* MastodonRegex.swift in Sources */, 2D69D00A25CAA00300C3A1B2 /* APIService+CoreData+Status.swift in Sources */, DB4481C625EE2ADA00BEFB67 /* PollSection.swift in Sources */, - DBAC648A267DC355007FE9FD /* NSDiffableDataSourceSnapshot.swift in Sources */, DBCBED1726132DB500B49291 /* UserTimelineViewModel+Diffable.swift in Sources */, DB71FD4C25F8C80E00512AE1 /* StatusPrefetchingService.swift in Sources */, 2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */, @@ -3993,6 +4004,7 @@ DBAE3FAF26172FC0004B8251 /* RemoteProfileViewModel.swift in Sources */, DBE3CE0D261D767100430CC6 /* FavoriteViewController+Provider.swift in Sources */, 2D084B9326259545003AA3AF /* NotificationViewModel+LoadLatestState.swift in Sources */, + DB73BF47271199CA00781945 /* Instance.swift in Sources */, DB0F8150264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift in Sources */, DB98337F25C9452D00AD9700 /* APIService+APIError.swift in Sources */, DB9E0D6F25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift in Sources */, @@ -4015,6 +4027,7 @@ 5B90C45E262599800002E742 /* SettingsViewModel.swift in Sources */, 2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */, 5DF1054725F8870E00D6C0D4 /* VideoPlayerViewModel.swift in Sources */, + DB73BF43271192BB00781945 /* InstanceService.swift in Sources */, DBA9443A265CC0FC00C537E1 /* Fields.swift in Sources */, 2DE0FAC12615F04D00CDF649 /* RecommendHashTagSection.swift in Sources */, DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */, @@ -4190,6 +4203,7 @@ DB0C946B26A700AB0088FB11 /* MastodonUser+Property.swift in Sources */, DB8AF54425C13647002E6C99 /* SceneCoordinator.swift in Sources */, 5DF1058525F88AE500D6C0D4 /* NeedsDependency+AVPlayerViewControllerDelegate.swift in Sources */, + DB73BF45271195AC00781945 /* APIService+CoreData+Instance.swift in Sources */, DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */, DB447697260B439000B66B82 /* CustomEmojiPickerHeaderCollectionReusableView.swift in Sources */, DB45FAF925CA80A2005A8AC7 /* APIService+CoreData+MastodonAuthentication.swift in Sources */, @@ -4304,6 +4318,7 @@ buildActionMask = 2147483647; files = ( DB6804D12637CE4700430867 /* UserDefaults.swift in Sources */, + DB73BF3B2711885500781945 /* UserDefaults+Notification.swift in Sources */, DB4932B726F30F0700EF46D4 /* Array.swift in Sources */, DB6804922637CD8700430867 /* AppName.swift in Sources */, DB6804FD2637CFEC00430867 /* AppSecret.swift in Sources */, @@ -4324,6 +4339,7 @@ 2D927F0E25C7E9C9004F19B8 /* History.swift in Sources */, DBCC3B9B261584A00045B23D /* PrivateNote.swift in Sources */, DB89BA3725C1145C008580ED /* CoreData.xcdatamodeld in Sources */, + DB73BF4127118B6D00781945 /* Instance.swift in Sources */, DB8AF52525C131D1002E6C99 /* MastodonUser.swift in Sources */, DB89BA1B25C1107F008580ED /* Collection.swift in Sources */, DB4481AD25EE155900BEFB67 /* Poll.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index b0f597fa2..b5ecab282 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 44 + 35 CoreDataStack.xcscheme_^#shared#^_ orderHint - 42 + 37 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -37,7 +37,7 @@ Mastodon - ca.xcscheme_^#shared#^_ orderHint - 16 + 18 Mastodon - de.xcscheme_^#shared#^_ @@ -67,7 +67,7 @@ Mastodon - jp.xcscheme_^#shared#^_ orderHint - 14 + 15 Mastodon - nl.xcscheme_^#shared#^_ @@ -87,7 +87,7 @@ Mastodon - zh_Hans.xcscheme_^#shared#^_ orderHint - 15 + 16 Mastodon.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 41 + 36 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 43 + 38 SuppressBuildableAutocreation diff --git a/Mastodon/Extension/CoreDataStack/Instance.swift b/Mastodon/Extension/CoreDataStack/Instance.swift new file mode 100644 index 000000000..6cacd9db9 --- /dev/null +++ b/Mastodon/Extension/CoreDataStack/Instance.swift @@ -0,0 +1,25 @@ +// +// Instance.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-9. +// + +import UIKit +import CoreDataStack +import MastodonSDK + +extension Instance { + var configuration: Mastodon.Entity.Instance.Configuration? { + guard let configurationRaw = configurationRaw else { return nil } + guard let configuration = try? JSONDecoder().decode(Mastodon.Entity.Instance.Configuration.self, from: configurationRaw) else { + return nil + } + + return configuration + } + + static func encode(configuration: Mastodon.Entity.Instance.Configuration) -> Data? { + return try? JSONEncoder().encode(configuration) + } +} diff --git a/Mastodon/Extension/NSDiffableDataSourceSnapshot.swift b/Mastodon/Extension/NSDiffableDataSourceSnapshot.swift deleted file mode 100644 index c2ff341d9..000000000 --- a/Mastodon/Extension/NSDiffableDataSourceSnapshot.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// NSDiffableDataSourceSnapshot.swift -// Mastodon -// -// Created by Cirno MainasuK on 2021-6-19. -// - -import UIKit - -//extension NSDiffableDataSourceSnapshot { -// func itemIdentifier(for indexPath: IndexPath) -> ItemIdentifierType? { -// guard 0..() @@ -38,6 +37,24 @@ final class ComposeViewModel: NSObject { var isViewAppeared = false // output + let instanceConfiguration: Mastodon.Entity.Instance.Configuration? + var composeContentLimit: Int { + guard let maxCharacters = instanceConfiguration?.statuses?.maxCharacters else { return 500 } + return max(1, maxCharacters) + } + var maxMediaAttachments: Int { + guard let maxMediaAttachments = instanceConfiguration?.statuses?.maxMediaAttachments else { + return 4 + } + // FIXME: update timeline media preview UI + return min(4, max(1, maxMediaAttachments)) + // return max(1, maxMediaAttachments) + } + var maxPollOptions: Int { + guard let maxOptions = instanceConfiguration?.polls?.maxOptions else { return 4 } + return max(2, maxOptions) + } + let composeStatusContentTableViewCell = ComposeStatusContentTableViewCell() let composeStatusAttachmentTableViewCell = ComposeStatusAttachmentTableViewCell() let composeStatusPollTableViewCell = ComposeStatusPollTableViewCell() @@ -128,8 +145,12 @@ final class ComposeViewModel: NSObject { } return CurrentValueSubject(visibility) }() - self.activeAuthentication = CurrentValueSubject(context.authenticationService.activeMastodonAuthentication.value) + let _activeAuthentication = context.authenticationService.activeMastodonAuthentication.value + self.activeAuthentication = CurrentValueSubject(_activeAuthentication) self.activeAuthenticationBox = CurrentValueSubject(context.authenticationService.activeMastodonAuthenticationBox.value) + // set limit + let _instanceConfiguration = _activeAuthentication?.instance?.configuration + self.instanceConfiguration = _instanceConfiguration super.init() // end init @@ -243,8 +264,9 @@ final class ComposeViewModel: NSObject { let isComposeContentEmpty = composeStatusAttribute.composeContent .map { ($0 ?? "").isEmpty } let isComposeContentValid = characterCount - .map { characterCount -> Bool in - return characterCount <= ComposeViewModel.composeContentLimit + .compactMap { [weak self] characterCount -> Bool in + guard let self = self else { return characterCount <= 500 } + return characterCount <= self.composeContentLimit } let isMediaEmpty = attachmentServices .map { $0.isEmpty } @@ -381,7 +403,7 @@ final class ComposeViewModel: NSObject { .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] isPollComposing, attachmentServices in guard let self = self else { return } - let shouldMediaDisable = isPollComposing || attachmentServices.count >= 4 + let shouldMediaDisable = isPollComposing || attachmentServices.count >= self.maxMediaAttachments let shouldPollDisable = attachmentServices.count > 0 self.isMediaToolbarButtonEnabled.value = !shouldMediaDisable @@ -455,7 +477,7 @@ extension ComposeViewModel { extension ComposeViewModel { func createNewPollOptionIfPossible() { - guard pollOptionAttributes.value.count < 4 else { return } + guard pollOptionAttributes.value.count < maxPollOptions else { return } let attribute = ComposeStatusPollItem.PollOptionAttribute() pollOptionAttributes.value = pollOptionAttributes.value + [attribute] @@ -488,7 +510,7 @@ extension ComposeViewModel { // check exclusive limit: // - up to 1 video - // - up to 4 photos + // - up to N photos func checkAttachmentPrecondition() throws { let attachmentServices = self.attachmentServices.value guard !attachmentServices.isEmpty else { return } diff --git a/Mastodon/Service/APIService/CoreData/APIService+CoreData+Instance.swift b/Mastodon/Service/APIService/CoreData/APIService+CoreData+Instance.swift new file mode 100644 index 000000000..614d098aa --- /dev/null +++ b/Mastodon/Service/APIService/CoreData/APIService+CoreData+Instance.swift @@ -0,0 +1,76 @@ +// +// APIService+CoreData+Instance.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-9. +// + +import os.log +import Foundation +import CoreData +import CoreDataStack +import MastodonSDK + +extension APIService.CoreData { + + static func createOrMergeInstance( + into managedObjectContext: NSManagedObjectContext, + domain: String, + entity: Mastodon.Entity.Instance, + networkDate: Date, + log: OSLog + ) -> (instance: Instance, isCreated: Bool) { + // fetch old mastodon user + let old: Instance? = { + let request = Instance.sortedFetchRequest + request.predicate = Instance.predicate(domain: domain) + request.fetchLimit = 1 + request.returnsObjectsAsFaults = false + do { + return try managedObjectContext.fetch(request).first + } catch { + assertionFailure(error.localizedDescription) + return nil + } + }() + + if let old = old { + // merge old + APIService.CoreData.merge( + instance: old, + entity: entity, + domain: domain, + networkDate: networkDate + ) + return (old, false) + } else { + let instance = Instance.insert( + into: managedObjectContext, + property: Instance.Property(domain: domain) + ) + let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) } + instance.update(configurationRaw: configurationRaw) + + return (instance, true) + } + } + +} + +extension APIService.CoreData { + + static func merge( + instance: Instance, + entity: Mastodon.Entity.Instance, + domain: String, + networkDate: Date + ) { + guard networkDate > instance.updatedAt else { return } + + let configurationRaw = entity.configuration.flatMap { Instance.encode(configuration: $0) } + instance.update(configurationRaw: configurationRaw) + + instance.didUpdate(at: networkDate) + } + +} diff --git a/Mastodon/Service/InstanceService.swift b/Mastodon/Service/InstanceService.swift new file mode 100644 index 000000000..4fb6309fd --- /dev/null +++ b/Mastodon/Service/InstanceService.swift @@ -0,0 +1,103 @@ +// +// InstanceService.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-9. +// + +import os.log +import Foundation +import Combine +import CoreData +import CoreDataStack +import MastodonSDK + +final class InstanceService { + + var disposeBag = Set() + + let logger = Logger(subsystem: "InstanceService", category: "Logic") + + // input + let backgroundManagedObjectContext: NSManagedObjectContext + weak var apiService: APIService? + weak var authenticationService: AuthenticationService? + + // output + + init( + apiService: APIService, + authenticationService: AuthenticationService + ) { + self.backgroundManagedObjectContext = apiService.backgroundManagedObjectContext + self.apiService = apiService + self.authenticationService = authenticationService + + authenticationService.activeMastodonAuthenticationBox + .receive(on: DispatchQueue.main) + .compactMap { $0?.domain } + .removeDuplicates() // prevent infinity loop + .sink { [weak self] domain in + guard let self = self else { return } + self.updateInstance(domain: domain) + } + .store(in: &disposeBag) + } + +} + +extension InstanceService { + func updateInstance(domain: String) { + guard let apiService = self.apiService else { return } + apiService.instance(domain: domain) + .flatMap { response -> AnyPublisher, Error> in + let managedObjectContext = self.backgroundManagedObjectContext + return managedObjectContext.performChanges { + // get instance + let (instance, _) = APIService.CoreData.createOrMergeInstance( + into: managedObjectContext, + domain: domain, + entity: response.value, + networkDate: response.networkDate, + log: OSLog.api + ) + + // update relationship + let request = MastodonAuthentication.sortedFetchRequest + request.predicate = MastodonAuthentication.predicate(domain: domain) + request.returnsObjectsAsFaults = false + do { + let authentications = try managedObjectContext.fetch(request) + for authentication in authentications { + authentication.update(instance: instance) + } + } catch { + assertionFailure(error.localizedDescription) + } + } + .setFailureType(to: Error.self) + .tryMap { result -> Mastodon.Response.Content in + switch result { + case .success: + return response + case .failure(let error): + throw error + } + } + .eraseToAnyPublisher() + } + .sink { [weak self] completion in + guard let self = self else { return } + switch completion { + case .failure(let error): + self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Instance] update instance failure: \(error.localizedDescription)") + case .finished: + self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): [Instance] update instance for domain: \(domain)") + } + } receiveValue: { [weak self] response in + guard let self = self else { return } + // do nothing + } + .store(in: &disposeBag) + } +} diff --git a/Mastodon/State/AppContext.swift b/Mastodon/State/AppContext.swift index d4682ed5e..d7c08d47f 100644 --- a/Mastodon/State/AppContext.swift +++ b/Mastodon/State/AppContext.swift @@ -31,6 +31,7 @@ class AppContext: ObservableObject { let statusPublishService = StatusPublishService() let notificationService: NotificationService let settingService: SettingService + let instanceService: InstanceService let blockDomainService: BlockDomainService let statusFilterService: StatusFilterService @@ -87,6 +88,11 @@ class AppContext: ObservableObject { notificationService: _notificationService ) + instanceService = InstanceService( + apiService: _apiService, + authenticationService: _authenticationService + ) + blockDomainService = BlockDomainService( backgroundManagedObjectContext: _backgroundManagedObjectContext, authenticationService: _authenticationService diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift index 226af40f8..d0d16ee4a 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Instance.swift @@ -34,6 +34,9 @@ extension Mastodon.Entity { public let thumbnail: String? public let contactAccount: Account? public let rules: [Rule]? + + // https://github.com/mastodon/mastodon/pull/16485 + public let configuration: Configuration? enum CodingKeys: String, CodingKey { case uri @@ -52,6 +55,8 @@ extension Mastodon.Entity { case thumbnail case contactAccount = "contact_account" case rules + + case configuration } } } @@ -86,3 +91,63 @@ extension Mastodon.Entity.Instance { public let text: String } } + +extension Mastodon.Entity.Instance { + public struct Configuration: Codable { + public let statuses: Statuses? + public let mediaAttachments: MediaAttachments? + public let polls: Polls? + + enum CodingKeys: String, CodingKey { + case statuses + case mediaAttachments = "media_attachments" + case polls + } + } +} + +extension Mastodon.Entity.Instance.Configuration { + public struct Statuses: Codable { + public let maxCharacters: Int + public let maxMediaAttachments: Int + public let charactersReservedPerURL: Int + + enum CodingKeys: String, CodingKey { + case maxCharacters = "max_characters" + case maxMediaAttachments = "max_media_attachments" + case charactersReservedPerURL = "characters_reserved_per_url" + } + } + + public struct MediaAttachments: Codable { + public let supportedMIMETypes: [String] + public let imageSizeLimit: Int + public let imageMatrixLimit: Int + public let videoSizeLimit: Int + public let videoFrameRateLimit: Int + public let videoMatrixLimit: Int + + enum CodingKeys: String, CodingKey { + case supportedMIMETypes = "supported_mime_types" + case imageSizeLimit = "image_size_limit" + case imageMatrixLimit = "image_matrix_limit" + case videoSizeLimit = "video_size_limit" + case videoFrameRateLimit = "video_frame_rate_limit" + case videoMatrixLimit = "video_matrix_limit" + } + } + + public struct Polls: Codable { + public let maxOptions: Int + public let maxCharactersPerOption: Int + public let minExpiration: Int + public let maxExpiration: Int + + enum CodingKeys: String, CodingKey { + case maxOptions = "max_options" + case maxCharactersPerOption = "max_characters_per_option" + case minExpiration = "min_expiration" + case maxExpiration = "max_expiration" + } + } +} From 1bcf4cfd2fc45d3377f0bc58c504e6d5e390e73e Mon Sep 17 00:00:00 2001 From: CMK Date: Sat, 9 Oct 2021 19:15:04 +0800 Subject: [PATCH 19/95] chore: update i18n assets --- Mastodon/Resources/ar.lproj/InfoPlist.strings | 4 +- .../Resources/ar.lproj/Localizable.strings | 98 +++--- .../ar.lproj/Localizable.stringsdict | 286 +++++++++--------- .../Resources/de.lproj/Localizable.strings | 6 +- .../de.lproj/Localizable.stringsdict | 4 +- .../Resources/th.lproj/Localizable.strings | 12 +- .../th.lproj/Localizable.stringsdict | 2 +- 7 files changed, 206 insertions(+), 206 deletions(-) diff --git a/Mastodon/Resources/ar.lproj/InfoPlist.strings b/Mastodon/Resources/ar.lproj/InfoPlist.strings index 5ced1e74f..c3b26f14a 100644 --- a/Mastodon/Resources/ar.lproj/InfoPlist.strings +++ b/Mastodon/Resources/ar.lproj/InfoPlist.strings @@ -1,4 +1,4 @@ -"NSCameraUsageDescription" = "Used to take photo for post status"; -"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library"; +"NSCameraUsageDescription" = "يُستخدم لالتقاط الصورة عِندَ نشر الحالات"; +"NSPhotoLibraryAddUsageDescription" = "يُستخدم لحِفظ الصورة في مكتبة الصور"; "NewPostShortcutItemTitle" = "منشور جديد"; "SearchShortcutItemTitle" = "البحث"; \ No newline at end of file diff --git a/Mastodon/Resources/ar.lproj/Localizable.strings b/Mastodon/Resources/ar.lproj/Localizable.strings index 3dfe057ed..98d8c09cb 100644 --- a/Mastodon/Resources/ar.lproj/Localizable.strings +++ b/Mastodon/Resources/ar.lproj/Localizable.strings @@ -1,14 +1,14 @@ "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.CleanCache.Message" = "تم تنظيف ذاكرة التخزين المؤقت %@ بنجاح."; -"Common.Alerts.CleanCache.Title" = "تنظيف ذاكرة التخزين المؤقت"; -"Common.Alerts.Common.PleaseTryAgain" = "الرجاء المحاولة مرة أخرى."; -"Common.Alerts.Common.PleaseTryAgainLater" = "الرجاء المحاولة مرة أخرى لاحقاً."; +"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.EditProfileFailure.Message" = "لا يمكن تعديل الملف الشخصي. الرجاء المحاولة مرة أخرى."; +"Common.Alerts.EditProfileFailure.Message" = "لا يمكن تعديل الملف الشخصي. يُرجى المحاولة مرة أُخرى."; "Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error"; "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."; @@ -33,7 +33,7 @@ Please check your internet connection."; "Common.Controls.Actions.CopyPhoto" = "نسخ الصورة"; "Common.Controls.Actions.Delete" = "احذف"; "Common.Controls.Actions.Discard" = "تجاهل"; -"Common.Controls.Actions.Done" = "تم"; +"Common.Controls.Actions.Done" = "تمّ"; "Common.Controls.Actions.Edit" = "تعديل"; "Common.Controls.Actions.FindPeople" = "ابحث عن أشخاص لمتابعتهم"; "Common.Controls.Actions.ManuallySearch" = "البحث يدوياً بدلاً من ذلك"; @@ -53,11 +53,11 @@ Please check your internet connection."; "Common.Controls.Actions.Share" = "شارك"; "Common.Controls.Actions.SharePost" = "شارك المنشور"; "Common.Controls.Actions.ShareUser" = "شارك %@"; -"Common.Controls.Actions.SignIn" = "لِج"; -"Common.Controls.Actions.SignUp" = "انشئ حسابًا"; +"Common.Controls.Actions.SignIn" = "تسجيل الدخول"; +"Common.Controls.Actions.SignUp" = "إنشاء حِساب"; "Common.Controls.Actions.Skip" = "تخطي"; "Common.Controls.Actions.TakePhoto" = "التقط صورة"; -"Common.Controls.Actions.TryAgain" = "حاول مرة أخرى"; +"Common.Controls.Actions.TryAgain" = "المُحاولة مرة أُخرى"; "Common.Controls.Actions.UnblockDomain" = "إلغاء حظر %@"; "Common.Controls.Friendship.Block" = "حظر"; "Common.Controls.Friendship.BlockDomain" = "حظر %@"; @@ -69,8 +69,8 @@ Please check your internet connection."; "Common.Controls.Friendship.Mute" = "أكتم"; "Common.Controls.Friendship.MuteUser" = "أكتم %@"; "Common.Controls.Friendship.Muted" = "مكتوم"; -"Common.Controls.Friendship.Pending" = "Pending"; -"Common.Controls.Friendship.Request" = "Request"; +"Common.Controls.Friendship.Pending" = "قيد المُراجعة"; +"Common.Controls.Friendship.Request" = "إرسال طَلَب"; "Common.Controls.Friendship.Unblock" = "إلغاء الحَظر"; "Common.Controls.Friendship.UnblockUser" = "إلغاء حظر %@"; "Common.Controls.Friendship.Unmute" = "إلغاء الكتم"; @@ -109,13 +109,13 @@ Please check your internet connection."; "Common.Controls.Status.Tag.Link" = "الرابط"; "Common.Controls.Status.Tag.Mention" = "أشر إلى"; "Common.Controls.Status.Tag.Url" = "عنوان URL"; -"Common.Controls.Status.UserReblogged" = "%@ reblogged"; +"Common.Controls.Status.UserReblogged" = "أعادَ %@ تدوينها"; "Common.Controls.Status.UserRepliedTo" = "رد على %@"; "Common.Controls.Tabs.Home" = "الخيط الرئيسي"; "Common.Controls.Tabs.Notification" = "الإشعارات"; "Common.Controls.Tabs.Profile" = "الملف التعريفي"; "Common.Controls.Tabs.Search" = "بحث"; -"Common.Controls.Timeline.Filtered" = "Filtered"; +"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 @@ -129,14 +129,14 @@ until they unblock you."; until you unblock them. Your profile looks like this to them."; "Common.Controls.Timeline.Header.UserSuspendedWarning" = "%@’s account has been suspended."; -"Common.Controls.Timeline.Loader.LoadMissingPosts" = "Load missing posts"; +"Common.Controls.Timeline.Loader.LoadMissingPosts" = "تحميل المنشورات المَفقودة"; "Common.Controls.Timeline.Loader.LoadingMissingPosts" = "تحميل المزيد من المنشورات..."; "Common.Controls.Timeline.Loader.ShowMoreReplies" = "إظهار المزيد من الردود"; "Common.Controls.Timeline.Timestamp.Now" = "الأن"; -"Scene.AccountList.AddAccount" = "Add Account"; -"Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; +"Scene.AccountList.AddAccount" = "إضافة حساب"; +"Scene.AccountList.DismissAccountSwitcher" = "تجاهُل مبدِّل الحساب"; "Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; -"Scene.Compose.Accessibility.AppendAttachment" = "Add Attachment"; +"Scene.Compose.Accessibility.AppendAttachment" = "إضافة مُرفَق"; "Scene.Compose.Accessibility.AppendPoll" = "اضافة استطلاع رأي"; "Scene.Compose.Accessibility.CustomEmojiPicker" = "منتقي مخصص للإيموجي"; "Scene.Compose.Accessibility.DisableContentWarning" = "تعطيل تحذير الحتوى"; @@ -151,14 +151,14 @@ uploaded to Mastodon."; "Scene.Compose.Attachment.Video" = "فيديو"; "Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add"; "Scene.Compose.ComposeAction" = "انشر"; -"Scene.Compose.ContentInputPlaceholder" = "ما الذي يجول ببالك"; +"Scene.Compose.ContentInputPlaceholder" = "أخبِرنا بِما يَجُولُ فِي ذِهنَك"; "Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here..."; -"Scene.Compose.Keyboard.AppendAttachmentEntry" = "Add Attachment - %@"; -"Scene.Compose.Keyboard.DiscardPost" = "Discard Post"; -"Scene.Compose.Keyboard.PublishPost" = "Publish Post"; +"Scene.Compose.Keyboard.AppendAttachmentEntry" = "إضافة مُرفَق - %@"; +"Scene.Compose.Keyboard.DiscardPost" = "تجاهُل المنشور"; +"Scene.Compose.Keyboard.PublishPost" = "نَشر المَنشُور"; "Scene.Compose.Keyboard.SelectVisibilityEntry" = "اختر مدى الظهور - %@"; -"Scene.Compose.Keyboard.ToggleContentWarning" = "Toggle Content Warning"; -"Scene.Compose.Keyboard.TogglePoll" = "Toggle Poll"; +"Scene.Compose.Keyboard.ToggleContentWarning" = "تبديل تحذير المُحتوى"; +"Scene.Compose.Keyboard.TogglePoll" = "تبديل الاستطلاع"; "Scene.Compose.MediaSelection.Browse" = "تصفح"; "Scene.Compose.MediaSelection.Camera" = "التقط صورة"; "Scene.Compose.MediaSelection.PhotoLibrary" = "مكتبة الصور"; @@ -180,23 +180,23 @@ uploaded to Mastodon."; "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.ResendEmail" = "Resend Email"; +"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.Mail" = "البريد"; -"Scene.ConfirmEmail.OpenEmailApp.OpenEmailClient" = "Open Email Client"; -"Scene.ConfirmEmail.OpenEmailApp.Title" = "Check your inbox."; +"Scene.ConfirmEmail.OpenEmailApp.OpenEmailClient" = "فتح عميل البريد الإلكتروني"; +"Scene.ConfirmEmail.OpenEmailApp.Title" = "تحقَّق من بريدك الوارِد."; "Scene.ConfirmEmail.Subtitle" = "لقد أرسلنا للتو رسالة بريد إلكتروني إلى %@، اضغط على الرابط لتأكيد حسابك."; "Scene.ConfirmEmail.Title" = "شيء واحد أخير."; "Scene.Favorite.Title" = "مفضلتك"; -"Scene.HomeTimeline.NavigationBarState.NewPosts" = "See new posts"; +"Scene.HomeTimeline.NavigationBarState.NewPosts" = "إظهار منشورات جديدة"; "Scene.HomeTimeline.NavigationBarState.Offline" = "غير متصل"; "Scene.HomeTimeline.NavigationBarState.Published" = "تم نشره!"; "Scene.HomeTimeline.NavigationBarState.Publishing" = "جارٍ نشر المشاركة…"; "Scene.HomeTimeline.Title" = "الخيط الرئيسي"; "Scene.Notification.Keyobard.ShowEverything" = "إظهار كل شيء"; -"Scene.Notification.Keyobard.ShowMentions" = "Show Mentions"; +"Scene.Notification.Keyobard.ShowMentions" = "إظهار الإشارات"; "Scene.Notification.Title.Everything" = "الكل"; "Scene.Notification.Title.Mentions" = "الإشارات"; "Scene.Notification.UserFavorited Your Post" = "أضاف %@ منشورك إلى مفضلته"; @@ -204,7 +204,7 @@ uploaded to Mastodon."; "Scene.Notification.UserMentionedYou" = "أشار إليك %@"; "Scene.Notification.UserRebloggedYourPost" = "أعاد %@ تدوين مشاركتك"; "Scene.Notification.UserRequestedToFollowYou" = "طلب %@ متابعتك"; -"Scene.Notification.UserYourPollHasEnded" = "%@ Your poll has ended"; +"Scene.Notification.UserYourPollHasEnded" = "%@ اِنتهى استطلاعُكَ للرأي"; "Scene.Preview.Keyboard.ClosePreview" = "إغلاق المعاينة"; "Scene.Preview.Keyboard.ShowNext" = "إظهار التالي"; "Scene.Preview.Keyboard.ShowPrevious" = "إظهار السابق"; @@ -213,7 +213,7 @@ uploaded to Mastodon."; "Scene.Profile.Dashboard.Posts" = "منشورات"; "Scene.Profile.Fields.AddRow" = "إضافة صف"; "Scene.Profile.Fields.Placeholder.Content" = "المحتوى"; -"Scene.Profile.Fields.Placeholder.Label" = "Label"; +"Scene.Profile.Fields.Placeholder.Label" = "التسمية"; "Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Message" = "Confirm to unblock %@"; "Scene.Profile.RelationshipActionAlert.ConfirmUnblockUsre.Title" = "إلغاء حظر الحساب"; "Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.Message" = "Confirm to unmute %@"; @@ -263,7 +263,7 @@ uploaded to Mastodon."; "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.PeopleTalking" = "%@ people are talking"; +"Scene.Search.Recommend.HashTag.PeopleTalking" = "%@ أشخاص يتحدَّثوا"; "Scene.Search.Recommend.HashTag.Title" = "ذات شعبية على ماستدون"; "Scene.Search.SearchBar.Cancel" = "إلغاء"; "Scene.Search.SearchBar.Placeholder" = "البحث عن وسوم أو مستخدمين·ات"; @@ -285,7 +285,7 @@ uploaded to Mastodon."; "Scene.ServerPicker.Button.Category.Games" = "ألعاب"; "Scene.ServerPicker.Button.Category.General" = "عام"; "Scene.ServerPicker.Button.Category.Journalism" = "صحافة"; -"Scene.ServerPicker.Button.Category.Lgbt" = "lgbt"; +"Scene.ServerPicker.Button.Category.Lgbt" = "مجتمع الشواذ"; "Scene.ServerPicker.Button.Category.Music" = "موسيقى"; "Scene.ServerPicker.Button.Category.Regional" = "اقليمي"; "Scene.ServerPicker.Button.Category.Tech" = "تكنولوجيا"; @@ -309,38 +309,38 @@ any server."; "Scene.Settings.Footer.MastodonDescription" = "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على غيت هب %@ (%@)"; "Scene.Settings.Keyboard.CloseSettingsWindow" = "إغلاق نافذة الإعدادات"; "Scene.Settings.Section.Appearance.Automatic" = "تلقائي"; -"Scene.Settings.Section.Appearance.Dark" = "Always Dark"; -"Scene.Settings.Section.Appearance.Light" = "Always Light"; +"Scene.Settings.Section.Appearance.Dark" = "مظلمٌ دائِمًا"; +"Scene.Settings.Section.Appearance.Light" = "مضيءٌ دائمًا"; "Scene.Settings.Section.Appearance.Title" = "المظهر"; "Scene.Settings.Section.BoringZone.AccountSettings" = "إعدادات الحساب"; "Scene.Settings.Section.BoringZone.Privacy" = "سياسة الخصوصية"; "Scene.Settings.Section.BoringZone.Terms" = "شروط الخدمة"; "Scene.Settings.Section.BoringZone.Title" = "المنطقة المملة"; -"Scene.Settings.Section.Notifications.Boosts" = "Reblogs my post"; -"Scene.Settings.Section.Notifications.Favorites" = "Favorites my post"; +"Scene.Settings.Section.Notifications.Boosts" = "إعادة تدوين منشوراتي"; +"Scene.Settings.Section.Notifications.Favorites" = "الإعجاب بِمنشوراتي"; "Scene.Settings.Section.Notifications.Follows" = "يتابعني"; -"Scene.Settings.Section.Notifications.Mentions" = "Mentions me"; +"Scene.Settings.Section.Notifications.Mentions" = "الإشارة لي"; "Scene.Settings.Section.Notifications.Title" = "الإشعارات"; -"Scene.Settings.Section.Notifications.Trigger.Anyone" = "anyone"; -"Scene.Settings.Section.Notifications.Trigger.Follow" = "anyone I follow"; +"Scene.Settings.Section.Notifications.Trigger.Anyone" = "أي شخص"; +"Scene.Settings.Section.Notifications.Trigger.Follow" = "أي شخص أُتابِعُه"; "Scene.Settings.Section.Notifications.Trigger.Follower" = "مشترِك"; -"Scene.Settings.Section.Notifications.Trigger.Noone" = "no one"; -"Scene.Settings.Section.Notifications.Trigger.Title" = "Notify me when"; -"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "Disable animated avatars"; -"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "Disable animated emojis"; +"Scene.Settings.Section.Notifications.Trigger.Noone" = "لا أحد"; +"Scene.Settings.Section.Notifications.Trigger.Title" = "إشعاري عِندَ"; +"Scene.Settings.Section.Preference.DisableAvatarAnimation" = "تعطيل الصور الرمزية المتحرِّكة"; +"Scene.Settings.Section.Preference.DisableEmojiAnimation" = "تعطيل الرموز التعبيرية المتحرِّكَة"; "Scene.Settings.Section.Preference.Title" = "التفضيلات"; -"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "True black dark mode"; -"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "Use default browser to open links"; +"Scene.Settings.Section.Preference.TrueBlackDarkMode" = "النمط الأسود الداكِن الحقيقي"; +"Scene.Settings.Section.Preference.UsingDefaultBrowser" = "اِستخدام المتصفح الافتراضي لفتح الروابط"; "Scene.Settings.Section.SpicyZone.Clear" = "مسح ذاكرة التخزين المؤقت للوسائط"; "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.Title" = "ابحث عن أشخاص لمتابعتهم"; -"Scene.Thread.BackTitle" = "Post"; -"Scene.Thread.Title" = "Post from %@"; +"Scene.Thread.BackTitle" = "منشور"; +"Scene.Thread.Title" = "مَنشور مِن %@"; "Scene.Welcome.Slogan" = "Social networking back in your hands."; -"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard"; +"Scene.Wizard.AccessibilityHint" = "انقر نقرًا مزدوجًا لتجاهل النافذة المنبثقة"; "Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button."; -"Scene.Wizard.NewInMastodon" = "New in Mastodon"; \ No newline at end of file +"Scene.Wizard.NewInMastodon" = "جديد في ماستودون"; \ No newline at end of file diff --git a/Mastodon/Resources/ar.lproj/Localizable.stringsdict b/Mastodon/Resources/ar.lproj/Localizable.stringsdict index e6b0d5f95..e3dee0d80 100644 --- a/Mastodon/Resources/ar.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/ar.lproj/Localizable.stringsdict @@ -15,21 +15,21 @@ zero %ld unread notification one - 1 unread notification + إشعار واحِد غير مقروء two - %ld unread notification + إشعاران غير مقروءان few %ld unread notification many - %ld unread notification + %ld إشعارًا غيرَ مقروء other - %ld unread notification + %ld إشعار غير مقروء a11y.plural.count.input_limit_exceeds NSStringLocalizedFormatKey - Input limit exceeds %#@character_count@ + تمَّ تجاوز حدّ الإدخال %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -37,23 +37,23 @@ NSStringFormatValueTypeKey ld zero - %ld characters + لا حرف one - 1 character + حرفٌ واحِد two - %ld characters + حرفان اثنان few - %ld characters + %ld حُرُوف many - %ld characters + %ld حرفًا other - %ld characters + %ld حَرف a11y.plural.count.input_limit_remains NSStringLocalizedFormatKey - Input limit remains %#@character_count@ + يتبقَّى على حدّ الإدخال %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -61,17 +61,17 @@ NSStringFormatValueTypeKey ld zero - %ld characters + لا حرف one - 1 character + حرفٌ واحِد two - %ld characters + حرفان اثنان few - %ld characters + %ld حُرُوف many - %ld characters + %ld حرفًا other - %ld characters + %ld حَرف plural.count.metric_formatted.post @@ -85,17 +85,17 @@ NSStringFormatValueTypeKey ld zero - posts + لا منشور one - post + منشور two - posts + منشوران few - posts + منشورات many - posts + منشورًا other - posts + منشور plural.count.post @@ -109,17 +109,17 @@ NSStringFormatValueTypeKey ld zero - %ld posts + لا منشور one - 1 post + منشورٌ واحِد two - %ld posts + منشورانِ اثنان few - %ld posts + %ld منشورات many - %ld posts + %ld منشورًا other - %ld posts + %ld منشور plural.count.favorite @@ -133,17 +133,17 @@ NSStringFormatValueTypeKey ld zero - %ld favorites + لا إعجاب one - 1 favorite + إعجابٌ واحِد two - %ld favorites + إعجابانِ اثنان few - %ld favorites + %ld إعجابات many - %ld favorites + %ld إعجابًا other - %ld favorites + %ld إعجاب plural.count.reblog @@ -157,17 +157,17 @@ NSStringFormatValueTypeKey ld zero - %ld reblogs + لا إعاد تدوين one - 1 reblog + إعادةُ تدوينٍ واحِدة two - %ld reblogs + إعادتا تدوين few - %ld reblogs + %ld إعاداتِ تدوين many - %ld reblogs + %ld إعادةٍ للتدوين other - %ld reblogs + %ld إعادة تدوين plural.count.vote @@ -181,17 +181,17 @@ NSStringFormatValueTypeKey ld zero - %ld votes + لا صوت one - 1 vote + صوتٌ واحِد two - %ld votes + صوتانِ اثنان few - %ld votes + %ld أصوات many - %ld votes + %ld صوتًا other - %ld votes + %ld صوت plural.count.voter @@ -205,17 +205,17 @@ NSStringFormatValueTypeKey ld zero - %ld voters + لا مُصوِّتون one - 1 voter + مُصوِّتٌ واحِد two - %ld voters + مُصوِّتانِ اثنان few - %ld voters + %ld مُصوِّتين many - %ld voters + %ld مُصوِّتًا other - %ld voters + %ld مُصوِّت plural.people_talking @@ -229,17 +229,17 @@ NSStringFormatValueTypeKey ld zero - %ld people talking + لا أحَدَ يتحدَّث one - 1 people talking + شخصٌ واحدٌ يتحدَّث two - %ld people talking + شخصانِ اثنان يتحدَّثا few - %ld people talking + %ld أشخاصٍ يتحدَّثون many - %ld people talking + %ld شخصًا يتحدَّثون other - %ld people talking + %ld شخصٍ يتحدَّثون plural.count.following @@ -253,17 +253,17 @@ NSStringFormatValueTypeKey ld zero - %ld following + لا مُتابَع one - 1 following + مُتابَعٌ واحد two - %ld following + مُتابَعانِ few - %ld following + %ld مُتابَعين many - %ld following + %ld مُتابَعًا other - %ld following + %ld مُتابَع plural.count.follower @@ -279,15 +279,15 @@ zero %ld followers one - 1 follower + مُتابِعٌ واحد two - %ld followers + مُتابِعانِ اثنان few - %ld followers + %ld مُتابِعين many - %ld followers + %ld مُتابِعًا other - %ld followers + %ld مُتابِع date.year.left @@ -301,17 +301,17 @@ NSStringFormatValueTypeKey ld zero - %ld years left + تتبقى لَحظة one - 1 year left + تتبقى سنة two - %ld years left + تتبقى سنتين few - %ld years left + تتبقى %ld سنوات many - %ld years left + تتبقى %ld سنةً other - %ld years left + تتبقى %ld سنة date.month.left @@ -325,17 +325,17 @@ NSStringFormatValueTypeKey ld zero - %ld months left + تتبقى لَحظة one - 1 months left + يتبقى شهر two - %ld months left + يتبقى شهرين few - %ld months left + يتبقى %ld أشهر many - %ld months left + يتبقى %ld شهرًا other - %ld months left + يتبقى %ld شهر date.day.left @@ -349,17 +349,17 @@ NSStringFormatValueTypeKey ld zero - %ld days left + تتبقى لحظة one - 1 day left + يتبقى يوم two - %ld days left + يتبقى يومين few - %ld days left + يتبقى %ld أيام many - %ld days left + يتبقى %ld يومًا other - %ld days left + يتبقى %ld يوم date.hour.left @@ -373,17 +373,17 @@ NSStringFormatValueTypeKey ld zero - %ld hours left + تتبقى لَحظة one - 1 hour left + تتبقى ساعة two - %ld hours left + تتبقى ساعتين few - %ld hours left + تتبقى %ld ساعات many - %ld hours left + تتبقى %ld ساعةً other - %ld hours left + تتبقى %ld ساعة date.minute.left @@ -397,17 +397,17 @@ NSStringFormatValueTypeKey ld zero - %ld minutes left + تتبقى لَحظة one - 1 minute left + تتبقى دقيقة two - %ld minutes left + تتبقى دقيقتين few - %ld minutes left + تتبقى %ld دقائق many - %ld minutes left + تتبقى %ld دقيقةً other - %ld minutes left + تتبقى %ld دقيقة date.second.left @@ -421,17 +421,17 @@ NSStringFormatValueTypeKey ld zero - %ld seconds left + تتبقى لَحظة one - 1 second left + تتبقى ثانية two - %ld seconds left + تتبقى ثانيتين few - %ld seconds left + تتبقى %ld ثوان many - %ld seconds left + تتبقى %ld ثانيةً other - %ld seconds left + تتبقى %ld ثانية date.year.ago.abbr @@ -445,17 +445,17 @@ NSStringFormatValueTypeKey ld zero - %ldy ago + مُنذُ لَحظة one - 1y ago + مُنذُ سنة two - %ldy ago + مُنذُ سنتين few - %ldy ago + مُنذُ %ld سنين many - %ldy ago + مُنذُ %ld سنةً other - %ldy ago + مُنذُ %ld سنة date.month.ago.abbr @@ -469,17 +469,17 @@ NSStringFormatValueTypeKey ld zero - %ldM ago + مُنذُ لَحظة one - 1M ago + مُنذُ شهر two - %ldM ago + مُنذُ شهرين few - %ldM ago + مُنذُ %ld أشهُر many - %ldM ago + مُنذُ %ld شهرًا other - %ldM ago + مُنذُ %ld شهر date.day.ago.abbr @@ -493,17 +493,17 @@ NSStringFormatValueTypeKey ld zero - %ldd ago + مُنذُ لَحظة one - 1d ago + مُنذُ يوم two - %ldd ago + مُنذُ يومين few - %ldd ago + مُنذُ %ld أيام many - %ldd ago + مُنذُ %ld يومًا other - %ldd ago + مُنذُ %ld يوم date.hour.ago.abbr @@ -517,17 +517,17 @@ NSStringFormatValueTypeKey ld zero - %ldh ago + مُنذُ لَحظة one - 1h ago + مُنذُ ساعة two - %ldh ago + مُنذُ ساعتين few - %ldh ago + مُنذُ %ld ساعات many - %ldh ago + مُنذُ %ld ساعةًَ other - %ldh ago + مُنذُ %ld ساعة date.minute.ago.abbr @@ -541,17 +541,17 @@ NSStringFormatValueTypeKey ld zero - %ldm ago + مُنذُ لَحظة one - 1m ago + مُنذُ دقيقة two - %ldm ago + مُنذُ دقيقتان few - %ldm ago + مُنذُ %ld دقائق many - %ldm ago + مُنذُ %ld دقيقةً other - %ldm ago + مُنذُ %ld دقيقة date.second.ago.abbr @@ -565,17 +565,17 @@ NSStringFormatValueTypeKey ld zero - %lds ago + مُنذُ لَحظة one - 1s ago + مُنذُ ثانية two - %lds ago + مُنذُ ثانيتين few - %lds ago + مُنذُ %ld ثوان many - %lds ago + مُنذُ %ld ثانية other - %lds ago + مُنذُ %ld ثانية diff --git a/Mastodon/Resources/de.lproj/Localizable.strings b/Mastodon/Resources/de.lproj/Localizable.strings index cc92b8e77..51028d7a8 100644 --- a/Mastodon/Resources/de.lproj/Localizable.strings +++ b/Mastodon/Resources/de.lproj/Localizable.strings @@ -133,7 +133,7 @@ Dein Profil sieht für diesen Benutzer auch so aus."; "Common.Controls.Timeline.Loader.LoadingMissingPosts" = "Lade fehlende Beiträge..."; "Common.Controls.Timeline.Loader.ShowMoreReplies" = "Weitere Antworten anzeigen"; "Common.Controls.Timeline.Timestamp.Now" = "Gerade"; -"Scene.AccountList.AddAccount" = "Add Account"; +"Scene.AccountList.AddAccount" = "Konto hinzufügen"; "Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; "Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; "Scene.Compose.Accessibility.AppendAttachment" = "Anhang hinzufügen"; @@ -340,6 +340,6 @@ beliebigen Server."; "Scene.Thread.BackTitle" = "Beitrag"; "Scene.Thread.Title" = "Beitrag von %@"; "Scene.Welcome.Slogan" = "Soziale Netzwerke wieder in deinen Händen."; -"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard"; +"Scene.Wizard.AccessibilityHint" = "Doppeltippen, um diesen Assistenten zu schließen"; "Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button."; -"Scene.Wizard.NewInMastodon" = "New in Mastodon"; \ No newline at end of file +"Scene.Wizard.NewInMastodon" = "Neu in Mastodon"; \ No newline at end of file diff --git a/Mastodon/Resources/de.lproj/Localizable.stringsdict b/Mastodon/Resources/de.lproj/Localizable.stringsdict index c868bdc0f..66b7f2a2d 100644 --- a/Mastodon/Resources/de.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/de.lproj/Localizable.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey ld one - 1 unread notification + 1 ungelesene Benachrichtigung other - %ld unread notification + %ld ungelesene Benachrichtigungen a11y.plural.count.input_limit_exceeds diff --git a/Mastodon/Resources/th.lproj/Localizable.strings b/Mastodon/Resources/th.lproj/Localizable.strings index 0c586cab3..a61b1d15f 100644 --- a/Mastodon/Resources/th.lproj/Localizable.strings +++ b/Mastodon/Resources/th.lproj/Localizable.strings @@ -133,9 +133,9 @@ "Common.Controls.Timeline.Loader.LoadingMissingPosts" = "กำลังโหลดโพสต์ที่ขาดหายไป..."; "Common.Controls.Timeline.Loader.ShowMoreReplies" = "แสดงการตอบกลับเพิ่มเติม"; "Common.Controls.Timeline.Timestamp.Now" = "ตอนนี้"; -"Scene.AccountList.AddAccount" = "Add Account"; -"Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; -"Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; +"Scene.AccountList.AddAccount" = "เพิ่มบัญชี"; +"Scene.AccountList.DismissAccountSwitcher" = "ปิดตัวสลับบัญชี"; +"Scene.AccountList.TabBarHint" = "โปรไฟล์ที่เลือกในปัจจุบัน: %@ แตะสองครั้งแล้วกดค้างไว้เพื่อแสดงตัวสลับบัญชี"; "Scene.Compose.Accessibility.AppendAttachment" = "เพิ่มไฟล์แนบ"; "Scene.Compose.Accessibility.AppendPoll" = "เพิ่มการสำรวจความคิดเห็น"; "Scene.Compose.Accessibility.CustomEmojiPicker" = "ตัวเลือกอีโมจิที่กำหนดเอง"; @@ -341,6 +341,6 @@ "Scene.Thread.Title" = "โพสต์จาก %@"; "Scene.Welcome.Slogan" = "ให้เครือข่ายสังคม กลับมาอยู่ในมือของคุณ"; -"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"; \ No newline at end of file +"Scene.Wizard.AccessibilityHint" = "แตะสองครั้งเพื่อปิดตัวช่วยสร้างนี้"; +"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "สลับระหว่างหลายบัญชีโดยกดปุ่มโปรไฟล์ค้างไว้"; +"Scene.Wizard.NewInMastodon" = "มาใหม่ใน Mastodon"; \ No newline at end of file diff --git a/Mastodon/Resources/th.lproj/Localizable.stringsdict b/Mastodon/Resources/th.lproj/Localizable.stringsdict index 1d6ff10bc..8971821f6 100644 --- a/Mastodon/Resources/th.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/th.lproj/Localizable.stringsdict @@ -13,7 +13,7 @@ NSStringFormatValueTypeKey ld other - %ld unread notification + %ld การแจ้งเตือนที่ยังไม่ได้อ่าน a11y.plural.count.input_limit_exceeds From 3b7e52edfa6328ec5f502f42eee543ea4a8313c7 Mon Sep 17 00:00:00 2001 From: CMK Date: Sat, 9 Oct 2021 19:25:42 +0800 Subject: [PATCH 20/95] chore: update version to 1.2.0 (73) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 9 --- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 12 files changed, 45 insertions(+), 54 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index d332d8527..d6c057eb7 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index d332d8527..d6c057eb7 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index d332d8527..d6c057eb7 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index a1283ea0a..009fbb446 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4776,7 +4776,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4805,7 +4805,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4913,11 +4913,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4944,11 +4944,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4973,11 +4973,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5003,11 +5003,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5070,7 +5070,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5095,7 +5095,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5120,7 +5120,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5145,7 +5145,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5170,7 +5170,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5195,7 +5195,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5220,7 +5220,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5245,7 +5245,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5336,7 +5336,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5403,11 +5403,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5452,7 +5452,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5477,11 +5477,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5573,7 +5573,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5640,11 +5640,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5689,7 +5689,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5714,11 +5714,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 72; + DYLIB_CURRENT_VERSION = 73; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5744,7 +5744,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5768,7 +5768,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 72; + CURRENT_PROJECT_VERSION = 73; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index b5ecab282..915ce8a0f 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 35 + 42 CoreDataStack.xcscheme_^#shared#^_ orderHint - 37 + 41 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 36 + 43 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 38 + 44 SuppressBuildableAutocreation diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index e118002a8..43a0036bd 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -216,15 +216,6 @@ "revision": "dad97167bf1be16aeecd109130900995dd01c515", "version": "2.6.0" } - }, - { - "package": "UITextView+Placeholder", - "repositoryURL": "https://github.com/MainasuK/UITextView-Placeholder", - "state": { - "branch": null, - "revision": "20f513ded04a040cdf5467f0891849b1763ede3b", - "version": "1.4.1" - } } ] }, diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 601a346dd..64cf3e07b 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 72 + 73 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index cfbcd88df..298c2a3d4 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index d332d8527..d6c057eb7 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index d332d8527..d6c057eb7 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index f5175f013..b9054fc14 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 03a3f83e5..de78b7103 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 72 + 73 NSExtension NSExtensionAttributes From 051a8f40e313367655d341186287e4e1fb9366d9 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 11:07:42 +0800 Subject: [PATCH 21/95] fix: logic error prevent status action menu display intro in 80ea6ac --- Mastodon/Protocol/UserProvider/UserProviderFacade.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift index 47490c2d8..f85881943 100644 --- a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift +++ b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift @@ -221,8 +221,6 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak sourceView, weak barButtonItem] _ in guard let provider = provider else { return } - guard let sourceView = sourceView else { return } - guard let barButtonItem = barButtonItem else { return } let activityViewController = createActivityViewControllerForMastodonUser(mastodonUser: shareUser, dependency: provider) provider.coordinator.present( scene: .activityViewController( @@ -247,8 +245,6 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak sourceView, weak barButtonItem] _ in guard let provider = provider else { return } - guard let sourceView = sourceView else { return } - guard let barButtonItem = barButtonItem else { return } let activityViewController = createActivityViewControllerForMastodonUser(status: shareStatus, dependency: provider) provider.coordinator.present( scene: .activityViewController( @@ -273,7 +269,6 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak cell] _ in guard let provider = provider else { return } - guard let cell = cell else { return } UserProviderFacade.toggleUserMuteRelationship( provider: provider, @@ -304,7 +299,6 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak cell] _ in guard let provider = provider else { return } - guard let cell = cell else { return } UserProviderFacade.toggleUserBlockRelationship( provider: provider, @@ -364,7 +358,6 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak cell] _ in guard let provider = provider else { return } - guard let cell = cell else { return } provider.context.blockDomainService.unblockDomain(userProvider: provider, cell: cell) } children.append(unblockDomainAction) @@ -378,14 +371,12 @@ extension UserProviderFacade { state: .off ) { [weak provider, weak cell] _ in guard let provider = provider else { return } - guard let cell = cell else { return } let alertController = UIAlertController(title: L10n.Common.Alerts.BlockDomain.title(mastodonUser.domainFromAcct), message: nil, preferredStyle: .alert) let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .default) { _ in } alertController.addAction(cancelAction) let blockDomainAction = UIAlertAction(title: L10n.Common.Alerts.BlockDomain.blockEntireDomain, style: .destructive) { [weak provider, weak cell] _ in guard let provider = provider else { return } - guard let cell = cell else { return } provider.context.blockDomainService.blockDomain(userProvider: provider, cell: cell) } alertController.addAction(blockDomainAction) From e5a3c4ae0ded68f40c9cea72c94434a34c483bd5 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 12:35:20 +0800 Subject: [PATCH 22/95] fix: iOS 15 new tab bar appearance not override issue --- Mastodon/Service/ThemeService/ThemeService+Appearance.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Mastodon/Service/ThemeService/ThemeService+Appearance.swift b/Mastodon/Service/ThemeService/ThemeService+Appearance.swift index 8130942aa..cffa45c7a 100644 --- a/Mastodon/Service/ThemeService/ThemeService+Appearance.swift +++ b/Mastodon/Service/ThemeService/ThemeService+Appearance.swift @@ -48,6 +48,11 @@ extension ThemeService { tabBarAppearance.backgroundColor = theme.tabBarBackgroundColor tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color UITabBar.appearance().standardAppearance = tabBarAppearance + if #available(iOS 15.0, *) { + UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance + } else { + // Fallback on earlier versions + } UITabBar.appearance().barTintColor = theme.tabBarBackgroundColor // set table view cell appearance From 7e37d2c7c93fd98ed855d79ce124acd4c8f72574 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 12:59:15 +0800 Subject: [PATCH 23/95] fix: status header icon not align on iOS 15 issue --- .../Section/Status/StatusSection.swift | 4 ++-- Mastodon/Extension/MetaLabel.swift | 18 ++++++++++++++++++ .../Compose/ComposeViewModel+DataSource.swift | 2 +- .../Scene/Compose/View/ReplicaStatusView.swift | 7 ++++--- .../Scene/Share/View/Content/StatusView.swift | 7 ++++--- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Mastodon/Diffiable/Section/Status/StatusSection.swift b/Mastodon/Diffiable/Section/Status/StatusSection.swift index 682422927..ceb0c9458 100644 --- a/Mastodon/Diffiable/Section/Status/StatusSection.swift +++ b/Mastodon/Diffiable/Section/Status/StatusSection.swift @@ -639,7 +639,7 @@ extension StatusSection { ) { if status.reblog != nil { cell.statusView.headerContainerView.isHidden = false - cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.reblogIconImage) + cell.statusView.headerIconLabel.configure(attributedString: StatusView.iconAttributedString(image: StatusView.reblogIconImage)) let headerText: String = { let author = status.author let name = author.displayName.isEmpty ? author.username : author.displayName @@ -657,7 +657,7 @@ extension StatusSection { cell.statusView.headerInfoLabel.isAccessibilityElement = true } else if status.inReplyToID != nil { cell.statusView.headerContainerView.isHidden = false - cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.replyIconImage) + cell.statusView.headerIconLabel.configure(attributedString: StatusView.iconAttributedString(image: StatusView.replyIconImage)) let headerText: String = { guard let replyTo = status.replyTo else { return L10n.Common.Controls.Status.userRepliedTo("-") diff --git a/Mastodon/Extension/MetaLabel.swift b/Mastodon/Extension/MetaLabel.swift index 04b214d82..cf7d27cc0 100644 --- a/Mastodon/Extension/MetaLabel.swift +++ b/Mastodon/Extension/MetaLabel.swift @@ -111,6 +111,24 @@ extension MetaLabel { } +extension MetaLabel { + func configure(attributedString: NSAttributedString) { + let attributedString = NSMutableAttributedString(attributedString: attributedString) + + MetaText.setAttributes( + for: attributedString, + textAttributes: textAttributes, + linkAttributes: linkAttributes, + paragraphStyle: paragraphStyle, + content: PlaintextMetaContent(string: "") + ) + + textStorage.setAttributedString(attributedString) + self.attributedText = attributedString + setNeedsDisplay() + } +} + struct PlaintextMetaContent: MetaContent { let string: String let entities: [Meta.Entity] = [] diff --git a/Mastodon/Scene/Compose/ComposeViewModel+DataSource.swift b/Mastodon/Scene/Compose/ComposeViewModel+DataSource.swift index 104bbde83..7fd07bf83 100644 --- a/Mastodon/Scene/Compose/ComposeViewModel+DataSource.swift +++ b/Mastodon/Scene/Compose/ComposeViewModel+DataSource.swift @@ -237,7 +237,7 @@ extension ComposeViewModel: UITableViewDataSource { return } cell.statusView.headerContainerView.isHidden = false - cell.statusView.headerIconLabel.attributedText = StatusView.iconAttributedString(image: StatusView.replyIconImage) + cell.statusView.headerIconLabel.configure(attributedString: StatusView.iconAttributedString(image: StatusView.replyIconImage)) let headerText: String = { let author = replyTo.author let name = author.displayName.isEmpty ? author.username : author.displayName diff --git a/Mastodon/Scene/Compose/View/ReplicaStatusView.swift b/Mastodon/Scene/Compose/View/ReplicaStatusView.swift index cb34f3ded..6f0527d55 100644 --- a/Mastodon/Scene/Compose/View/ReplicaStatusView.swift +++ b/Mastodon/Scene/Compose/View/ReplicaStatusView.swift @@ -45,9 +45,10 @@ final class ReplicaStatusView: UIView { return attributedString } - let headerIconLabel: UILabel = { - let label = UILabel() - label.attributedText = ReplicaStatusView.iconAttributedString(image: ReplicaStatusView.reblogIconImage) + let headerIconLabel: MetaLabel = { + let label = MetaLabel(style: .statusHeader) + let attributedString = StatusView.iconAttributedString(image: StatusView.reblogIconImage) + label.configure(attributedString: attributedString) return label }() diff --git a/Mastodon/Scene/Share/View/Content/StatusView.swift b/Mastodon/Scene/Share/View/Content/StatusView.swift index 9a6907d1c..a2788f524 100644 --- a/Mastodon/Scene/Share/View/Content/StatusView.swift +++ b/Mastodon/Scene/Share/View/Content/StatusView.swift @@ -73,9 +73,10 @@ final class StatusView: UIView { return attributedString } - let headerIconLabel: UILabel = { - let label = UILabel() - label.attributedText = StatusView.iconAttributedString(image: StatusView.reblogIconImage) + let headerIconLabel: MetaLabel = { + let label = MetaLabel(style: .statusHeader) + let attributedString = StatusView.iconAttributedString(image: StatusView.reblogIconImage) + label.configure(attributedString: attributedString) return label }() From cba963a81eb84f0c70c8978f147267a181a0c450 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 14:11:10 +0800 Subject: [PATCH 24/95] chore: update snapshot update logic for iOS 15 new default behavior --- Mastodon.xcodeproj/project.pbxproj | 8 ++++ .../UICollectionViewDiffableDataSource.swift | 40 +++++++++++++++++++ .../UITableViewDiffableDataSource.swift | 40 +++++++++++++++++++ .../HashtagTimelineViewModel+Diffable.swift | 2 +- .../HomeTimelineViewModel+Diffable.swift | 2 +- .../PublicTimelineViewModel.swift | 2 +- .../Thread/ThreadViewModel+Diffable.swift | 2 +- 7 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 Mastodon/Extension/UICollectionViewDiffableDataSource.swift create mode 100644 Mastodon/Extension/UITableViewDiffableDataSource.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 009fbb446..7d72f0397 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -361,6 +361,8 @@ DB73BF43271192BB00781945 /* InstanceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF42271192BB00781945 /* InstanceService.swift */; }; DB73BF45271195AC00781945 /* APIService+CoreData+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF44271195AC00781945 /* APIService+CoreData+Instance.swift */; }; DB73BF47271199CA00781945 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF46271199CA00781945 /* Instance.swift */; }; + DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */; }; + DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */; }; DB75BF1E263C1C1B00EDBF1F /* CustomScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */; }; DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */; }; DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */; }; @@ -1153,6 +1155,8 @@ DB73BF42271192BB00781945 /* InstanceService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceService.swift; sourceTree = ""; }; DB73BF44271195AC00781945 /* APIService+CoreData+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+CoreData+Instance.swift"; sourceTree = ""; }; DB73BF46271199CA00781945 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = ""; }; + DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewDiffableDataSource.swift; sourceTree = ""; }; + DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewDiffableDataSource.swift; sourceTree = ""; }; DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScheduler.swift; sourceTree = ""; }; DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = ""; }; DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = ""; }; @@ -2748,6 +2752,8 @@ DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */, DBCC3B2F261440A50045B23D /* UITabBarController.swift */, DBCC3B35261440BA0045B23D /* UINavigationController.swift */, + DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */, + DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */, ); path = Extension; sourceTree = ""; @@ -4014,6 +4020,7 @@ DB59F11825EFA35B001F1DAB /* StripProgressView.swift in Sources */, DB59F10425EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift in Sources */, DBF9814C265E339500E4BA07 /* ProfileFieldAddEntryCollectionViewCell.swift in Sources */, + DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */, DBA5E7AB263BD3F5004598BB /* TimelineTableViewCellContextMenuConfiguration.swift in Sources */, DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */, DB6D1B44263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift in Sources */, @@ -4110,6 +4117,7 @@ DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */, 2D61254D262547C200299647 /* APIService+Notification.swift in Sources */, DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */, + DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */, DBB525642612C988002F1F29 /* MeProfileViewModel.swift in Sources */, 5BB04FE9262EFC300043BFF6 /* ReportedStatusTableviewCell.swift in Sources */, DBAE3F822615DDA3004B8251 /* ProfileViewController+UserProvider.swift in Sources */, diff --git a/Mastodon/Extension/UICollectionViewDiffableDataSource.swift b/Mastodon/Extension/UICollectionViewDiffableDataSource.swift new file mode 100644 index 000000000..07d2fbd12 --- /dev/null +++ b/Mastodon/Extension/UICollectionViewDiffableDataSource.swift @@ -0,0 +1,40 @@ +// +// UICollectionViewDiffableDataSource.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-11. +// + +import UIKit + +// ref: https://www.jessesquires.com/blog/2021/07/08/diffable-data-source-behavior-changes-and-reconfiguring-cells-in-ios-15/ +extension UICollectionViewDiffableDataSource { + func reloadData( + snapshot: NSDiffableDataSourceSnapshot, + completion: (() -> Void)? = nil + ) { + if #available(iOS 15.0, *) { + self.applySnapshotUsingReloadData(snapshot, completion: completion) + } else { + self.apply(snapshot, animatingDifferences: false, completion: completion) + } + } + + func applySnapshot( + _ snapshot: NSDiffableDataSourceSnapshot, + animated: Bool, + completion: (() -> Void)? = nil) { + + if #available(iOS 15.0, *) { + self.apply(snapshot, animatingDifferences: animated, completion: completion) + } else { + if animated { + self.apply(snapshot, animatingDifferences: true, completion: completion) + } else { + UIView.performWithoutAnimation { + self.apply(snapshot, animatingDifferences: true, completion: completion) + } + } + } + } +} diff --git a/Mastodon/Extension/UITableViewDiffableDataSource.swift b/Mastodon/Extension/UITableViewDiffableDataSource.swift new file mode 100644 index 000000000..5006417a4 --- /dev/null +++ b/Mastodon/Extension/UITableViewDiffableDataSource.swift @@ -0,0 +1,40 @@ +// +// UITableViewDiffableDataSource.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-11. +// + +import UIKit + +// ref: https://www.jessesquires.com/blog/2021/07/08/diffable-data-source-behavior-changes-and-reconfiguring-cells-in-ios-15/ +extension UITableViewDiffableDataSource { + func reloadData( + snapshot: NSDiffableDataSourceSnapshot, + completion: (() -> Void)? = nil + ) { + if #available(iOS 15.0, *) { + self.applySnapshotUsingReloadData(snapshot, completion: completion) + } else { + self.apply(snapshot, animatingDifferences: false, completion: completion) + } + } + + func applySnapshot( + _ snapshot: NSDiffableDataSourceSnapshot, + animated: Bool, + completion: (() -> Void)? = nil) { + + if #available(iOS 15.0, *) { + self.apply(snapshot, animatingDifferences: animated, completion: completion) + } else { + if animated { + self.apply(snapshot, animatingDifferences: true, completion: completion) + } else { + UIView.performWithoutAnimation { + self.apply(snapshot, animatingDifferences: true, completion: completion) + } + } + } + } +} diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift index 23ed744fc..a601eb927 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift @@ -89,7 +89,7 @@ extension HashtagTimelineViewModel { } DispatchQueue.main.async { - diffableDataSource.apply(newSnapshot, animatingDifferences: false) { + diffableDataSource.reloadData(snapshot: newSnapshot) { tableView.scrollToRow(at: difference.targetIndexPath, at: .top, animated: false) tableView.contentOffset.y = tableView.contentOffset.y - difference.offset self.isFetchingLatestTimeline.value = false diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift index 73d2c1739..e87cab1c1 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift @@ -119,7 +119,7 @@ extension HomeTimelineViewModel: NSFetchedResultsControllerDelegate { return } - diffableDataSource.apply(newSnapshot, animatingDifferences: false) { + diffableDataSource.reloadData(snapshot: newSnapshot) { tableView.scrollToRow(at: difference.targetIndexPath, at: .top, animated: false) tableView.contentOffset.y = tableView.contentOffset.y - difference.offset self.isFetchingLatestTimeline.value = false diff --git a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift index c3b1a3d4b..6d6ecbd34 100644 --- a/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift +++ b/Mastodon/Scene/PublicTimeline/PublicTimelineViewModel.swift @@ -102,7 +102,7 @@ class PublicTimelineViewModel: NSObject { return } - diffableDataSource.apply(snapshot, animatingDifferences: false) { + diffableDataSource.reloadData(snapshot: snapshot) { tableView.scrollToRow(at: difference.targetIndexPath, at: .top, animated: false) tableView.contentOffset.y = tableView.contentOffset.y - difference.offset self.isFetchingLatestTimeline.value = false diff --git a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift index 5bbc383e4..853bee9da 100644 --- a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift +++ b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift @@ -135,7 +135,7 @@ extension ThreadViewModel { // save height before cell reuse let oldRootCellHeight = oldRootCell?.frame.height - diffableDataSource.apply(newSnapshot, animatingDifferences: false) { + diffableDataSource.reloadData(snapshot: newSnapshot) { guard let _ = rootItem else { return } From 084524eb75e877841ac8d51a3817e25a57fe4790 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 14:32:34 +0800 Subject: [PATCH 25/95] fix: home screen quick actions crash on iPad issue --- Mastodon/Coordinator/SceneCoordinator.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 3178a2cb7..00a245bad 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -14,11 +14,11 @@ final public class SceneCoordinator { private weak var scene: UIScene! private weak var sceneDelegate: SceneDelegate! private weak var appContext: AppContext! - private(set) weak var tabBarController: MainTabBarController! let id = UUID().uuidString - weak var splitViewController: RootSplitViewController? + private(set) weak var tabBarController: MainTabBarController! + private(set) weak var splitViewController: RootSplitViewController? private(set) var secondaryStackHashValues = Set() @@ -124,6 +124,7 @@ extension SceneCoordinator { default: let splitViewController = RootSplitViewController(context: appContext, coordinator: self) self.splitViewController = splitViewController + self.tabBarController = splitViewController.mainTabBarController sceneDelegate.window?.rootViewController = splitViewController } } @@ -253,7 +254,7 @@ extension SceneCoordinator { } func switchToTabBar(tab: MainTabBarController.Tab) { - tabBarController.selectedIndex = tab.rawValue + tabBarController.currentTab.value = tab } } From 575035daaf22508b789902bb9b9cf0bced603099 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 15:25:21 +0800 Subject: [PATCH 26/95] fix: ShareExtension UI hack not works on iOS 15 issue --- .../Scene/View/ComposeView.swift | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ShareActionExtension/Scene/View/ComposeView.swift b/ShareActionExtension/Scene/View/ComposeView.swift index 25adf4c5a..a688d6492 100644 --- a/ShareActionExtension/Scene/View/ComposeView.swift +++ b/ShareActionExtension/Scene/View/ComposeView.swift @@ -85,6 +85,7 @@ public struct ComposeView: View { .frame(height: viewModel.toolbarHeight + 20) .listRow(backgroundColor: Color(viewModel.backgroundColor)) } // end List + .listStyle(.plain) .introspectTableView(customize: { tableView in // tableView.keyboardDismissMode = .onDrag tableView.verticalScrollIndicatorInsets.bottom = viewModel.toolbarHeight @@ -101,7 +102,7 @@ public struct ComposeView: View { .introspectTableView(customize: { tableView in tableView.backgroundColor = .clear }) - .background(Color(viewModel.backgroundColor).ignoresSafeArea()) + .overrideBackground(color: Color(viewModel.backgroundColor)) } // end GeometryReader } // end body } @@ -112,10 +113,26 @@ struct ComposeListViewFramePreferenceKey: PreferenceKey { } extension View { + // hack for separator line + @ViewBuilder func listRow(backgroundColor: Color) -> some View { - self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) - .listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1)) - .background(backgroundColor) + // expand list row to edge (set inset) + // then hide the separator + if #available(iOS 15, *) { + frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) + .listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1)) + .background(backgroundColor) + .listRowSeparator(.hidden) // new API + } else { + frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) + .listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1)) // separator line hidden magic + .background(backgroundColor) + } + } + + @ViewBuilder + func overrideBackground(color: Color) -> some View { + background(color.ignoresSafeArea()) } } From eaa2ef40839f248a9f5bcb3493b0f8997dc8019e Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 19:19:27 +0800 Subject: [PATCH 27/95] feat: [WIP] handle notification for multiple accounts --- .../Entity/MastodonAuthentication.swift | 4 + Mastodon/Coordinator/SceneCoordinator.swift | 92 +++++++++++++++++++ .../Scene/Account/AccountViewController.swift | 4 +- ...meTimelineViewController+DebugAction.swift | 78 ++++++++++++++++ .../Profile/RemoteProfileViewModel.swift | 48 ++++++++++ .../Root/MainTab/MainTabBarController.swift | 10 -- .../Scene/Thread/RemoteThreadViewModel.swift | 1 - Mastodon/Service/AuthenticationService.swift | 27 +++++- Mastodon/Service/NotificationService.swift | 2 +- Mastodon/Supporting Files/AppDelegate.swift | 4 +- .../MastodonNotification.swift | 20 +++- 11 files changed, 272 insertions(+), 18 deletions(-) diff --git a/CoreDataStack/Entity/MastodonAuthentication.swift b/CoreDataStack/Entity/MastodonAuthentication.swift index 66b8ad6a9..7aafd65a4 100644 --- a/CoreDataStack/Entity/MastodonAuthentication.swift +++ b/CoreDataStack/Entity/MastodonAuthentication.swift @@ -167,4 +167,8 @@ extension MastodonAuthentication { ]) } + public static func predicate(userAccessToken: String) -> NSPredicate { + return NSPredicate(format: "%K == %@", #keyPath(MastodonAuthentication.userAccessToken), userAccessToken) + } + } diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 00a245bad..87e97a536 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -5,12 +5,16 @@ // Created by Cirno MainasuK on 2021-1-27. import UIKit +import Combine import SafariServices import CoreDataStack +import MastodonSDK import PanModal final public class SceneCoordinator { + private var disposeBag = Set() + private weak var scene: UIScene! private weak var sceneDelegate: SceneDelegate! private weak var appContext: AppContext! @@ -28,6 +32,93 @@ final public class SceneCoordinator { self.appContext = appContext scene.session.sceneCoordinator = self + + appContext.notificationService.requestRevealNotificationPublisher + .receive(on: DispatchQueue.main) + .compactMap { [weak self] pushNotification -> AnyPublisher in + guard let self = self else { return Just(nil).eraseToAnyPublisher() } + // skip if no available account + guard let currentActiveAuthenticationBox = appContext.authenticationService.activeMastodonAuthenticationBox.value else { + return Just(nil).eraseToAnyPublisher() + } + + let accessToken = pushNotification._accessToken // use raw accessToken value without normalize + if currentActiveAuthenticationBox.userAuthorization.accessToken == accessToken { + // do nothing if notification for current account + return Just(pushNotification).eraseToAnyPublisher() + } else { + // switch to notification's account + let request = MastodonAuthentication.sortedFetchRequest + request.predicate = MastodonAuthentication.predicate(userAccessToken: accessToken) + request.returnsObjectsAsFaults = false + request.fetchLimit = 1 + do { + guard let authentication = try appContext.managedObjectContext.fetch(request).first else { + return Just(nil).eraseToAnyPublisher() + } + let domain = authentication.domain + let userID = authentication.userID + return appContext.authenticationService.activeMastodonUser(domain: domain, userID: userID) + .receive(on: DispatchQueue.main) + .map { [weak self] result -> MastodonPushNotification? in + guard let self = self else { return nil } + switch result { + case .success: + // reset view hierarchy + self.setup() + return pushNotification + case .failure: + return nil + } + } + .delay(for: 1, scheduler: DispatchQueue.main) // set delay to slow transition (not must) + .eraseToAnyPublisher() + } catch { + assertionFailure(error.localizedDescription) + return Just(nil).eraseToAnyPublisher() + } + } + } + .switchToLatest() + .receive(on: DispatchQueue.main) + .sink { [weak self] pushNotification in + guard let self = self else { return } + guard let pushNotification = pushNotification else { return } + + // redirect to notification tab + self.switchToTabBar(tab: .notification) + + + // Delay in next run loop + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + // Note: + // show (push) on phone + // showDetail in .secondary in UISplitViewController on pad + let from = self.splitViewController?.topMost ?? self.tabBarController.topMost + + // show notification related content + guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } + let notificationID = String(pushNotification.notificationID) + + switch type { + case .follow: + let profileViewModel = RemoteProfileViewModel(context: appContext, notificationID: notificationID) + self.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) + case .followRequest: + // do nothing + break + case .mention, .reblog, .favourite, .poll, .status: + let threadViewModel = RemoteThreadViewModel(context: appContext, notificationID: notificationID) + self.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show) + case ._other: + assertionFailure() + break + } + } + } + .store(in: &disposeBag) } } @@ -254,6 +345,7 @@ extension SceneCoordinator { } func switchToTabBar(tab: MainTabBarController.Tab) { + tabBarController.selectedIndex = tab.rawValue tabBarController.currentTab.value = tab } } diff --git a/Mastodon/Scene/Account/AccountViewController.swift b/Mastodon/Scene/Account/AccountViewController.swift index 2898b9b5f..4f2ece253 100644 --- a/Mastodon/Scene/Account/AccountViewController.swift +++ b/Mastodon/Scene/Account/AccountViewController.swift @@ -111,8 +111,10 @@ extension AccountListViewController { viewModel.dataSourceDidUpdate .receive(on: DispatchQueue.main) - .sink { [weak self] in + .sink { [weak self, weak presentingViewController] in guard let self = self else { return } + // the presentingViewController may deinit + guard let _ = presentingViewController else { return } self.hasLoaded = true self.panModalSetNeedsLayoutUpdate() self.panModalTransition(to: .shortForm) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift index fbc221c7a..6d79d0603 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift @@ -14,6 +14,7 @@ import CoreDataStack import FLEX import SwiftUI import MastodonUI +import MastodonSDK extension HomeTimelineViewController { var debugMenu: UIMenu { @@ -27,6 +28,7 @@ extension HomeTimelineViewController { moveMenu, dropMenu, miscMenu, + notificationMenu, UIAction(title: "Settings", image: UIImage(systemName: "gear"), attributes: []) { [weak self] action in guard let self = self else { return } self.showSettings(action) @@ -175,6 +177,25 @@ extension HomeTimelineViewController { ) } + var notificationMenu: UIMenu { + return UIMenu( + title: "Notification…", + image: UIImage(systemName: "bell.badge"), + identifier: nil, + options: [], + children: [ + UIAction(title: "Profile", image: UIImage(systemName: "person.badge.plus"), attributes: []) { [weak self] action in + guard let self = self else { return } + self.showNotification(action, notificationType: .follow) + }, + UIAction(title: "Status", image: UIImage(systemName: "list.bullet.rectangle"), attributes: []) { [weak self] action in + guard let self = self else { return } + self.showNotification(action, notificationType: .mention) + }, + ] + ) + } + } extension HomeTimelineViewController { @@ -412,6 +433,63 @@ extension HomeTimelineViewController { coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) } + private func showNotification(_ sender: UIAction, notificationType: Mastodon.Entity.Notification.NotificationType) { + guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return } + + let alertController = UIAlertController(title: "Enter notification ID", message: nil, preferredStyle: .alert) + alertController.addTextField() + + let showAction = UIAlertAction(title: "Show", style: .default) { [weak self, weak alertController] _ in + guard let self = self else { return } + guard let textField = alertController?.textFields?.first, + let text = textField.text, + let notificationID = Int(text) + else { return } + + let pushNotification = MastodonPushNotification( + _accessToken: authenticationBox.userAuthorization.accessToken, + notificationID: notificationID, + notificationType: notificationType.rawValue, + preferredLocale: nil, + icon: nil, + title: "", + body: "" + ) + self.context.notificationService.requestRevealNotificationPublisher.send(pushNotification) + } + alertController.addAction(showAction) + + // for multiple accounts debug + let boxes = self.context.authenticationService.mastodonAuthenticationBoxes.value // already sorted + if boxes.count >= 2 { + let accessToken = boxes[1].userAuthorization.accessToken + let showForSecondaryAction = UIAlertAction(title: "Show for Secondary", style: .default) { [weak self, weak alertController] _ in + guard let self = self else { return } + guard let textField = alertController?.textFields?.first, + let text = textField.text, + let notificationID = Int(text) + else { return } + + let pushNotification = MastodonPushNotification( + _accessToken: accessToken, + notificationID: notificationID, + notificationType: notificationType.rawValue, + preferredLocale: nil, + icon: nil, + title: "", + body: "" + ) + self.context.notificationService.requestRevealNotificationPublisher.send(pushNotification) + } + alertController.addAction(showForSecondaryAction) + } + + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + alertController.addAction(cancelAction) + + self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil)) + } + @objc private func showSettings(_ sender: UIAction) { guard let currentSetting = context.settingService.currentSetting.value else { return } let settingsViewModel = SettingsViewModel(context: context, setting: currentSetting) diff --git a/Mastodon/Scene/Profile/RemoteProfileViewModel.swift b/Mastodon/Scene/Profile/RemoteProfileViewModel.swift index 153f50998..ef04d5811 100644 --- a/Mastodon/Scene/Profile/RemoteProfileViewModel.swift +++ b/Mastodon/Scene/Profile/RemoteProfileViewModel.swift @@ -7,6 +7,7 @@ import os.log import Foundation +import Combine import CoreDataStack import MastodonSDK @@ -49,4 +50,51 @@ final class RemoteProfileViewModel: ProfileViewModel { .store(in: &disposeBag) } + init(context: AppContext, notificationID: Mastodon.Entity.Notification.ID) { + super.init(context: context, optionalMastodonUser: nil) + + guard let activeMastodonAuthenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { + return + } + let domain = activeMastodonAuthenticationBox.domain + let authorization = activeMastodonAuthenticationBox.userAuthorization + + context.apiService.notification( + notificationID: notificationID, + mastodonAuthenticationBox: activeMastodonAuthenticationBox + ) + .compactMap { [weak self] response -> AnyPublisher, Error>? in + let userID = response.value.account.id + // TODO: use .account directly + return context.apiService.accountInfo( + domain: domain, + userID: userID, + authorization: authorization + ) + } + .switchToLatest() + .retry(3) + .sink { completion in + switch completion { + case .failure(let error): + // TODO: handle error + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: remote notification %s user fetch failed: %s", ((#file as NSString).lastPathComponent), #line, #function, notificationID, error.localizedDescription) + case .finished: + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: remote notification %s user fetched", ((#file as NSString).lastPathComponent), #line, #function, notificationID) + } + } receiveValue: { [weak self] response in + guard let self = self else { return } + let managedObjectContext = context.managedObjectContext + let request = MastodonUser.sortedFetchRequest + request.fetchLimit = 1 + request.predicate = MastodonUser.predicate(domain: domain, id: response.value.id) + guard let mastodonUser = managedObjectContext.safeFetch(request).first else { + assertionFailure() + return + } + self.mastodonUser.value = mastodonUser + } + .store(in: &disposeBag) + } + } diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift index d24a67c4a..d34c85531 100644 --- a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift +++ b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift @@ -226,16 +226,6 @@ extension MainTabBarController { } .store(in: &disposeBag) - context.notificationService.requestRevealNotificationPublisher - .receive(on: DispatchQueue.main) - .sink { [weak self] notificationID in - guard let self = self else { return } - self.coordinator.switchToTabBar(tab: .notification) - let threadViewModel = RemoteThreadViewModel(context: self.context, notificationID: notificationID) - self.coordinator.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show) - } - .store(in: &disposeBag) - layoutAvatarButton() context.authenticationService.activeMastodonAuthentication .receive(on: DispatchQueue.main) diff --git a/Mastodon/Scene/Thread/RemoteThreadViewModel.swift b/Mastodon/Scene/Thread/RemoteThreadViewModel.swift index e6e111018..f8f5d3e7e 100644 --- a/Mastodon/Scene/Thread/RemoteThreadViewModel.swift +++ b/Mastodon/Scene/Thread/RemoteThreadViewModel.swift @@ -48,7 +48,6 @@ final class RemoteThreadViewModel: ThreadViewModel { .store(in: &disposeBag) } - // FIXME: multiple account supports init(context: AppContext, notificationID: Mastodon.Entity.Notification.ID) { super.init(context: context, optionalStatus: nil) diff --git a/Mastodon/Service/AuthenticationService.swift b/Mastodon/Service/AuthenticationService.swift index 0b3c3fa11..9e27caab6 100644 --- a/Mastodon/Service/AuthenticationService.swift +++ b/Mastodon/Service/AuthenticationService.swift @@ -95,8 +95,11 @@ extension AuthenticationService { func activeMastodonUser(domain: String, userID: MastodonUser.ID) -> AnyPublisher, Never> { var isActive = false + var _mastodonAuthentication: MastodonAuthentication? - return backgroundManagedObjectContext.performChanges { + return backgroundManagedObjectContext.performChanges { [weak self] in + guard let self = self else { return } + let request = MastodonAuthentication.sortedFetchRequest request.predicate = MastodonAuthentication.predicate(domain: domain, userID: userID) request.fetchLimit = 1 @@ -104,9 +107,29 @@ extension AuthenticationService { return } mastodonAuthentication.update(activedAt: Date()) + _mastodonAuthentication = mastodonAuthentication isActive = true + } - .map { result in + .receive(on: DispatchQueue.main) + .map { [weak self] result in + switch result { + case .success: + if let self = self, + let mastodonAuthentication = _mastodonAuthentication + { + // force set to avoid delay + self.activeMastodonAuthentication.value = mastodonAuthentication + self.activeMastodonAuthenticationBox.value = MastodonAuthenticationBox( + domain: mastodonAuthentication.domain, + userID: mastodonAuthentication.userID, + appAuthorization: Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.appAccessToken), + userAuthorization: Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.userAccessToken) + ) + } + case .failure: + break + } return result.map { isActive } } .eraseToAnyPublisher() diff --git a/Mastodon/Service/NotificationService.swift b/Mastodon/Service/NotificationService.swift index 463a2def8..6eb3120c7 100644 --- a/Mastodon/Service/NotificationService.swift +++ b/Mastodon/Service/NotificationService.swift @@ -30,7 +30,7 @@ final class NotificationService { /// [Token: NotificationViewModel] let notificationSubscriptionDict: [String: NotificationViewModel] = [:] let unreadNotificationCountDidUpdate = CurrentValueSubject(Void()) - let requestRevealNotificationPublisher = PassthroughSubject() + let requestRevealNotificationPublisher = PassthroughSubject() init( apiService: APIService, diff --git a/Mastodon/Supporting Files/AppDelegate.swift b/Mastodon/Supporting Files/AppDelegate.swift index 192f201d1..87d16241a 100644 --- a/Mastodon/Supporting Files/AppDelegate.swift +++ b/Mastodon/Supporting Files/AppDelegate.swift @@ -109,7 +109,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { completionHandler([.sound]) } - // response to user action for notification + // response to user action for notification (e.g. redirect to post) func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, @@ -125,7 +125,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { let notificationID = String(mastodonPushNotification.notificationID) os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: [Push Notification] notification %s", ((#file as NSString).lastPathComponent), #line, #function, notificationID) appContext.notificationService.handle(mastodonPushNotification: mastodonPushNotification) - appContext.notificationService.requestRevealNotificationPublisher.send(notificationID) + appContext.notificationService.requestRevealNotificationPublisher.send(mastodonPushNotification) completionHandler() } diff --git a/NotificationService/MastodonNotification.swift b/NotificationService/MastodonNotification.swift index f3941b12d..7d6fb034d 100644 --- a/NotificationService/MastodonNotification.swift +++ b/NotificationService/MastodonNotification.swift @@ -9,7 +9,7 @@ import Foundation struct MastodonPushNotification: Codable { - private let _accessToken: String + let _accessToken: String var accessToken: String { return String.normalize(base64String: _accessToken) } @@ -32,4 +32,22 @@ struct MastodonPushNotification: Codable { case body } + public init( + _accessToken: String, + notificationID: Int, + notificationType: String, + preferredLocale: String?, + icon: String?, + title: String, + body: String + ) { + self._accessToken = _accessToken + self.notificationID = notificationID + self.notificationType = notificationType + self.preferredLocale = preferredLocale + self.icon = icon + self.title = title + self.body = body + } + } From cafd4cc3f2ebc9a9a15a201ec3ee785375f94040 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 20:01:54 +0800 Subject: [PATCH 28/95] feat: implement notification for multiple accounts handler --- .../xcschemes/xcschememanagement.plist | 8 ++++---- Mastodon/Coordinator/SceneCoordinator.swift | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 915ce8a0f..21e265788 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 42 + 36 CoreDataStack.xcscheme_^#shared#^_ orderHint - 41 + 35 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 43 + 38 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 44 + 37 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 87e97a536..5df6f7e98 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -94,9 +94,21 @@ final public class SceneCoordinator { guard let self = self else { return } // Note: - // show (push) on phone - // showDetail in .secondary in UISplitViewController on pad - let from = self.splitViewController?.topMost ?? self.tabBarController.topMost + // show (push) on phone or pad (compact) + // showDetail in .secondary in UISplitViewController on pad (expand) + let from: UIViewController? = { + if let splitViewController = self.splitViewController { + if splitViewController.mainTabBarController.topMost?.view.window != nil { + // compact + return splitViewController.mainTabBarController.topMost + } else { + // expand + return splitViewController.viewController(for: .supplementary) + } + } else { + return self.tabBarController.topMost + } + }() // show notification related content guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } From 75bcbdb7a87aa4f9622da0a7a1e60503189a2a50 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 11 Oct 2021 20:03:55 +0800 Subject: [PATCH 29/95] chore: update version to 1.2.0 (74) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 6 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 47 insertions(+), 47 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index d6c057eb7..222a7a1db 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index d6c057eb7..222a7a1db 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index d6c057eb7..222a7a1db 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 7d72f0397..4337bdc7d 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4784,7 +4784,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4813,7 +4813,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4921,11 +4921,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4952,11 +4952,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4981,11 +4981,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5011,11 +5011,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5078,7 +5078,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5103,7 +5103,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5128,7 +5128,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5153,7 +5153,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5178,7 +5178,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5203,7 +5203,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5228,7 +5228,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5253,7 +5253,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5344,7 +5344,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5411,11 +5411,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5460,7 +5460,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5485,11 +5485,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5581,7 +5581,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5648,11 +5648,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5697,7 +5697,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5722,11 +5722,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 73; + DYLIB_CURRENT_VERSION = 74; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5752,7 +5752,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5776,7 +5776,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 73; + CURRENT_PROJECT_VERSION = 74; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 21e265788..beafe8c8e 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 36 + 43 CoreDataStack.xcscheme_^#shared#^_ orderHint - 35 + 44 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 38 + 42 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 37 + 41 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 64cf3e07b..dd63aea2b 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -2,8 +2,6 @@ - UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -32,7 +30,7 @@ CFBundleVersion - 73 + 74 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes @@ -113,5 +111,7 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance + diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 298c2a3d4..6b409db94 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index d6c057eb7..222a7a1db 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index d6c057eb7..222a7a1db 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index b9054fc14..e0a35aca8 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index de78b7103..3cc7eaedd 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 73 + 74 NSExtension NSExtensionAttributes From 8c57da25222f957b212118a89c6212e38433c5cb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 08:07:55 +0200 Subject: [PATCH 30/95] New translations Localizable.stringsdict (Catalan) --- .../input/ca_ES/Localizable.stringsdict | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Localization/StringsConvertor/input/ca_ES/Localizable.stringsdict b/Localization/StringsConvertor/input/ca_ES/Localizable.stringsdict index cc7312938..140185bad 100644 --- a/Localization/StringsConvertor/input/ca_ES/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/ca_ES/Localizable.stringsdict @@ -21,7 +21,7 @@ a11y.plural.count.input_limit_exceeds NSStringLocalizedFormatKey - El límit d’entrada supera a %#@character_count@ + El límit de la entrada supera a %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -37,7 +37,7 @@ a11y.plural.count.input_limit_remains NSStringLocalizedFormatKey - El límit d’entrada continua sent %#@character_count@ + El límit de la entrada continua sent %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -111,7 +111,7 @@ one 1 impuls other - %ld impuls + %ld impulsos plural.count.vote @@ -301,9 +301,9 @@ NSStringFormatValueTypeKey ld one - fa 1a + fa 1 any other - fa %ldy anys + fa %ld anys date.month.ago.abbr @@ -317,9 +317,9 @@ NSStringFormatValueTypeKey ld one - fa 1M + fa 1 mes other - fa %ldM mesos + fa %ld mesos date.day.ago.abbr @@ -333,9 +333,9 @@ NSStringFormatValueTypeKey ld one - fa 1d + fa 1 día other - fa %ldd dies + fa %ld dies date.hour.ago.abbr @@ -351,7 +351,7 @@ one fa 1h other - fa %ldh hores + fa %ld hores date.minute.ago.abbr @@ -365,9 +365,9 @@ NSStringFormatValueTypeKey ld one - fa 1m + fa 1 minut other - fa %ldm minuts + fa %ld minuts date.second.ago.abbr @@ -381,9 +381,9 @@ NSStringFormatValueTypeKey ld one - fa 1s + fa 1 segon other - fa %lds seg + fa %ld segons From 5377adb39fcdea868e1e69e53b0b6277025d6ad9 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 18 Oct 2021 17:41:43 +0800 Subject: [PATCH 31/95] feat: update trends UI with chart --- Mastodon.xcodeproj/project.pbxproj | 4 + ...earchRecommendTagsCollectionViewCell.swift | 59 +++---- .../Search/Search/SearchViewController.swift | 3 - .../Search/Search/View/LineChartView.swift | 151 ++++++++++++++++++ .../Entity/Mastodon+Entity+Tag.swift | 1 + 5 files changed, 182 insertions(+), 36 deletions(-) create mode 100644 Mastodon/Scene/Search/Search/View/LineChartView.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 4337bdc7d..555329be9 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -347,6 +347,7 @@ DB6D9F9726367249008423CD /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D9F9626367249008423CD /* SettingsViewController.swift */; }; DB6F5E35264E78E7009108F4 /* AutoCompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */; }; DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */; }; + DB71C7CB271D5A0300BE3819 /* LineChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71C7CA271D5A0300BE3819 /* LineChartView.swift */; }; DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */; }; DB71FD3625F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD3525F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift */; }; DB71FD3C25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD3B25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift */; }; @@ -1141,6 +1142,7 @@ DB6D9F9626367249008423CD /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteViewController.swift; sourceTree = ""; }; DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteTopChevronView.swift; sourceTree = ""; }; + DB71C7CA271D5A0300BE3819 /* LineChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartView.swift; sourceTree = ""; }; DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarStackContainerButton.swift; sourceTree = ""; }; DB71FD3525F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+PersistMemo.swift"; sourceTree = ""; }; DB71FD3B25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+PersistCache.swift"; sourceTree = ""; }; @@ -1954,6 +1956,7 @@ isa = PBXGroup; children = ( 2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */, + DB71C7CA271D5A0300BE3819 /* LineChartView.swift */, ); path = View; sourceTree = ""; @@ -4162,6 +4165,7 @@ DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */, DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */, 2D34D9CB261489930081BFC0 /* SearchViewController+Recommend.swift in Sources */, + DB71C7CB271D5A0300BE3819 /* LineChartView.swift in Sources */, DB938F1526241FDF00E5B6C1 /* APIService+Thread.swift in Sources */, DB482A45261335BA008AE74C /* UserTimelineViewController+Provider.swift in Sources */, 2D206B8625F5FB0900143C56 /* Double.swift in Sources */, diff --git a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift index e555e1fac..a538d30bf 100644 --- a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift +++ b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift @@ -17,7 +17,7 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { let hashtagTitleLabel: UILabel = { let label = UILabel() - label.textColor = .white + label.textColor = .label label.font = .systemFont(ofSize: 20, weight: .semibold) label.lineBreakMode = .byTruncatingTail return label @@ -25,18 +25,12 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { let peopleLabel: UILabel = { let label = UILabel() - label.textColor = .white + label.textColor = .label label.font = .preferredFont(forTextStyle: .body) return label }() - let flameIconView: UIImageView = { - let imageView = UIImageView() - let image = UIImage(systemName: "flame.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .semibold))!.withRenderingMode(.alwaysTemplate) - imageView.image = image - imageView.tintColor = .white - return imageView - }() + let lineChartView = LineChartView() override func prepareForReuse() { super.prepareForReuse() @@ -54,7 +48,7 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { override var isHighlighted: Bool { didSet { - backgroundColor = isHighlighted ? Asset.Colors.brandBlueDarken20.color : Asset.Colors.brandBlue.color + backgroundColor = isHighlighted ? .systemBackground.withAlphaComponent(0.8) : .systemBackground } } } @@ -68,7 +62,7 @@ extension SearchRecommendTagsCollectionViewCell { } private func configure() { - backgroundColor = Asset.Colors.brandBlue.color + backgroundColor = .systemBackground layer.cornerRadius = 10 layer.cornerCurve = .continuous clipsToBounds = false @@ -98,41 +92,40 @@ extension SearchRecommendTagsCollectionViewCell { containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor) ]) - - - let horizontalStackView = UIStackView() - horizontalStackView.axis = .horizontal - horizontalStackView.translatesAutoresizingMaskIntoConstraints = false - horizontalStackView.distribution = .fill - hashtagTitleLabel.translatesAutoresizingMaskIntoConstraints = false - hashtagTitleLabel.setContentHuggingPriority(.defaultLow - 1, for: .horizontal) - horizontalStackView.addArrangedSubview(hashtagTitleLabel) - horizontalStackView.setContentHuggingPriority(.required - 1, for: .vertical) - - flameIconView.translatesAutoresizingMaskIntoConstraints = false - horizontalStackView.addArrangedSubview(flameIconView) - flameIconView.setContentHuggingPriority(.required - 1, for: .horizontal) - - containerStackView.addArrangedSubview(horizontalStackView) - peopleLabel.translatesAutoresizingMaskIntoConstraints = false - peopleLabel.setContentHuggingPriority(.defaultLow - 1, for: .vertical) + containerStackView.addArrangedSubview(hashtagTitleLabel) containerStackView.addArrangedSubview(peopleLabel) - containerStackView.setCustomSpacing(SearchViewController.hashtagPeopleTalkingLabelTop, after: horizontalStackView) + + lineChartView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(lineChartView) + NSLayoutConstraint.activate([ + lineChartView.topAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 8), + lineChartView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), + contentView.trailingAnchor.constraint(equalTo: lineChartView.trailingAnchor, constant: 16), + contentView.bottomAnchor.constraint(equalTo: lineChartView.bottomAnchor, constant: 16), + ]) } func config(with tag: Mastodon.Entity.Tag) { hashtagTitleLabel.text = "# " + tag.name - guard let historys = tag.history else { + guard let history = tag.history else { peopleLabel.text = "" return } - let recentHistory = historys.prefix(2) + let recentHistory = history.prefix(2) let peopleAreTalking = recentHistory.compactMap({ Int($0.accounts) }).reduce(0, +) let string = L10n.Scene.Search.Recommend.HashTag.peopleTalking(String(peopleAreTalking)) peopleLabel.text = string - + + lineChartView.data = history + .sorted(by: { $0.day < $1.day }) // latest last + .map { entry in + guard let point = Int(entry.accounts) else { + return .zero + } + return CGFloat(point) + } } } diff --git a/Mastodon/Scene/Search/Search/SearchViewController.swift b/Mastodon/Scene/Search/Search/SearchViewController.swift index 8dcf9cd3b..c72945927 100644 --- a/Mastodon/Scene/Search/Search/SearchViewController.swift +++ b/Mastodon/Scene/Search/Search/SearchViewController.swift @@ -23,9 +23,6 @@ final class SearchViewController: UIViewController, NeedsDependency { public static var hashtagCardHeight: CGFloat { get { - if UIScreen.main.bounds.size.height > 736 { - return 186 - } return 130 } } diff --git a/Mastodon/Scene/Search/Search/View/LineChartView.swift b/Mastodon/Scene/Search/Search/View/LineChartView.swift new file mode 100644 index 000000000..5b35bbc4f --- /dev/null +++ b/Mastodon/Scene/Search/Search/View/LineChartView.swift @@ -0,0 +1,151 @@ +// +// LineChartView.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-18. +// + +import UIKit +import Accelerate +import simd + +final class LineChartView: UIView { + + var data: [CGFloat] = [] { + didSet { + setNeedsLayout() + } + } + + let lineShapeLayer = CAShapeLayer() + let gradientLayer = CAGradientLayer() + let dotShapeLayer = CAShapeLayer() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension LineChartView { + private func _init() { + lineShapeLayer.frame = bounds + gradientLayer.frame = bounds + dotShapeLayer.frame = bounds + layer.addSublayer(lineShapeLayer) + layer.addSublayer(gradientLayer) + layer.addSublayer(dotShapeLayer) + + gradientLayer.colors = [ + Asset.Colors.brandBlue.color.withAlphaComponent(0.5).cgColor, + Asset.Colors.brandBlue.color.withAlphaComponent(0).cgColor, + ] + gradientLayer.startPoint = CGPoint(x: 0.5, y: 0) + gradientLayer.endPoint = CGPoint(x: 0.5, y: 1) + } + + override func layoutSubviews() { + super.layoutSubviews() + + lineShapeLayer.frame = bounds + gradientLayer.frame = bounds + dotShapeLayer.frame = bounds + + guard data.count > 1 else { + lineShapeLayer.path = nil + dotShapeLayer.path = nil + gradientLayer.isHidden = true + return + } + gradientLayer.isHidden = false + + // Draw smooth chart + // use vDSP scale the data with line interpolation method + var data = data.map { Float($0) } + // duplicate first and last value to prevent interpolation at edge data + data.insert(data[0], at: 0) + if let last = data.last { + data.append(last) + } + + let n = vDSP_Length(128) + let stride = vDSP_Stride(1) + + // generate fine control with smoothing (simd_smoothstep(_:_:_:)) + let denominator = Float(n) / Float(data.count - 1) + let control: [Float] = (0...n).map { + let x = Float($0) / denominator + return floor(x) + simd_smoothstep(0, 1, simd_fract(x)) + } + + var points = [Float](repeating: 0, count: Int(n)) + vDSP_vlint(data, + control, stride, + &points, stride, + n, + vDSP_Length(data.count)) + + guard let maxDataPoint = data.max() else { + return + } + func calculateY(for point: Float, in frame: CGRect) -> CGFloat { + guard maxDataPoint > 0 else { return .zero } + return (1 - CGFloat(point / maxDataPoint)) * frame.height + } + + let segmentCount = points.count - 1 + let segmentWidth = bounds.width / CGFloat(segmentCount) + + let linePath = UIBezierPath() + let dotPath = UIBezierPath() + + // move to first data point + var x: CGFloat = 0 + let y = calculateY(for: points[0], in: bounds) + linePath.move(to: CGPoint(x: x, y: y)) + for point in points.dropFirst() { + x += segmentWidth + linePath.addLine(to: CGPoint( + x: x, + y: calculateY(for: point, in: bounds) + )) + } + + if let last = points.last { + let y = calculateY(for: last, in: bounds) + let center = CGPoint(x: bounds.maxX, y: y) + dotPath.addArc(withCenter: center, radius: 3, startAngle: 0, endAngle: 2 * .pi, clockwise: true) + } + + // this not works + // linePath.lineJoinStyle = .round + // lineShapeLayer.lineJoin = .round + + lineShapeLayer.lineWidth = 3 + lineShapeLayer.strokeColor = Asset.Colors.brandBlue.color.cgColor + lineShapeLayer.fillColor = UIColor.clear.cgColor + lineShapeLayer.lineCap = .round + lineShapeLayer.path = linePath.cgPath + + let maskPath = UIBezierPath(cgPath: linePath.cgPath) + maskPath.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) + maskPath.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY)) + maskPath.close() + let maskLayer = CAShapeLayer() + maskLayer.path = maskPath.cgPath + maskLayer.fillColor = UIColor.red.cgColor + maskLayer.strokeColor = UIColor.clear.cgColor + maskLayer.lineWidth = 0.0 + gradientLayer.mask = maskLayer + + dotShapeLayer.lineWidth = 3 + dotShapeLayer.fillColor = Asset.Colors.brandBlue.color.cgColor + dotShapeLayer.path = dotPath.cgPath + } +} diff --git a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Tag.swift b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Tag.swift index e7f095eb3..740001572 100644 --- a/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Tag.swift +++ b/MastodonSDK/Sources/MastodonSDK/Entity/Mastodon+Entity+Tag.swift @@ -22,6 +22,7 @@ extension Mastodon.Entity { public let url: String public let history: [History]? + enum CodingKeys: String, CodingKey { case name case url From 68a5c6d4fd7a59d7ffb3c3e8bac9f06b81f01933 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 18 Oct 2021 17:43:44 +0800 Subject: [PATCH 32/95] chore: update version to 1.2.0 (75) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 6 +- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 222a7a1db..aae7af223 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 222a7a1db..aae7af223 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 222a7a1db..aae7af223 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 555329be9..6fc33a114 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4788,7 +4788,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4817,7 +4817,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4925,11 +4925,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4956,11 +4956,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4985,11 +4985,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5015,11 +5015,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5082,7 +5082,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5107,7 +5107,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5132,7 +5132,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5157,7 +5157,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5182,7 +5182,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5207,7 +5207,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5232,7 +5232,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5257,7 +5257,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5348,7 +5348,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5415,11 +5415,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5464,7 +5464,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5489,11 +5489,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5585,7 +5585,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5652,11 +5652,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5701,7 +5701,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5726,11 +5726,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 74; + DYLIB_CURRENT_VERSION = 75; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5756,7 +5756,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5780,7 +5780,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 74; + CURRENT_PROJECT_VERSION = 75; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index beafe8c8e..30ec23f52 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 43 + 41 CoreDataStack.xcscheme_^#shared#^_ orderHint - 44 + 43 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 41 + 44 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index dd63aea2b..44e29d479 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 74 + 75 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 6b409db94..b5cbd7591 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 222a7a1db..aae7af223 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 222a7a1db..aae7af223 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index e0a35aca8..c5b18a9c1 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 3cc7eaedd..c35420f84 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 74 + 75 NSExtension NSExtensionAttributes From faff2004c1110c0710560e6fec652add7000132d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 12:20:39 +0200 Subject: [PATCH 33/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 549 ++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 Localization/StringsConvertor/input/kmr_TR/app.json diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json new file mode 100644 index 000000000..3ec77cf10 --- /dev/null +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -0,0 +1,549 @@ +{ + "common": { + "alerts": { + "common": { + "please_try_again": "Please try again.", + "please_try_again_later": "Please try again later." + }, + "sign_up_failure": { + "title": "Sign Up Failure" + }, + "server_error": { + "title": "Server Error" + }, + "vote_failure": { + "title": "Vote Failure", + "poll_ended": "The poll has ended" + }, + "discard_post_content": { + "title": "Discard Draft", + "message": "Confirm to discard composed post content." + }, + "publish_post_failure": { + "title": "Publish Failure", + "message": "Failed to publish the post.\nPlease check your internet connection.", + "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." + } + }, + "edit_profile_failure": { + "title": "Edit Profile Error", + "message": "Cannot edit profile. Please try again." + }, + "sign_out": { + "title": "Sign Out", + "message": "Are you sure you want to sign out?", + "confirm": "Sign Out" + }, + "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": "Block Domain" + }, + "save_photo_failure": { + "title": "Save Photo Failure", + "message": "Please enable the photo library access permission to save the photo." + }, + "delete_post": { + "title": "Are you sure you want to delete this post?", + "delete": "Delete" + }, + "clean_cache": { + "title": "Clean Cache", + "message": "Successfully cleaned %s cache." + } + }, + "controls": { + "actions": { + "back": "Back", + "next": "Next", + "previous": "Previous", + "open": "Open", + "add": "Add", + "remove": "Remove", + "edit": "Edit", + "save": "Save", + "ok": "OK", + "done": "Done", + "confirm": "Confirm", + "continue": "Continue", + "cancel": "Cancel", + "discard": "Discard", + "try_again": "Try Again", + "take_photo": "Take Photo", + "save_photo": "Save Photo", + "copy_photo": "Copy Photo", + "sign_in": "Sign In", + "sign_up": "Sign Up", + "see_more": "See More", + "preview": "Preview", + "share": "Share", + "share_user": "Share %s", + "share_post": "Share Post", + "open_in_safari": "Open in Safari", + "find_people": "Find people to follow", + "manually_search": "Manually search instead", + "skip": "Skip", + "reply": "Reply", + "report_user": "Report %s", + "block_domain": "Block %s", + "unblock_domain": "Unblock %s", + "settings": "Settings", + "delete": "Delete" + }, + "tabs": { + "home": "Home", + "search": "Search", + "notification": "Notification", + "profile": "Profile" + }, + "keyboard": { + "common": { + "switch_to_tab": "Switch to %s", + "compose_new_post": "Compose New Post", + "show_favorites": "Show Favorites", + "open_settings": "Open Settings" + }, + "timeline": { + "previous_status": "Previous Post", + "next_status": "Next Post", + "open_status": "Open Post", + "open_author_profile": "Open Author's Profile", + "open_reblogger_profile": "Open Reblogger's Profile", + "reply_status": "Reply to Post", + "toggle_reblog": "Toggle Reblog on Post", + "toggle_favorite": "Toggle Favorite on Post", + "toggle_content_warning": "Toggle Content Warning", + "preview_image": "Preview Image" + }, + "segmented_control": { + "previous_section": "Previous Section", + "next_section": "Next Section" + } + }, + "status": { + "user_reblogged": "%s reblogged", + "user_replied_to": "Replied to %s", + "show_post": "Show Post", + "show_user_profile": "Show user profile", + "content_warning": "Content Warning", + "media_content_warning": "Tap anywhere to reveal", + "poll": { + "vote": "Vote", + "closed": "Closed" + }, + "actions": { + "reply": "Reply", + "reblog": "Reblog", + "unreblog": "Undo reblog", + "favorite": "Favorite", + "unfavorite": "Unfavorite", + "menu": "Menu" + }, + "tag": { + "url": "URL", + "mention": "Mention", + "link": "Link", + "hashtag": "Hashtag", + "email": "Email", + "emoji": "Emoji" + } + }, + "friendship": { + "follow": "Follow", + "following": "Following", + "request": "Request", + "pending": "Pending", + "block": "Block", + "block_user": "Block %s", + "block_domain": "Block %s", + "unblock": "Unblock", + "unblock_user": "Unblock %s", + "blocked": "Blocked", + "mute": "Mute", + "mute_user": "Mute %s", + "unmute": "Unmute", + "unmute_user": "Unmute %s", + "muted": "Muted", + "edit_info": "Edit Info" + }, + "timeline": { + "filtered": "Filtered", + "timestamp": { + "now": "Now" + }, + "loader": { + "load_missing_posts": "Load missing posts", + "loading_missing_posts": "Loading missing posts...", + "show_more_replies": "Show more replies" + }, + "header": { + "no_status_found": "No Post 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." + } + } + } + }, + "scene": { + "welcome": { + "slogan": "Social networking\nback in your hands." + }, + "server_picker": { + "title": "Pick a server,\nany server.", + "button": { + "category": { + "all": "All", + "all_accessiblity_description": "Category: All", + "academia": "academia", + "activism": "activism", + "food": "food", + "furry": "furry", + "games": "games", + "general": "general", + "journalism": "journalism", + "lgbt": "lgbt", + "regional": "regional", + "art": "art", + "music": "music", + "tech": "tech" + }, + "see_less": "See Less", + "see_more": "See More" + }, + "label": { + "language": "LANGUAGE", + "users": "USERS", + "category": "CATEGORY" + }, + "input": { + "placeholder": "Find a server or join your own..." + }, + "empty_state": { + "finding_servers": "Finding available servers...", + "bad_network": "Something went wrong while loading the data. Check your internet connection.", + "no_results": "No results" + } + }, + "register": { + "title": "Tell us about you.", + "input": { + "avatar": { + "delete": "Delete" + }, + "username": { + "placeholder": "username", + "duplicate_prompt": "This username is taken." + }, + "display_name": { + "placeholder": "display name" + }, + "email": { + "placeholder": "email" + }, + "password": { + "placeholder": "password", + "hint": "Your password needs at least eight characters" + }, + "invite": { + "registration_user_invite_request": "Why do you want to join?" + } + }, + "error": { + "item": { + "username": "Username", + "email": "Email", + "password": "Password", + "agreement": "Agreement", + "locale": "Locale", + "reason": "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", + "blank": "%s is required", + "invalid": "%s is invalid", + "too_long": "%s is too long", + "too_short": "%s is too short", + "inclusion": "%s is not a supported value" + }, + "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)", + "email_invalid": "This is not a valid email address", + "password_too_short": "Password is too short (must be at least 8 characters)" + } + } + }, + "server_rules": { + "title": "Some ground rules.", + "subtitle": "These rules are set by the admins of %s.", + "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.", + "terms_of_service": "terms of service", + "privacy_policy": "privacy policy", + "button": { + "confirm": "I Agree" + } + }, + "confirm_email": { + "title": "One last thing.", + "subtitle": "We just sent an email to %s,\ntap the link to confirm your account.", + "button": { + "open_email_app": "Open Email App", + "dont_receive_email": "I never got an email" + }, + "dont_receive_email": { + "title": "Check your email", + "description": "Check if your email address is correct as well as your junk folder if you haven’t.", + "resend_email": "Resend Email" + }, + "open_email_app": { + "title": "Check your inbox.", + "description": "We just sent you an email. Check your junk folder if you haven’t.", + "mail": "Mail", + "open_email_client": "Open Email Client" + } + }, + "home_timeline": { + "title": "Home", + "navigation_bar_state": { + "offline": "Offline", + "new_posts": "See new posts", + "published": "Published!", + "Publishing": "Publishing post..." + } + }, + "suggestion_account": { + "title": "Find People to Follow", + "follow_explain": "When you follow someone, you’ll see their posts in your home feed." + }, + "compose": { + "title": { + "new_post": "New Post", + "new_reply": "New Reply" + }, + "media_selection": { + "camera": "Take Photo", + "photo_library": "Photo Library", + "browse": "Browse" + }, + "content_input_placeholder": "Type or paste what’s on your mind", + "compose_action": "Publish", + "replying_to_user": "replying to %s", + "attachment": { + "photo": "photo", + "video": "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..." + }, + "poll": { + "duration_time": "Duration: %s", + "thirty_minutes": "30 minutes", + "one_hour": "1 Hour", + "six_hours": "6 Hours", + "one_day": "1 Day", + "three_days": "3 Days", + "seven_days": "7 Days", + "option_number": "Option %ld" + }, + "content_warning": { + "placeholder": "Write an accurate warning here..." + }, + "visibility": { + "public": "Public", + "unlisted": "Unlisted", + "private": "Followers only", + "direct": "Only people I mention" + }, + "auto_complete": { + "space_to_add": "Space to add" + }, + "accessibility": { + "append_attachment": "Add Attachment", + "append_poll": "Add Poll", + "remove_poll": "Remove Poll", + "custom_emoji_picker": "Custom Emoji Picker", + "enable_content_warning": "Enable Content Warning", + "disable_content_warning": "Disable Content Warning", + "post_visibility_menu": "Post Visibility Menu" + }, + "keyboard": { + "discard_post": "Discard Post", + "publish_post": "Publish Post", + "toggle_poll": "Toggle Poll", + "toggle_content_warning": "Toggle Content Warning", + "append_attachment_entry": "Add Attachment - %s", + "select_visibility_entry": "Select Visibility - %s" + } + }, + "profile": { + "dashboard": { + "posts": "posts", + "following": "following", + "followers": "followers" + }, + "fields": { + "add_row": "Add Row", + "placeholder": { + "label": "Label", + "content": "Content" + } + }, + "segmented_control": { + "posts": "Posts", + "replies": "Replies", + "media": "Media" + }, + "relationship_action_alert": { + "confirm_unmute_user": { + "title": "Unmute Account", + "message": "Confirm to unmute %s" + }, + "confirm_unblock_usre": { + "title": "Unblock Account", + "message": "Confirm to unblock %s" + } + } + }, + "search": { + "title": "Search", + "search_bar": { + "placeholder": "Search hashtags and users", + "cancel": "Cancel" + }, + "recommend": { + "button_text": "See All", + "hash_tag": { + "title": "Trending on Mastodon", + "description": "Hashtags that are getting quite a bit of attention", + "people_talking": "%s people are talking" + }, + "accounts": { + "title": "Accounts you might like", + "description": "You may like to follow these accounts", + "follow": "Follow" + } + }, + "searching": { + "segment": { + "all": "All", + "people": "People", + "hashtags": "Hashtags", + "posts": "Posts" + }, + "empty_state": { + "no_results": "No results" + }, + "recent_search": "Recent searches", + "clear": "Clear" + } + }, + "favorite": { + "title": "Your Favorites" + }, + "notification": { + "title": { + "Everything": "Everything", + "Mentions": "Mentions" + }, + "user_followed_you": "%s followed you", + "user_favorited your post": "%s favorited your post", + "user_reblogged_your_post": "%s reblogged your post", + "user_mentioned_you": "%s mentioned you", + "user_requested_to_follow_you": "%s requested to follow you", + "user_your_poll_has_ended": "%s Your poll has ended", + "keyobard": { + "show_everything": "Show Everything", + "show_mentions": "Show Mentions" + } + }, + "thread": { + "back_title": "Post", + "title": "Post from %s" + }, + "settings": { + "title": "Settings", + "section": { + "appearance": { + "title": "Appearance", + "automatic": "Automatic", + "light": "Always Light", + "dark": "Always Dark" + }, + "notifications": { + "title": "Notifications", + "favorites": "Favorites my post", + "follows": "Follows me", + "boosts": "Reblogs my post", + "mentions": "Mentions me", + "trigger": { + "anyone": "anyone", + "follower": "a follower", + "follow": "anyone I follow", + "noone": "no one", + "title": "Notify me when" + } + }, + "preference": { + "title": "Preferences", + "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" + }, + "boring_zone": { + "title": "The Boring Zone", + "account_settings": "Account Settings", + "terms": "Terms of Service", + "privacy": "Privacy Policy" + }, + "spicy_zone": { + "title": "The Spicy Zone", + "clear": "Clear Media Cache", + "signout": "Sign Out" + } + }, + "footer": { + "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)" + }, + "keyboard": { + "close_settings_window": "Close Settings Window" + } + }, + "report": { + "title": "Report %s", + "step1": "Step 1 of 2", + "step2": "Step 2 of 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": "Send Report", + "skip_to_send": "Send without comment", + "text_placeholder": "Type or paste additional comments" + }, + "preview": { + "keyboard": { + "close_preview": "Close Preview", + "show_next": "Show Next", + "show_previous": "Show Previous" + } + }, + "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" + }, + "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" + } + } +} \ No newline at end of file From 9c5e01149d78703be62c0eefb8a31e0989d71756 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 12:20:40 +0200 Subject: [PATCH 34/95] New translations ios-infoPlist.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/ios-infoPlist.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json diff --git a/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json b/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json new file mode 100644 index 000000000..c6db73de0 --- /dev/null +++ b/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json @@ -0,0 +1,6 @@ +{ + "NSCameraUsageDescription": "Used to take photo for post status", + "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library", + "NewPostShortcutItemTitle": "New Post", + "SearchShortcutItemTitle": "Search" +} From 5dddeb318255bb4a1050823fad6b3e0319d3b0a2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 12:20:41 +0200 Subject: [PATCH 35/95] New translations Localizable.stringsdict (Kurmanji (Kurdish)) --- .../input/kmr_TR/Localizable.stringsdict | 390 ++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict diff --git a/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict new file mode 100644 index 000000000..730e2902a --- /dev/null +++ b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict @@ -0,0 +1,390 @@ + + + + + a11y.plural.count.unread.notification + + NSStringLocalizedFormatKey + %#@notification_count_unread_notification@ + notification_count_unread_notification + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 unread notification + other + %ld unread notification + + + a11y.plural.count.input_limit_exceeds + + NSStringLocalizedFormatKey + Input limit exceeds %#@character_count@ + character_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 character + other + %ld characters + + + a11y.plural.count.input_limit_remains + + NSStringLocalizedFormatKey + Input limit remains %#@character_count@ + character_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 character + other + %ld characters + + + plural.count.metric_formatted.post + + NSStringLocalizedFormatKey + %@ %#@post_count@ + post_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + post + other + posts + + + plural.count.post + + NSStringLocalizedFormatKey + %#@post_count@ + post_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 post + other + %ld posts + + + plural.count.favorite + + NSStringLocalizedFormatKey + %#@favorite_count@ + favorite_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 favorite + other + %ld favorites + + + plural.count.reblog + + NSStringLocalizedFormatKey + %#@reblog_count@ + reblog_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 reblog + other + %ld reblogs + + + plural.count.vote + + NSStringLocalizedFormatKey + %#@vote_count@ + vote_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 vote + other + %ld votes + + + plural.count.voter + + NSStringLocalizedFormatKey + %#@voter_count@ + voter_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 voter + other + %ld voters + + + plural.people_talking + + NSStringLocalizedFormatKey + %#@count_people_talking@ + count_people_talking + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 people talking + other + %ld people talking + + + plural.count.following + + NSStringLocalizedFormatKey + %#@count_following@ + count_following + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 following + other + %ld following + + + plural.count.follower + + NSStringLocalizedFormatKey + %#@count_follower@ + count_follower + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 follower + other + %ld followers + + + date.year.left + + NSStringLocalizedFormatKey + %#@count_year_left@ + count_year_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 year left + other + %ld years left + + + date.month.left + + NSStringLocalizedFormatKey + %#@count_month_left@ + count_month_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 months left + other + %ld months left + + + date.day.left + + NSStringLocalizedFormatKey + %#@count_day_left@ + count_day_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 day left + other + %ld days left + + + date.hour.left + + NSStringLocalizedFormatKey + %#@count_hour_left@ + count_hour_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 hour left + other + %ld hours left + + + date.minute.left + + NSStringLocalizedFormatKey + %#@count_minute_left@ + count_minute_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 minute left + other + %ld minutes left + + + date.second.left + + NSStringLocalizedFormatKey + %#@count_second_left@ + count_second_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 second left + other + %ld seconds left + + + date.year.ago.abbr + + NSStringLocalizedFormatKey + %#@count_year_ago_abbr@ + count_year_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1y ago + other + %ldy ago + + + date.month.ago.abbr + + NSStringLocalizedFormatKey + %#@count_month_ago_abbr@ + count_month_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1M ago + other + %ldM ago + + + date.day.ago.abbr + + NSStringLocalizedFormatKey + %#@count_day_ago_abbr@ + count_day_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1d ago + other + %ldd ago + + + date.hour.ago.abbr + + NSStringLocalizedFormatKey + %#@count_hour_ago_abbr@ + count_hour_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1h ago + other + %ldh ago + + + date.minute.ago.abbr + + NSStringLocalizedFormatKey + %#@count_minute_ago_abbr@ + count_minute_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1m ago + other + %ldm ago + + + date.second.ago.abbr + + NSStringLocalizedFormatKey + %#@count_second_ago_abbr@ + count_second_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1s ago + other + %lds ago + + + + From 7aa634cce8faf9eac3de98040cf56da4a5a68166 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 12:20:42 +0200 Subject: [PATCH 36/95] New translations Intents.strings (Kurmanji (Kurdish)) --- .../Intents/input/kmr_TR/Intents.strings | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings new file mode 100644 index 000000000..6877490ba --- /dev/null +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings @@ -0,0 +1,51 @@ +"16wxgf" = "Post on Mastodon"; + +"751xkl" = "Text Content"; + +"CsR7G2" = "Post on Mastodon"; + +"HZSGTr" = "What content to post?"; + +"HdGikU" = "Posting failed"; + +"KDNTJ4" = "Failure Reason"; + +"RHxKOw" = "Send Post with text content"; + +"RxSqsb" = "Post"; + +"WCIR3D" = "Post ${content} on Mastodon"; + +"ZKJSNu" = "Post"; + +"ZS1XaK" = "${content}"; + +"ZbSjzC" = "Visibility"; + +"Zo4jgJ" = "Post Visibility"; + +"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’."; + +"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’."; + +"ayoYEb-dYQ5NN" = "${content}, Public"; + +"ayoYEb-ehFLjY" = "${content}, Followers Only"; + +"dUyuGg" = "Post on Mastodon"; + +"dYQ5NN" = "Public"; + +"ehFLjY" = "Followers Only"; + +"gfePDu" = "Posting failed. ${failureReason}"; + +"k7dbKQ" = "Post was sent successfully."; + +"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?"; + +"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?"; + +"rM6dvp" = "URL"; + +"ryJLwG" = "Post was sent successfully. "; From 86926a360bd172126822313139ad546a26d8ac53 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 12:20:43 +0200 Subject: [PATCH 37/95] New translations Intents.stringsdict (Kurmanji (Kurdish)) --- .../Intents/input/kmr_TR/Intents.stringsdict | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict new file mode 100644 index 000000000..18422c772 --- /dev/null +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict @@ -0,0 +1,38 @@ + + + + + There are ${count} options matching ‘${content}’. - 2 + + NSStringLocalizedFormatKey + There are %#@count_option@ matching ‘${content}’. + count_option + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + %ld + one + 1 option + other + %ld options + + + There are ${count} options matching ‘${visibility}’. + + NSStringLocalizedFormatKey + There are %#@count_option@ matching ‘${visibility}’. + count_option + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + %ld + one + 1 option + other + %ld options + + + + From 0f55d80e2031ac737acdc6e6bbcdbc54c51655a8 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 18 Oct 2021 18:35:19 +0800 Subject: [PATCH 38/95] chore: update trends line chart style --- Mastodon.xcodeproj/project.pbxproj | 4 ++ .../Search/Search/View/LineChartView.swift | 60 +++++-------------- Mastodon/Vender/CurveAlgorithm.swift | 47 +++++++++++++++ 3 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 Mastodon/Vender/CurveAlgorithm.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 6fc33a114..e1586f23f 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -348,6 +348,7 @@ DB6F5E35264E78E7009108F4 /* AutoCompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */; }; DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */; }; DB71C7CB271D5A0300BE3819 /* LineChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71C7CA271D5A0300BE3819 /* LineChartView.swift */; }; + DB71C7CD271D7F4300BE3819 /* CurveAlgorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71C7CC271D7F4300BE3819 /* CurveAlgorithm.swift */; }; DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */; }; DB71FD3625F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD3525F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift */; }; DB71FD3C25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD3B25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift */; }; @@ -1143,6 +1144,7 @@ DB6F5E34264E78E7009108F4 /* AutoCompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteViewController.swift; sourceTree = ""; }; DB6F5E37264E994A009108F4 /* AutoCompleteTopChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCompleteTopChevronView.swift; sourceTree = ""; }; DB71C7CA271D5A0300BE3819 /* LineChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineChartView.swift; sourceTree = ""; }; + DB71C7CC271D7F4300BE3819 /* CurveAlgorithm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurveAlgorithm.swift; sourceTree = ""; }; DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarStackContainerButton.swift; sourceTree = ""; }; DB71FD3525F8A16C00512AE1 /* APIService+Persist+PersistMemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+PersistMemo.swift"; sourceTree = ""; }; DB71FD3B25F8A1C500512AE1 /* APIService+Persist+PersistCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Persist+PersistCache.swift"; sourceTree = ""; }; @@ -1752,6 +1754,7 @@ 2D5A3D0125CF8640002347D6 /* Vender */ = { isa = PBXGroup; children = ( + DB71C7CC271D7F4300BE3819 /* CurveAlgorithm.swift */, 2D5A3D0225CF8742002347D6 /* ControlContainableScrollViews.swift */, DB51D170262832380062B7A1 /* BlurHashDecode.swift */, DB51D171262832380062B7A1 /* BlurHashEncode.swift */, @@ -4234,6 +4237,7 @@ DB8AF52E25C13561002E6C99 /* ViewStateStore.swift in Sources */, DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */, 2DA7D04A25CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift in Sources */, + DB71C7CD271D7F4300BE3819 /* CurveAlgorithm.swift in Sources */, DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */, DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */, DBAC6499267DF2C4007FE9FD /* TimelineBottomLoaderNode.swift in Sources */, diff --git a/Mastodon/Scene/Search/Search/View/LineChartView.swift b/Mastodon/Scene/Search/Search/View/LineChartView.swift index 5b35bbc4f..51164034e 100644 --- a/Mastodon/Scene/Search/Search/View/LineChartView.swift +++ b/Mastodon/Scene/Search/Search/View/LineChartView.swift @@ -66,61 +66,33 @@ extension LineChartView { gradientLayer.isHidden = false // Draw smooth chart - // use vDSP scale the data with line interpolation method - var data = data.map { Float($0) } - // duplicate first and last value to prevent interpolation at edge data - data.insert(data[0], at: 0) - if let last = data.last { - data.append(last) - } - - let n = vDSP_Length(128) - let stride = vDSP_Stride(1) - - // generate fine control with smoothing (simd_smoothstep(_:_:_:)) - let denominator = Float(n) / Float(data.count - 1) - let control: [Float] = (0...n).map { - let x = Float($0) / denominator - return floor(x) + simd_smoothstep(0, 1, simd_fract(x)) - } - - var points = [Float](repeating: 0, count: Int(n)) - vDSP_vlint(data, - control, stride, - &points, stride, - n, - vDSP_Length(data.count)) - guard let maxDataPoint = data.max() else { return } - func calculateY(for point: Float, in frame: CGRect) -> CGFloat { + func calculateY(for point: CGFloat, in frame: CGRect) -> CGFloat { guard maxDataPoint > 0 else { return .zero } - return (1 - CGFloat(point / maxDataPoint)) * frame.height + return (1 - point / maxDataPoint) * frame.height } - let segmentCount = points.count - 1 + let segmentCount = data.count - 1 let segmentWidth = bounds.width / CGFloat(segmentCount) - let linePath = UIBezierPath() + let points: [CGPoint] = { + var points: [CGPoint] = [] + var x: CGFloat = 0 + for value in data { + let point = CGPoint(x: x, y: calculateY(for: value, in: bounds)) + points.append(point) + x += segmentWidth + } + return points + }() + + guard let linePath = CurveAlgorithm.shared.createCurvedPath(points) else { return } let dotPath = UIBezierPath() - // move to first data point - var x: CGFloat = 0 - let y = calculateY(for: points[0], in: bounds) - linePath.move(to: CGPoint(x: x, y: y)) - for point in points.dropFirst() { - x += segmentWidth - linePath.addLine(to: CGPoint( - x: x, - y: calculateY(for: point, in: bounds) - )) - } - if let last = points.last { - let y = calculateY(for: last, in: bounds) - let center = CGPoint(x: bounds.maxX, y: y) - dotPath.addArc(withCenter: center, radius: 3, startAngle: 0, endAngle: 2 * .pi, clockwise: true) + dotPath.addArc(withCenter: last, radius: 3, startAngle: 0, endAngle: 2 * .pi, clockwise: true) } // this not works diff --git a/Mastodon/Vender/CurveAlgorithm.swift b/Mastodon/Vender/CurveAlgorithm.swift new file mode 100644 index 000000000..00db06048 --- /dev/null +++ b/Mastodon/Vender/CurveAlgorithm.swift @@ -0,0 +1,47 @@ +// +// CurveAlgorithm.swift +// +// Ref: https://github.com/nhatminh12369/LineChart/blob/master/LineChart/CurveAlgorithm.swift + +import UIKit + +struct CurvedSegment { + var controlPoint1: CGPoint + var controlPoint2: CGPoint +} + +class CurveAlgorithm { + static let shared = CurveAlgorithm() + + private func controlPointsFrom(points: [CGPoint]) -> [CurvedSegment] { + var result: [CurvedSegment] = [] + + let delta: CGFloat = 0.4 + + // only use horizontal control point + for i in 1.. UIBezierPath? { + let path = UIBezierPath() + path.move(to: dataPoints[0]) + + var curveSegments: [CurvedSegment] = [] + curveSegments = controlPointsFrom(points: dataPoints) + + for i in 1.. Date: Mon, 18 Oct 2021 18:49:15 +0800 Subject: [PATCH 39/95] chore: update version to 1.2.0 (76) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index aae7af223..d4f88f0be 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index aae7af223..d4f88f0be 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index aae7af223..d4f88f0be 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index e1586f23f..a4fe60399 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4792,7 +4792,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4821,7 +4821,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4929,11 +4929,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4960,11 +4960,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4989,11 +4989,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5019,11 +5019,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5086,7 +5086,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5111,7 +5111,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5136,7 +5136,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5161,7 +5161,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5186,7 +5186,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5211,7 +5211,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5236,7 +5236,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5261,7 +5261,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5352,7 +5352,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5419,11 +5419,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5468,7 +5468,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5493,11 +5493,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5589,7 +5589,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5656,11 +5656,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5705,7 +5705,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5730,11 +5730,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 75; + DYLIB_CURRENT_VERSION = 76; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5760,7 +5760,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5784,7 +5784,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 76; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 30ec23f52..751f88d54 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 41 + 53 CoreDataStack.xcscheme_^#shared#^_ orderHint - 43 + 50 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 42 + 52 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 44 + 51 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 44e29d479..5a0b24aec 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 75 + 76 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index b5cbd7591..5a2f9e2ff 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index aae7af223..d4f88f0be 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index aae7af223..d4f88f0be 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index c5b18a9c1..ee858ed64 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index c35420f84..a7ea5b685 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 75 + 76 NSExtension NSExtensionAttributes From 3e9290c5a608687f1212d623d33a456c4040d3e7 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 18 Oct 2021 19:00:55 +0800 Subject: [PATCH 40/95] chore: set path line join style --- Mastodon/Scene/Search/Search/View/LineChartView.swift | 7 ++----- Mastodon/Vender/CurveAlgorithm.swift | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Mastodon/Scene/Search/Search/View/LineChartView.swift b/Mastodon/Scene/Search/Search/View/LineChartView.swift index 51164034e..2fb8ac733 100644 --- a/Mastodon/Scene/Search/Search/View/LineChartView.swift +++ b/Mastodon/Scene/Search/Search/View/LineChartView.swift @@ -94,14 +94,11 @@ extension LineChartView { if let last = points.last { dotPath.addArc(withCenter: last, radius: 3, startAngle: 0, endAngle: 2 * .pi, clockwise: true) } - - // this not works - // linePath.lineJoinStyle = .round - // lineShapeLayer.lineJoin = .round - + lineShapeLayer.lineWidth = 3 lineShapeLayer.strokeColor = Asset.Colors.brandBlue.color.cgColor lineShapeLayer.fillColor = UIColor.clear.cgColor + lineShapeLayer.lineJoin = .round lineShapeLayer.lineCap = .round lineShapeLayer.path = linePath.cgPath diff --git a/Mastodon/Vender/CurveAlgorithm.swift b/Mastodon/Vender/CurveAlgorithm.swift index 00db06048..0ca4c8734 100644 --- a/Mastodon/Vender/CurveAlgorithm.swift +++ b/Mastodon/Vender/CurveAlgorithm.swift @@ -16,7 +16,7 @@ class CurveAlgorithm { private func controlPointsFrom(points: [CGPoint]) -> [CurvedSegment] { var result: [CurvedSegment] = [] - let delta: CGFloat = 0.4 + let delta: CGFloat = 0.2 // only use horizontal control point for i in 1.. Date: Mon, 18 Oct 2021 19:01:49 +0800 Subject: [PATCH 41/95] chore: update version to 1.2.0 (77) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index d4f88f0be..009534d8f 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index d4f88f0be..009534d8f 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index d4f88f0be..009534d8f 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index a4fe60399..dab0ea3cc 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4792,7 +4792,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4821,7 +4821,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4929,11 +4929,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4960,11 +4960,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4989,11 +4989,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5019,11 +5019,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5086,7 +5086,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5111,7 +5111,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5136,7 +5136,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5161,7 +5161,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5186,7 +5186,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5211,7 +5211,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5236,7 +5236,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5261,7 +5261,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5352,7 +5352,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5419,11 +5419,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5468,7 +5468,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5493,11 +5493,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5589,7 +5589,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5656,11 +5656,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5705,7 +5705,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5730,11 +5730,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 76; + DYLIB_CURRENT_VERSION = 77; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5760,7 +5760,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5784,7 +5784,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 76; + CURRENT_PROJECT_VERSION = 77; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 751f88d54..e6f225e6d 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 53 + 54 CoreDataStack.xcscheme_^#shared#^_ orderHint - 50 + 53 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 52 + 55 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 51 + 56 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 5a0b24aec..a371b7f41 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 76 + 77 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 5a2f9e2ff..9179ceb0c 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index d4f88f0be..009534d8f 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index d4f88f0be..009534d8f 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index ee858ed64..642d2f561 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index a7ea5b685..fa46a9679 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 76 + 77 NSExtension NSExtensionAttributes From 107e6437174738c8e5b125790dfd73ce409c8254 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 13:33:27 +0200 Subject: [PATCH 42/95] New translations Intents.strings (Kurmanji (Kurdish)) --- .../Intents/input/kmr_TR/Intents.strings | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings index 6877490ba..944f91c46 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings @@ -1,8 +1,8 @@ -"16wxgf" = "Post on Mastodon"; +"16wxgf" = "Di Mastodon de biweşîne"; -"751xkl" = "Text Content"; +"751xkl" = "Naveroka nivîsê"; -"CsR7G2" = "Post on Mastodon"; +"CsR7G2" = "Di Mastodon de biweşîne"; "HZSGTr" = "What content to post?"; @@ -12,17 +12,17 @@ "RHxKOw" = "Send Post with text content"; -"RxSqsb" = "Post"; +"RxSqsb" = "Şandî"; -"WCIR3D" = "Post ${content} on Mastodon"; +"WCIR3D" = "${content} biweşîne di Mastodon de"; -"ZKJSNu" = "Post"; +"ZKJSNu" = "Şandî"; "ZS1XaK" = "${content}"; -"ZbSjzC" = "Visibility"; +"ZbSjzC" = "Xuyanî"; -"Zo4jgJ" = "Post Visibility"; +"Zo4jgJ" = "Xuyaniya şandiyê"; "apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’."; From cda40437e053ce0f819b8d56da8cc3b82224cd8c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 18 Oct 2021 13:33:28 +0200 Subject: [PATCH 43/95] New translations Intents.stringsdict (Kurmanji (Kurdish)) --- .../StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict index 18422c772..fb10126c0 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict @@ -13,9 +13,9 @@ NSStringFormatValueTypeKey %ld one - 1 option + 1 vebijêrk other - %ld options + %ld vebijêrk There are ${count} options matching ‘${visibility}’. From 78a50049984ff2e34561e3e1e100b69ef6f15e5e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 03:24:14 +0200 Subject: [PATCH 44/95] New translations Localizable.stringsdict (Kurmanji (Kurdish)) --- .../input/kmr_TR/Localizable.stringsdict | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict index 730e2902a..8efdda094 100644 --- a/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict @@ -13,15 +13,15 @@ NSStringFormatValueTypeKey ld one - 1 unread notification + 1 agahdariya nexwendî other - %ld unread notification + %ld agahdariyên nexwendî a11y.plural.count.input_limit_exceeds NSStringLocalizedFormatKey - Input limit exceeds %#@character_count@ + Sînorê têketinê derbas kir %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -61,9 +61,9 @@ NSStringFormatValueTypeKey ld one - post + şandî other - posts + şandî plural.count.post @@ -77,9 +77,9 @@ NSStringFormatValueTypeKey ld one - 1 post + 1 şandî other - %ld posts + %ld şandî plural.count.favorite @@ -93,9 +93,9 @@ NSStringFormatValueTypeKey ld one - 1 favorite + 1 hezkirin other - %ld favorites + %ld hezkirin plural.count.reblog @@ -125,9 +125,9 @@ NSStringFormatValueTypeKey ld one - 1 vote + 1 deng other - %ld votes + %ld deng plural.count.voter @@ -141,9 +141,9 @@ NSStringFormatValueTypeKey ld one - 1 voter + 1 hilbijêr other - %ld voters + %ld hilbijêr plural.people_talking @@ -301,9 +301,9 @@ NSStringFormatValueTypeKey ld one - 1y ago + 1 sal berê other - %ldy ago + %ld sal berê date.month.ago.abbr @@ -317,9 +317,9 @@ NSStringFormatValueTypeKey ld one - 1M ago + 1 xulek berê other - %ldM ago + %ld xulek berê date.day.ago.abbr @@ -333,9 +333,9 @@ NSStringFormatValueTypeKey ld one - 1d ago + 1 roj berê other - %ldd ago + %ld roj berê date.hour.ago.abbr @@ -349,9 +349,9 @@ NSStringFormatValueTypeKey ld one - 1h ago + 1 demjimêr berê other - %ldh ago + %ld demjimêr berê date.minute.ago.abbr @@ -365,9 +365,9 @@ NSStringFormatValueTypeKey ld one - 1m ago + 1 xulek berê other - %ldm ago + %ld xulek berê date.second.ago.abbr @@ -381,9 +381,9 @@ NSStringFormatValueTypeKey ld one - 1s ago + 1 çirke berê other - %lds ago + %ld çirke berê From 35af570bdbdccf5fc524e96643e57cae350bc0cd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 07:10:52 +0200 Subject: [PATCH 45/95] New translations ios-infoPlist.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/ios-infoPlist.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json b/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json index c6db73de0..cdb286c00 100644 --- a/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json +++ b/Localization/StringsConvertor/input/kmr_TR/ios-infoPlist.json @@ -1,6 +1,6 @@ { - "NSCameraUsageDescription": "Used to take photo for post status", - "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library", - "NewPostShortcutItemTitle": "New Post", - "SearchShortcutItemTitle": "Search" + "NSCameraUsageDescription": "Bo kişandina wêneyê ji bo rewşa şandiyan tê bikaranîn", + "NSPhotoLibraryAddUsageDescription": "Ji bo tomarkirina wêneyê di pirtûkxaneya wêneyan de tê bikaranîn", + "NewPostShortcutItemTitle": "Şandiya nû", + "SearchShortcutItemTitle": "Bigere" } From ce23fa4b78f83e2f9e9260257d39e9a7d55b0766 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 07:10:54 +0200 Subject: [PATCH 46/95] New translations Localizable.stringsdict (Kurmanji (Kurdish)) --- .../input/kmr_TR/Localizable.stringsdict | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict index 8efdda094..064b8bf2b 100644 --- a/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/kmr_TR/Localizable.stringsdict @@ -29,15 +29,15 @@ NSStringFormatValueTypeKey ld one - 1 character + 1 tîp other - %ld characters + %ld tîp a11y.plural.count.input_limit_remains NSStringLocalizedFormatKey - Input limit remains %#@character_count@ + Sînorê têketinê %#@character_count@ maye character_count NSStringFormatSpecTypeKey @@ -45,9 +45,9 @@ NSStringFormatValueTypeKey ld one - 1 character + 1 tîp other - %ld characters + %ld tîp plural.count.metric_formatted.post @@ -157,9 +157,9 @@ NSStringFormatValueTypeKey ld one - 1 people talking + 1 mirov diaxive other - %ld people talking + %ld mirov diaxive plural.count.following @@ -173,9 +173,9 @@ NSStringFormatValueTypeKey ld one - 1 following + 1 dişopîne other - %ld following + %ld dişopîne plural.count.follower @@ -189,9 +189,9 @@ NSStringFormatValueTypeKey ld one - 1 follower + 1 şopîner other - %ld followers + %ld şopîner date.year.left @@ -205,9 +205,9 @@ NSStringFormatValueTypeKey ld one - 1 year left + 1 sal berê other - %ld years left + %ld sal berê date.month.left @@ -221,9 +221,9 @@ NSStringFormatValueTypeKey ld one - 1 months left + 1 meh berê other - %ld months left + %ld meh berê date.day.left @@ -237,9 +237,9 @@ NSStringFormatValueTypeKey ld one - 1 day left + 1 roj berê other - %ld days left + %ld roj berê date.hour.left @@ -253,9 +253,9 @@ NSStringFormatValueTypeKey ld one - 1 hour left + 1 demjimêr berê other - %ld hours left + %ld demjimêr berê date.minute.left @@ -269,9 +269,9 @@ NSStringFormatValueTypeKey ld one - 1 minute left + 1 xulek berê other - %ld minutes left + %ld xulek berê date.second.left @@ -285,9 +285,9 @@ NSStringFormatValueTypeKey ld one - 1 second left + 1 çirke berê other - %ld seconds left + %ld çirke berê date.year.ago.abbr From 4e3275120ebafe0541ea0adf31ef164f3d06c5c4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 19:19:48 +0200 Subject: [PATCH 47/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 3ec77cf10..6dffb38bb 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -46,7 +46,7 @@ }, "delete_post": { "title": "Are you sure you want to delete this post?", - "delete": "Delete" + "delete": "Jê bibe" }, "clean_cache": { "title": "Clean Cache", @@ -55,31 +55,31 @@ }, "controls": { "actions": { - "back": "Back", - "next": "Next", - "previous": "Previous", - "open": "Open", - "add": "Add", - "remove": "Remove", - "edit": "Edit", - "save": "Save", - "ok": "OK", - "done": "Done", - "confirm": "Confirm", - "continue": "Continue", - "cancel": "Cancel", - "discard": "Discard", - "try_again": "Try Again", + "back": "Vegere", + "next": "Pêş", + "previous": "Paş", + "open": "Veke", + "add": "Tevlî bike", + "remove": "Rake", + "edit": "Serrast bike", + "save": "Tomar bike", + "ok": "BAŞ E", + "done": "Qediya", + "confirm": "Bipejirîne", + "continue": "Bidomîne", + "cancel": "Dev jê berde", + "discard": "Biavêje", + "try_again": "Dîsa biceribîne", "take_photo": "Take Photo", "save_photo": "Save Photo", "copy_photo": "Copy Photo", - "sign_in": "Sign In", - "sign_up": "Sign Up", - "see_more": "See More", - "preview": "Preview", - "share": "Share", - "share_user": "Share %s", - "share_post": "Share Post", + "sign_in": "Têkeve", + "sign_up": "Tomar bibe", + "see_more": "Bêtir bibîne", + "preview": "Pêşdîtin", + "share": "Parve bike", + "share_user": "%s parve bike", + "share_post": "Şandiyê parve bike", "open_in_safari": "Open in Safari", "find_people": "Find people to follow", "manually_search": "Manually search instead", @@ -110,7 +110,7 @@ "open_status": "Open Post", "open_author_profile": "Open Author's Profile", "open_reblogger_profile": "Open Reblogger's Profile", - "reply_status": "Reply to Post", + "reply_status": "Bersivê bide şandiyê", "toggle_reblog": "Toggle Reblog on Post", "toggle_favorite": "Toggle Favorite on Post", "toggle_content_warning": "Toggle Content Warning", From a6a782517668a7a43645a13a457b2c28746b4e86 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 19:19:49 +0200 Subject: [PATCH 48/95] New translations Intents.strings (Kurmanji (Kurdish)) --- .../StringsConvertor/Intents/input/kmr_TR/Intents.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings index 944f91c46..22b6300dc 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings @@ -34,9 +34,9 @@ "dUyuGg" = "Post on Mastodon"; -"dYQ5NN" = "Public"; +"dYQ5NN" = "Gelemperî"; -"ehFLjY" = "Followers Only"; +"ehFLjY" = "Tenê şopîneran"; "gfePDu" = "Posting failed. ${failureReason}"; @@ -46,6 +46,6 @@ "oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?"; -"rM6dvp" = "URL"; +"rM6dvp" = "Girêdan"; "ryJLwG" = "Post was sent successfully. "; From 301a53f2228276478bdc6c9e118e7bd3b0c9fba2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 20:20:38 +0200 Subject: [PATCH 49/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 6dffb38bb..09f992590 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -2,50 +2,50 @@ "common": { "alerts": { "common": { - "please_try_again": "Please try again.", - "please_try_again_later": "Please try again later." + "please_try_again": "Ji kerema xwe dîsa biceribîne.", + "please_try_again_later": "Ji kerema xwe paşê dîsa biceribîne." }, "sign_up_failure": { - "title": "Sign Up Failure" + "title": "Tomarkirin têkçû" }, "server_error": { - "title": "Server Error" + "title": "Çewtiya rajekar" }, "vote_failure": { - "title": "Vote Failure", - "poll_ended": "The poll has ended" + "title": "Dengdayîn têkçû", + "poll_ended": "Rapirsîya qediya" }, "discard_post_content": { "title": "Discard Draft", "message": "Confirm to discard composed post content." }, "publish_post_failure": { - "title": "Publish Failure", - "message": "Failed to publish the post.\nPlease check your internet connection.", + "title": "Weşandin têkçû", + "message": "Weşandina şandiyê têkçû.\nJkx girêdana înternetê xwe kontrol bike.", "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": "Nikare vîdyoyekê tevlî şandiyê ku berê wêne tê de heye bike.", + "more_than_one_video": "Nikare ji bêtirî yek vîdyoyekê tevlî şandiyê bike." } }, "edit_profile_failure": { "title": "Edit Profile Error", - "message": "Cannot edit profile. Please try again." + "message": "Nikare profîlê serrast bike. Jkx dîsa biceribîne." }, "sign_out": { - "title": "Sign Out", - "message": "Are you sure you want to sign out?", - "confirm": "Sign Out" + "title": "Derkeve", + "message": "Ma tu dixwazî ku derkevî?", + "confirm": "Derkeve" }, "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": "Block Domain" + "title": "Tu ji xwe bawerî, bi rastî tu dixwazî hemû %s 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.", + "block_entire_domain": "Navperê asteng bike" }, "save_photo_failure": { - "title": "Save Photo Failure", + "title": "Tomarkirina wêneyê têkçû", "message": "Please enable the photo library access permission to save the photo." }, "delete_post": { - "title": "Are you sure you want to delete this post?", + "title": "Ma tu dixwazî vê şandiyê jê bibî?", "delete": "Jê bibe" }, "clean_cache": { @@ -80,35 +80,35 @@ "share": "Parve bike", "share_user": "%s parve bike", "share_post": "Şandiyê parve bike", - "open_in_safari": "Open in Safari", + "open_in_safari": "Di Safariyê de veke", "find_people": "Find people to follow", - "manually_search": "Manually search instead", - "skip": "Skip", - "reply": "Reply", - "report_user": "Report %s", - "block_domain": "Block %s", - "unblock_domain": "Unblock %s", - "settings": "Settings", - "delete": "Delete" + "manually_search": "Ji devlê i destan lêgerînê bike", + "skip": "Derbas bike", + "reply": "Bersivê bide", + "report_user": "%s ragihîne", + "block_domain": "%s asteng bike", + "unblock_domain": "%s asteng neke", + "settings": "Sazkarî", + "delete": "Jê bibe" }, "tabs": { - "home": "Home", - "search": "Search", - "notification": "Notification", - "profile": "Profile" + "home": "Serrûpel", + "search": "Bigere", + "notification": "Agahdarî", + "profile": "Profîl" }, "keyboard": { "common": { - "switch_to_tab": "Switch to %s", - "compose_new_post": "Compose New Post", - "show_favorites": "Show Favorites", - "open_settings": "Open Settings" + "switch_to_tab": "Biguherîne bo %s", + "compose_new_post": "Şandiyeke nû binivsîne", + "show_favorites": "Bijarteyan nîşan bide", + "open_settings": "Sazkariyan Veke" }, "timeline": { - "previous_status": "Previous Post", - "next_status": "Next Post", - "open_status": "Open Post", - "open_author_profile": "Open Author's Profile", + "previous_status": "Şandeya paş", + "next_status": "Şandiya pêş", + "open_status": "Şandiyê veke", + "open_author_profile": "Profîla nivîskaran veke", "open_reblogger_profile": "Open Reblogger's Profile", "reply_status": "Bersivê bide şandiyê", "toggle_reblog": "Toggle Reblog on Post", @@ -124,8 +124,8 @@ "status": { "user_reblogged": "%s reblogged", "user_replied_to": "Replied to %s", - "show_post": "Show Post", - "show_user_profile": "Show user profile", + "show_post": "Şandiyê nîşan bide", + "show_user_profile": "Profîla bikarhêner nîşan bide", "content_warning": "Content Warning", "media_content_warning": "Tap anywhere to reveal", "poll": { @@ -152,33 +152,33 @@ "friendship": { "follow": "Follow", "following": "Following", - "request": "Request", - "pending": "Pending", - "block": "Block", - "block_user": "Block %s", - "block_domain": "Block %s", - "unblock": "Unblock", - "unblock_user": "Unblock %s", - "blocked": "Blocked", - "mute": "Mute", - "mute_user": "Mute %s", - "unmute": "Unmute", - "unmute_user": "Unmute %s", + "request": "Daxwazên şopandinê", + "pending": "Tê nirxandin", + "block": "Asteng bike", + "block_user": "%s asteng bike", + "block_domain": "%s asteng bike", + "unblock": "Astengiyê rake", + "unblock_user": "%s asteng neke", + "blocked": "Astengkirî", + "mute": "Bêdeng bike", + "mute_user": "%s bêdeng bike", + "unmute": "Bêdeng neke", + "unmute_user": "%s bêdeng neke", "muted": "Muted", - "edit_info": "Edit Info" + "edit_info": "Zanyariyan serrast bike" }, "timeline": { "filtered": "Filtered", "timestamp": { - "now": "Now" + "now": "Niha" }, "loader": { "load_missing_posts": "Load missing posts", "loading_missing_posts": "Loading missing posts...", - "show_more_replies": "Show more replies" + "show_more_replies": "Bêtir bersivan nîşan bide" }, "header": { - "no_status_found": "No Post Found", + "no_status_found": "Şandî nehate dîtin", "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.", @@ -197,15 +197,15 @@ "title": "Pick a server,\nany server.", "button": { "category": { - "all": "All", - "all_accessiblity_description": "Category: All", - "academia": "academia", - "activism": "activism", - "food": "food", + "all": "Hemû", + "all_accessiblity_description": "Beş: Hemû", + "academia": "akademî", + "activism": "çalakî", + "food": "xwarin", "furry": "furry", - "games": "games", - "general": "general", - "journalism": "journalism", + "games": "lîsk", + "general": "giştî", + "journalism": "rojnamevanî", "lgbt": "lgbt", "regional": "regional", "art": "art", From 86e83a3f31a89b7712b2dd5286d7530c405fb537 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 22:09:00 +0200 Subject: [PATCH 50/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 09f992590..0c465cb25 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -16,8 +16,8 @@ "poll_ended": "Rapirsîya qediya" }, "discard_post_content": { - "title": "Discard Draft", - "message": "Confirm to discard composed post content." + "title": "Reşnivîs jêbibe", + "message": "Piştrast bikin ku naveroka posteyê ya hatîye nivîsandin jê bibin." }, "publish_post_failure": { "title": "Weşandin têkçû", @@ -28,7 +28,7 @@ } }, "edit_profile_failure": { - "title": "Edit Profile Error", + "title": "Çewtiya profîlê biguherîne", "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": "Please enable the photo library access permission to save the photo." + "message": "Ji kerema xwe destûra gihîştina pirtûkxaneya wêneyê çalak bikin da ku wêneyê hilînin." }, "delete_post": { "title": "Ma tu dixwazî vê şandiyê jê bibî?", "delete": "Jê bibe" }, "clean_cache": { - "title": "Clean Cache", - "message": "Successfully cleaned %s cache." + "title": "Pêşbîrê paqij bike", + "message": "Pêşbîra %s biserketî hate paqijkirin." } }, "controls": { @@ -70,9 +70,9 @@ "cancel": "Dev jê berde", "discard": "Biavêje", "try_again": "Dîsa biceribîne", - "take_photo": "Take Photo", - "save_photo": "Save Photo", - "copy_photo": "Copy Photo", + "take_photo": "Wêne bikişîne", + "save_photo": "Wêneyê hilîne", + "copy_photo": "Wêne kopî bikin", "sign_in": "Têkeve", "sign_up": "Tomar bibe", "see_more": "Bêtir bibîne", @@ -81,7 +81,7 @@ "share_user": "%s parve bike", "share_post": "Şandiyê parve bike", "open_in_safari": "Di Safariyê de veke", - "find_people": "Find people to follow", + "find_people": "Kesên ku bişopînin bibînin", "manually_search": "Ji devlê i destan lêgerînê bike", "skip": "Derbas bike", "reply": "Bersivê bide", @@ -109,24 +109,24 @@ "next_status": "Şandiya pêş", "open_status": "Şandiyê veke", "open_author_profile": "Profîla nivîskaran veke", - "open_reblogger_profile": "Open Reblogger's Profile", + "open_reblogger_profile": "Profîla nivîskaran veke", "reply_status": "Bersivê bide şandiyê", "toggle_reblog": "Toggle Reblog on Post", - "toggle_favorite": "Toggle Favorite on Post", - "toggle_content_warning": "Toggle Content Warning", - "preview_image": "Preview Image" + "toggle_favorite": "Di postê da Bijartin veke/bigire", + "toggle_content_warning": "Hişyariya naverokê veke/bigire", + "preview_image": "Wêneya pêşdîtinê" }, "segmented_control": { - "previous_section": "Previous Section", - "next_section": "Next Section" + "previous_section": "Beşa berê", + "next_section": "Beşa paşê" } }, "status": { - "user_reblogged": "%s reblogged", - "user_replied_to": "Replied to %s", + "user_reblogged": "%s ji nû ve hat blogkirin", + "user_replied_to": "Bersiv da %s", "show_post": "Şandiyê nîşan bide", "show_user_profile": "Profîla bikarhêner nîşan bide", - "content_warning": "Content Warning", + "content_warning": "Hişyariya naverokê", "media_content_warning": "Tap anywhere to reveal", "poll": { "vote": "Vote", From 0fb360aca88d4f27b7715789469a4f168e69f88e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 19 Oct 2021 23:06:46 +0200 Subject: [PATCH 51/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 0c465cb25..cc2fdd329 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -127,31 +127,31 @@ "show_post": "Şandiyê nîşan bide", "show_user_profile": "Profîla bikarhêner nîşan bide", "content_warning": "Hişyariya naverokê", - "media_content_warning": "Tap anywhere to reveal", + "media_content_warning": "Ji bo aşkerakirinê derekî bitikîne", "poll": { - "vote": "Vote", - "closed": "Closed" + "vote": "Deng", + "closed": "Girtî" }, "actions": { - "reply": "Reply", - "reblog": "Reblog", - "unreblog": "Undo reblog", - "favorite": "Favorite", - "unfavorite": "Unfavorite", - "menu": "Menu" + "reply": "Bersivê bide", + "reblog": "Ji nû ve blog", + "unreblog": "Ji nû ve blogkirin betal bikin", + "favorite": "Bijartî", + "unfavorite": "Nebijare", + "menu": "Menû" }, "tag": { "url": "URL", - "mention": "Mention", - "link": "Link", - "hashtag": "Hashtag", - "email": "Email", - "emoji": "Emoji" + "mention": "Behs", + "link": "Girêdan", + "hashtag": "Etîket", + "email": "E-name", + "emoji": "E-name" } }, "friendship": { - "follow": "Follow", - "following": "Following", + "follow": "Bişopîne", + "following": "Dişopîne", "request": "Daxwazên şopandinê", "pending": "Tê nirxandin", "block": "Asteng bike", @@ -164,37 +164,37 @@ "mute_user": "%s bêdeng bike", "unmute": "Bêdeng neke", "unmute_user": "%s bêdeng neke", - "muted": "Muted", + "muted": "Bêdengkirî", "edit_info": "Zanyariyan serrast bike" }, "timeline": { - "filtered": "Filtered", + "filtered": "Parzûnkirî", "timestamp": { "now": "Niha" }, "loader": { - "load_missing_posts": "Load missing posts", - "loading_missing_posts": "Loading missing posts...", + "load_missing_posts": "Barkirina posteyên kêm", + "loading_missing_posts": "Barkirina posteyên kêm...", "show_more_replies": "Bêtir bersivan nîşan bide" }, "header": { "no_status_found": "Şandî nehate dîtin", - "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": "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.", + "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." } } } }, "scene": { "welcome": { - "slogan": "Social networking\nback in your hands." + "slogan": "Tevna civakî\nvegerrîna di destê te da." }, "server_picker": { - "title": "Pick a server,\nany server.", + "title": "Pêşkêşkarekê hilbijêre,\nher pêşkêşvanek.", "button": { "category": { "all": "Hemû", From 084a41b5039664a30f4726ee1a063641d613ba37 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 21 Oct 2021 04:36:43 +0200 Subject: [PATCH 52/95] New translations app.json (Kurmanji (Kurdish)) --- Localization/StringsConvertor/input/kmr_TR/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index cc2fdd329..eaa584de0 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -194,7 +194,7 @@ "slogan": "Tevna civakî\nvegerrîna di destê te da." }, "server_picker": { - "title": "Pêşkêşkarekê hilbijêre,\nher pêşkêşvanek.", + "title": "Rajekarekê hilbijêre,\nHer kîjan rajekar be.", "button": { "category": { "all": "Hemû", From d640be9cbe4c805ba27f02b447efa1b3d6a5f878 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 21 Oct 2021 05:38:59 +0200 Subject: [PATCH 53/95] New translations app.json (Kurmanji (Kurdish)) --- Localization/StringsConvertor/input/kmr_TR/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index eaa584de0..b2db1adb3 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -191,7 +191,7 @@ }, "scene": { "welcome": { - "slogan": "Tevna civakî\nvegerrîna di destê te da." + "slogan": "Torên civakî\ndi destên te de." }, "server_picker": { "title": "Rajekarekê hilbijêre,\nHer kîjan rajekar be.", From 2b1a7e7d399b6a72857f3cce27a2aa63d5e652a7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 21 Oct 2021 05:39:00 +0200 Subject: [PATCH 54/95] New translations Intents.strings (Kurmanji (Kurdish)) --- .../StringsConvertor/Intents/input/kmr_TR/Intents.strings | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings index 22b6300dc..687cac69f 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings @@ -4,13 +4,13 @@ "CsR7G2" = "Di Mastodon de biweşîne"; -"HZSGTr" = "What content to post?"; +"HZSGTr" = "Kîjan naverok bila bê şandin?"; -"HdGikU" = "Posting failed"; +"HdGikU" = "Şandin têkçû"; -"KDNTJ4" = "Failure Reason"; +"KDNTJ4" = "Sedema têkçûnê"; -"RHxKOw" = "Send Post with text content"; +"RHxKOw" = "Bi naveroka nivîsî şandiyan bişîne"; "RxSqsb" = "Şandî"; From 87722f172e03fdc0a653780348557a0de038b9c7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 21 Oct 2021 05:39:01 +0200 Subject: [PATCH 55/95] New translations Intents.stringsdict (Kurmanji (Kurdish)) --- .../Intents/input/kmr_TR/Intents.stringsdict | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict index fb10126c0..2f001aaa9 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.stringsdict @@ -5,7 +5,7 @@ There are ${count} options matching ‘${content}’. - 2 NSStringLocalizedFormatKey - There are %#@count_option@ matching ‘${content}’. + %#@count_option@ heye ku bi ‘${content}’ re têkildar e. count_option NSStringFormatSpecTypeKey @@ -21,7 +21,7 @@ There are ${count} options matching ‘${visibility}’. NSStringLocalizedFormatKey - There are %#@count_option@ matching ‘${visibility}’. + %#@count_option@ heye ku bi ‘${visibility}’ re têkildar e. count_option NSStringFormatSpecTypeKey @@ -29,9 +29,9 @@ NSStringFormatValueTypeKey %ld one - 1 option + 1 vebijêrk other - %ld options + %ld vebijêrk From 61f839ff0a0a64cf23de7a7b6270cd843f168792 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 22 Oct 2021 20:23:55 +0800 Subject: [PATCH 56/95] chore: update SDWebImage to 5.12.1 --- .../xcschemes/xcschememanagement.plist | 14 +++++++------- .../xcshareddata/swiftpm/Package.resolved | 13 +++++++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index e6f225e6d..5a5418919 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 54 + 37 CoreDataStack.xcscheme_^#shared#^_ orderHint - 53 + 36 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -37,7 +37,7 @@ Mastodon - ca.xcscheme_^#shared#^_ orderHint - 18 + 16 Mastodon - de.xcscheme_^#shared#^_ @@ -67,7 +67,7 @@ Mastodon - jp.xcscheme_^#shared#^_ orderHint - 15 + 14 Mastodon - nl.xcscheme_^#shared#^_ @@ -87,7 +87,7 @@ Mastodon - zh_Hans.xcscheme_^#shared#^_ orderHint - 16 + 15 Mastodon.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 55 + 35 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 56 + 38 SuppressBuildableAutocreation diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 43a0036bd..26ba58d55 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -141,8 +141,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "d6367439527663d2038ca445a3c3c4e4bac40d60", - "version": "5.12.0" + "revision": "a72df4849408da7e5d3c1b586797b7c601c41d1b", + "version": "5.12.1" } }, { @@ -216,6 +216,15 @@ "revision": "dad97167bf1be16aeecd109130900995dd01c515", "version": "2.6.0" } + }, + { + "package": "UITextView+Placeholder", + "repositoryURL": "https://github.com/MainasuK/UITextView-Placeholder", + "state": { + "branch": null, + "revision": "20f513ded04a040cdf5467f0891849b1763ede3b", + "version": "1.4.1" + } } ] }, From de86e5eab1225a0b1d3100c678c97d193e6138c8 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 22 Oct 2021 20:31:11 +0800 Subject: [PATCH 57/95] fix: user profile timeline not deselect item sometimes when cancel swipe issue --- .../Paging/ProfilePagingViewController.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Mastodon/Scene/Profile/Segmented/Paging/ProfilePagingViewController.swift b/Mastodon/Scene/Profile/Segmented/Paging/ProfilePagingViewController.swift index 16dcbbeb6..23630741f 100644 --- a/Mastodon/Scene/Profile/Segmented/Paging/ProfilePagingViewController.swift +++ b/Mastodon/Scene/Profile/Segmented/Paging/ProfilePagingViewController.swift @@ -21,6 +21,19 @@ final class ProfilePagingViewController: TabmanViewController { // MARK: - PageboyViewControllerDelegate + override func pageboyViewController(_ pageboyViewController: PageboyViewController, didCancelScrollToPageAt index: PageboyViewController.PageIndex, returnToPageAt previousIndex: PageboyViewController.PageIndex) { + super.pageboyViewController(pageboyViewController, didCancelScrollToPageAt: index, returnToPageAt: previousIndex) + + // Fix the SDK bug for table view get row selected during swipe but cancel paging + guard previousIndex < viewModel.viewControllers.count else { return } + let viewController = viewModel.viewControllers[previousIndex] + + if let tableView = viewController.scrollView as? UITableView { + for cell in tableView.visibleCells { + cell.setHighlighted(false, animated: false) + } + } + } override func pageboyViewController(_ pageboyViewController: PageboyViewController, didScrollToPageAt index: TabmanViewController.PageIndex, direction: PageboyViewController.NavigationDirection, animated: Bool) { super.pageboyViewController(pageboyViewController, didScrollToPageAt: index, direction: direction, animated: animated) From 510199aad3ce98e583ddeb2f81bad03363b262b1 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 22 Oct 2021 20:31:30 +0800 Subject: [PATCH 58/95] chore: update version to 1.2.0 (78) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 009534d8f..1958eb540 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 009534d8f..1958eb540 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 009534d8f..1958eb540 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index dab0ea3cc..600d1f4bb 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4792,7 +4792,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4821,7 +4821,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4929,11 +4929,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4960,11 +4960,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4989,11 +4989,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5019,11 +5019,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5086,7 +5086,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5111,7 +5111,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5136,7 +5136,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5161,7 +5161,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5186,7 +5186,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5211,7 +5211,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5236,7 +5236,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5261,7 +5261,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5352,7 +5352,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5419,11 +5419,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5468,7 +5468,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5493,11 +5493,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5589,7 +5589,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5656,11 +5656,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5705,7 +5705,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5730,11 +5730,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 77; + DYLIB_CURRENT_VERSION = 78; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5760,7 +5760,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5784,7 +5784,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 77; + CURRENT_PROJECT_VERSION = 78; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 5a5418919..424b6b090 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 37 + 44 CoreDataStack.xcscheme_^#shared#^_ orderHint - 36 + 43 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 35 + 42 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 38 + 41 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index a371b7f41..f8987fab7 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 77 + 78 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 9179ceb0c..1f4cb4c62 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 009534d8f..1958eb540 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 009534d8f..1958eb540 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index 642d2f561..f1f14ae01 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index fa46a9679..9edd32f6a 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 77 + 78 NSExtension NSExtensionAttributes From 532e10e466f7a673af73f8c1c0b7c14dbcc3959d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 25 Oct 2021 10:40:39 +0200 Subject: [PATCH 59/95] New translations app.json (Japanese) --- Localization/StringsConvertor/input/ja_JP/app.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/input/ja_JP/app.json b/Localization/StringsConvertor/input/ja_JP/app.json index 2f1aec4ec..1c7d408f5 100644 --- a/Localization/StringsConvertor/input/ja_JP/app.json +++ b/Localization/StringsConvertor/input/ja_JP/app.json @@ -191,7 +191,7 @@ }, "scene": { "welcome": { - "slogan": "Social networking\nback in your hands." + "slogan": "ソーシャルネットワーキングを、あなたの手の中に." }, "server_picker": { "title": "サーバーを選択", @@ -538,11 +538,11 @@ "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": "アカウントを追加" }, "wizard": { - "new_in_mastodon": "New in Mastodon", - "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.", + "new_in_mastodon": "Mastodon の新機能", + "multiple_account_switch_intro_description": "プロフィールボタンを押して複数のアカウントを切り替えます。", "accessibility_hint": "Double tap to dismiss this wizard" } } From 30110d1560b594aab1069dbd4bc6cbcd8eb7f593 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 25 Oct 2021 10:40:40 +0200 Subject: [PATCH 60/95] New translations Localizable.stringsdict (Japanese) --- .../input/ja_JP/Localizable.stringsdict | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Localization/StringsConvertor/input/ja_JP/Localizable.stringsdict b/Localization/StringsConvertor/input/ja_JP/Localizable.stringsdict index 0300d9dc3..c51a9a29d 100644 --- a/Localization/StringsConvertor/input/ja_JP/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/ja_JP/Localizable.stringsdict @@ -13,7 +13,7 @@ NSStringFormatValueTypeKey ld other - %ld unread notification + %ld 件の未読通知 a11y.plural.count.input_limit_exceeds @@ -27,7 +27,7 @@ NSStringFormatValueTypeKey ld other - %ld characters + %ld 文字 a11y.plural.count.input_limit_remains @@ -41,7 +41,7 @@ NSStringFormatValueTypeKey ld other - %ld characters + %ld 文字 plural.count.metric_formatted.post @@ -111,7 +111,7 @@ NSStringFormatValueTypeKey ld other - %ld votes + %ld票 plural.count.voter @@ -195,7 +195,7 @@ NSStringFormatValueTypeKey ld other - %ld months left + %ldか月前 date.day.left @@ -279,7 +279,7 @@ NSStringFormatValueTypeKey ld other - %ldM ago + %ld分前 date.day.ago.abbr From 6b12adb9c56d0fb4a257742dd0c27bf7b21d10e3 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 16:59:31 +0800 Subject: [PATCH 61/95] fix: auto complete in Compose scene not trigger delegate issue. resolve #300 --- Mastodon/Scene/Compose/ComposeViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mastodon/Scene/Compose/ComposeViewController.swift b/Mastodon/Scene/Compose/ComposeViewController.swift index ec8a20e3f..5968df428 100644 --- a/Mastodon/Scene/Compose/ComposeViewController.swift +++ b/Mastodon/Scene/Compose/ComposeViewController.swift @@ -1275,7 +1275,6 @@ extension ComposeViewController: AutoCompleteViewControllerDelegate { case .bottomLoader: return nil } - text.append(" ") return text }() guard let replacedText = _replacedText else { return } @@ -1286,6 +1285,9 @@ extension ComposeViewController: AutoCompleteViewControllerDelegate { let range = NSRange(info.toHighlightEndRange, in: text) textEditorView.textStorage.replaceCharacters(in: range, with: replacedText) + DispatchQueue.main.async { + textEditorView.textView.insertText(" ") // trigger textView delegate update + } viewModel.autoCompleteInfo.value = nil switch item { From eaff3632430de2f9592d9b8b6afa4efc91bf552f Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 17:00:36 +0800 Subject: [PATCH 62/95] fix: auto complete list cell title UI is user interaction enabled issue --- .../Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift b/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift index 7492753fe..c1e7ab6a4 100644 --- a/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift +++ b/Mastodon/Scene/Compose/AutoComplete/Cell/AutoCompleteTableViewCell.swift @@ -33,6 +33,7 @@ final class AutoCompleteTableViewCell: UITableViewCell { let titleLabel: MetaLabel = { let label = MetaLabel(style: .autoCompletion) + label.isUserInteractionEnabled = false return label }() From 829c3f4cd232367525c43d2a5aa02f14f03475e5 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 17:24:44 +0800 Subject: [PATCH 63/95] fix: hashtag icon in search history record cell can not adaptive update appearance issue --- .../Section/Search/SearchHistorySection.swift | 18 +----- .../SearchResultTableViewCell.swift | 55 ++++++++++++++----- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Mastodon/Diffiable/Section/Search/SearchHistorySection.swift b/Mastodon/Diffiable/Section/Search/SearchHistorySection.swift index 8f39eb6bd..b5c5cd8cc 100644 --- a/Mastodon/Diffiable/Section/Search/SearchHistorySection.swift +++ b/Mastodon/Diffiable/Section/Search/SearchHistorySection.swift @@ -32,24 +32,8 @@ extension SearchHistorySection { } return cell case .status: + // Should not show status in the history list return UITableViewCell() -// let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StatusTableViewCell.self), for: indexPath) as! StatusTableViewCell -// if let status = try? dependency.context.managedObjectContext.existingObject(with: statusObjectID) as? Status { -// let activeMastodonAuthenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value -// let requestUserID = activeMastodonAuthenticationBox?.userID ?? "" -// StatusSection.configure( -// cell: cell, -// tableView: tableView, -// timelineContext: .search, -// dependency: dependency, -// readableLayoutFrame: tableView.readableContentGuide.layoutFrame, -// status: status, -// requestUserID: requestUserID, -// statusItemAttribute: attribute -// ) -// } -// cell.delegate = statusTableViewCellDelegate -// return cell } // end switch } // end UITableViewDiffableDataSource } // end func diff --git a/Mastodon/Scene/Search/SearchDetail/TableViewCell/SearchResultTableViewCell.swift b/Mastodon/Scene/Search/SearchDetail/TableViewCell/SearchResultTableViewCell.swift index a872fca43..0c919e7d5 100644 --- a/Mastodon/Scene/Search/SearchDetail/TableViewCell/SearchResultTableViewCell.swift +++ b/Mastodon/Scene/Search/SearchDetail/TableViewCell/SearchResultTableViewCell.swift @@ -16,7 +16,7 @@ import MastodonMeta final class SearchResultTableViewCell: UITableViewCell { - let _imageView: AvatarImageView = { + let avatarImageView: AvatarImageView = { let imageView = AvatarImageView() imageView.tintColor = Asset.Colors.Label.primary.color imageView.layer.cornerRadius = 4 @@ -24,6 +24,13 @@ final class SearchResultTableViewCell: UITableViewCell { return imageView }() + let hashtagImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(systemName: "number.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 34, weight: .regular))!.withRenderingMode(.alwaysTemplate) + imageView.tintColor = Asset.Colors.Label.primary.color + return imageView + }() + let _titleLabel = MetaLabel(style: .statusName) let _subTitleLabel: UILabel = { @@ -43,7 +50,8 @@ final class SearchResultTableViewCell: UITableViewCell { override func prepareForReuse() { super.prepareForReuse() - _imageView.af.cancelImageRequest() + avatarImageView.af.cancelImageRequest() + setDisplayAvatarImage() } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -74,11 +82,20 @@ extension SearchResultTableViewCell { containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) - _imageView.translatesAutoresizingMaskIntoConstraints = false - containerStackView.addArrangedSubview(_imageView) + avatarImageView.translatesAutoresizingMaskIntoConstraints = false + containerStackView.addArrangedSubview(avatarImageView) NSLayoutConstraint.activate([ - _imageView.widthAnchor.constraint(equalToConstant: 42).priority(.required - 1), - _imageView.heightAnchor.constraint(equalToConstant: 42).priority(.required - 1), + avatarImageView.widthAnchor.constraint(equalToConstant: 42).priority(.required - 1), + avatarImageView.heightAnchor.constraint(equalToConstant: 42).priority(.required - 1), + ]) + + hashtagImageView.translatesAutoresizingMaskIntoConstraints = false + containerStackView.addSubview(hashtagImageView) + NSLayoutConstraint.activate([ + hashtagImageView.centerXAnchor.constraint(equalTo: avatarImageView.centerXAnchor), + hashtagImageView.centerYAnchor.constraint(equalTo: avatarImageView.centerYAnchor), + hashtagImageView.widthAnchor.constraint(equalToConstant: 42).priority(.required - 1), + hashtagImageView.heightAnchor.constraint(equalToConstant: 42).priority(.required - 1), ]) let textStackView = UIStackView() @@ -107,7 +124,9 @@ extension SearchResultTableViewCell { _titleLabel.isUserInteractionEnabled = false _subTitleLabel.isUserInteractionEnabled = false - _imageView.isUserInteractionEnabled = false + avatarImageView.isUserInteractionEnabled = false + + setDisplayAvatarImage() } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { @@ -182,8 +201,7 @@ extension SearchResultTableViewCell { func config(with tag: Mastodon.Entity.Tag) { configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: nil)) - let image = UIImage(systemName: "number.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 34, weight: .regular))!.withRenderingMode(.alwaysTemplate) - _imageView.image = image + setDisplayHashtagImage() let metaContent = PlaintextMetaContent(string: "#" + tag.name) _titleLabel.configure(content: metaContent) guard let histories = tag.history else { @@ -198,8 +216,7 @@ extension SearchResultTableViewCell { func config(with tag: Tag) { configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: nil)) - let image = UIImage(systemName: "number.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 34, weight: .regular))!.withRenderingMode(.alwaysTemplate) - _imageView.image = image + setDisplayHashtagImage() let metaContent = PlaintextMetaContent(string: "#" + tag.name) _titleLabel.configure(content: metaContent) guard let histories = tag.histories?.sorted(by: { @@ -215,11 +232,23 @@ extension SearchResultTableViewCell { } } +extension SearchResultTableViewCell { + func setDisplayAvatarImage() { + avatarImageView.alpha = 1 + hashtagImageView.alpha = 0 + } + + func setDisplayHashtagImage() { + avatarImageView.alpha = 0 + hashtagImageView.alpha = 1 + } +} + // MARK: - AvatarStackedImageView extension SearchResultTableViewCell: AvatarConfigurableView { static var configurableAvatarImageSize: CGSize { CGSize(width: 42, height: 42) } static var configurableAvatarImageCornerRadius: CGFloat { 4 } - var configurableAvatarImageView: FLAnimatedImageView? { _imageView } + var configurableAvatarImageView: FLAnimatedImageView? { avatarImageView } } #if canImport(SwiftUI) && DEBUG @@ -231,7 +260,7 @@ struct SearchResultTableViewCell_Previews: PreviewProvider { UIViewPreview { let cell = SearchResultTableViewCell() cell.backgroundColor = .white - cell._imageView.image = UIImage(systemName: "number.circle.fill") + cell.setDisplayHashtagImage() cell._titleLabel.text = "Electronic Frontier Foundation" cell._subTitleLabel.text = "@eff@mastodon.social" return cell From 1b74df7f27c28d08895e3fba4df8807c3a88477b Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 18:08:33 +0800 Subject: [PATCH 64/95] chore: update trends card UI --- ...earchRecommendTagsCollectionViewCell.swift | 32 +++++++++++++------ .../Search/Search/SearchViewController.swift | 3 ++ .../Search/Search/View/LineChartView.swift | 22 ++++++------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift index a538d30bf..3734bc8a4 100644 --- a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift +++ b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendTagsCollectionViewCell.swift @@ -17,7 +17,7 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { let hashtagTitleLabel: UILabel = { let label = UILabel() - label.textColor = .label + label.textColor = .white label.font = .systemFont(ofSize: 20, weight: .semibold) label.lineBreakMode = .byTruncatingTail return label @@ -25,7 +25,7 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { let peopleLabel: UILabel = { let label = UILabel() - label.textColor = .label + label.textColor = .white label.font = .preferredFont(forTextStyle: .body) return label }() @@ -48,7 +48,7 @@ class SearchRecommendTagsCollectionViewCell: UICollectionViewCell { override var isHighlighted: Bool { didSet { - backgroundColor = isHighlighted ? .systemBackground.withAlphaComponent(0.8) : .systemBackground + backgroundColor = isHighlighted ? Asset.Colors.brandBlueDarken20.color : Asset.Colors.brandBlue.color } } } @@ -62,7 +62,7 @@ extension SearchRecommendTagsCollectionViewCell { } private func configure() { - backgroundColor = .systemBackground + backgroundColor = Asset.Colors.brandBlue.color layer.cornerRadius = 10 layer.cornerCurve = .continuous clipsToBounds = false @@ -96,14 +96,26 @@ extension SearchRecommendTagsCollectionViewCell { containerStackView.addArrangedSubview(hashtagTitleLabel) containerStackView.addArrangedSubview(peopleLabel) - lineChartView.translatesAutoresizingMaskIntoConstraints = false - contentView.addSubview(lineChartView) + let lineChartContainer = UIView() + lineChartContainer.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(lineChartContainer) NSLayoutConstraint.activate([ - lineChartView.topAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 8), - lineChartView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), - contentView.trailingAnchor.constraint(equalTo: lineChartView.trailingAnchor, constant: 16), - contentView.bottomAnchor.constraint(equalTo: lineChartView.bottomAnchor, constant: 16), + lineChartContainer.topAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 12), + lineChartContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: lineChartContainer.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: lineChartContainer.bottomAnchor, constant: 12), ]) + lineChartContainer.layer.masksToBounds = true + + lineChartView.translatesAutoresizingMaskIntoConstraints = false + lineChartContainer.addSubview(lineChartView) + NSLayoutConstraint.activate([ + lineChartView.topAnchor.constraint(equalTo: lineChartContainer.topAnchor, constant: 4), + lineChartView.leadingAnchor.constraint(equalTo: lineChartContainer.leadingAnchor), + lineChartView.trailingAnchor.constraint(equalTo: lineChartContainer.trailingAnchor), + lineChartContainer.bottomAnchor.constraint(equalTo: lineChartView.bottomAnchor, constant: 4), + ]) + } func config(with tag: Mastodon.Entity.Tag) { diff --git a/Mastodon/Scene/Search/Search/SearchViewController.swift b/Mastodon/Scene/Search/Search/SearchViewController.swift index c72945927..8dcf9cd3b 100644 --- a/Mastodon/Scene/Search/Search/SearchViewController.swift +++ b/Mastodon/Scene/Search/Search/SearchViewController.swift @@ -23,6 +23,9 @@ final class SearchViewController: UIViewController, NeedsDependency { public static var hashtagCardHeight: CGFloat { get { + if UIScreen.main.bounds.size.height > 736 { + return 186 + } return 130 } } diff --git a/Mastodon/Scene/Search/Search/View/LineChartView.swift b/Mastodon/Scene/Search/Search/View/LineChartView.swift index 2fb8ac733..a64aa270d 100644 --- a/Mastodon/Scene/Search/Search/View/LineChartView.swift +++ b/Mastodon/Scene/Search/Search/View/LineChartView.swift @@ -19,7 +19,7 @@ final class LineChartView: UIView { let lineShapeLayer = CAShapeLayer() let gradientLayer = CAGradientLayer() - let dotShapeLayer = CAShapeLayer() +// let dotShapeLayer = CAShapeLayer() override init(frame: CGRect) { super.init(frame: frame) @@ -37,14 +37,14 @@ extension LineChartView { private func _init() { lineShapeLayer.frame = bounds gradientLayer.frame = bounds - dotShapeLayer.frame = bounds +// dotShapeLayer.frame = bounds layer.addSublayer(lineShapeLayer) layer.addSublayer(gradientLayer) - layer.addSublayer(dotShapeLayer) +// layer.addSublayer(dotShapeLayer) gradientLayer.colors = [ - Asset.Colors.brandBlue.color.withAlphaComponent(0.5).cgColor, - Asset.Colors.brandBlue.color.withAlphaComponent(0).cgColor, + UIColor.white.withAlphaComponent(0.5).cgColor, + UIColor.white.withAlphaComponent(0).cgColor, ] gradientLayer.startPoint = CGPoint(x: 0.5, y: 0) gradientLayer.endPoint = CGPoint(x: 0.5, y: 1) @@ -55,11 +55,11 @@ extension LineChartView { lineShapeLayer.frame = bounds gradientLayer.frame = bounds - dotShapeLayer.frame = bounds +// dotShapeLayer.frame = bounds guard data.count > 1 else { lineShapeLayer.path = nil - dotShapeLayer.path = nil +// dotShapeLayer.path = nil gradientLayer.isHidden = true return } @@ -96,7 +96,7 @@ extension LineChartView { } lineShapeLayer.lineWidth = 3 - lineShapeLayer.strokeColor = Asset.Colors.brandBlue.color.cgColor + lineShapeLayer.strokeColor = UIColor.white.cgColor lineShapeLayer.fillColor = UIColor.clear.cgColor lineShapeLayer.lineJoin = .round lineShapeLayer.lineCap = .round @@ -113,8 +113,8 @@ extension LineChartView { maskLayer.lineWidth = 0.0 gradientLayer.mask = maskLayer - dotShapeLayer.lineWidth = 3 - dotShapeLayer.fillColor = Asset.Colors.brandBlue.color.cgColor - dotShapeLayer.path = dotPath.cgPath +// dotShapeLayer.lineWidth = 3 +// dotShapeLayer.fillColor = Asset.Colors.brandBlue.color.cgColor +// dotShapeLayer.path = dotPath.cgPath } } From 825447ef69d1fea4c311fa290b991b80f12cb2b5 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 18:11:37 +0800 Subject: [PATCH 65/95] fix: name label in user recommend card is user interaction enabled issue --- .../SearchRecommendAccountsCollectionViewCell.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendAccountsCollectionViewCell.swift b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendAccountsCollectionViewCell.swift index 365c1ee72..2b0c4736d 100644 --- a/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendAccountsCollectionViewCell.swift +++ b/Mastodon/Scene/Search/Search/CollectionViewCell/SearchRecommendAccountsCollectionViewCell.swift @@ -167,6 +167,8 @@ extension SearchRecommendAccountsCollectionViewCell { containerStackView.addArrangedSubview(followButton) followButton.addTarget(self, action: #selector(SearchRecommendAccountsCollectionViewCell.followButtonDidPressed(_:)), for: .touchUpInside) + + displayNameLabel.isUserInteractionEnabled = false } } From b2e8eb18a0daace77b000b86e561008391afc7e3 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 25 Oct 2021 18:11:52 +0800 Subject: [PATCH 66/95] chore: update version to 1.2.0 (79) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 6 +- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 1958eb540..7f509a3fe 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 1958eb540..7f509a3fe 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 1958eb540..7f509a3fe 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 600d1f4bb..3c44f617b 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4792,7 +4792,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4821,7 +4821,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4929,11 +4929,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4960,11 +4960,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4989,11 +4989,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5019,11 +5019,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5086,7 +5086,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5111,7 +5111,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5136,7 +5136,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5161,7 +5161,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5186,7 +5186,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5211,7 +5211,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5236,7 +5236,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5261,7 +5261,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5352,7 +5352,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5419,11 +5419,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5468,7 +5468,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5493,11 +5493,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5589,7 +5589,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5656,11 +5656,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5705,7 +5705,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5730,11 +5730,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 78; + DYLIB_CURRENT_VERSION = 79; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5760,7 +5760,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5784,7 +5784,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 424b6b090..62b4c7f21 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ AppShared.xcscheme_^#shared#^_ orderHint - 44 + 42 CoreDataStack.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 42 + 41 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 41 + 44 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index f8987fab7..43ae9e6d0 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 78 + 79 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 1f4cb4c62..714ba3106 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 1958eb540..7f509a3fe 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 1958eb540..7f509a3fe 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index f1f14ae01..b5ee7a305 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 9edd32f6a..5643d878f 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 78 + 79 NSExtension NSExtensionAttributes From 4640ac80eb2851c1a47641e7bc990bbfcb8a87d3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 25 Oct 2021 23:36:20 +0200 Subject: [PATCH 67/95] New translations app.json (German) --- Localization/StringsConvertor/input/de_DE/app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localization/StringsConvertor/input/de_DE/app.json b/Localization/StringsConvertor/input/de_DE/app.json index ff29e8d64..43d8ed70a 100644 --- a/Localization/StringsConvertor/input/de_DE/app.json +++ b/Localization/StringsConvertor/input/de_DE/app.json @@ -536,13 +536,13 @@ } }, "account_list": { - "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher", + "tab_bar_hint": "Aktuell ausgewähltes Profil: %s. Doppeltippen dann gedrückt halten, um den Kontoschalter anzuzeigen", "dismiss_account_switcher": "Dismiss Account Switcher", "add_account": "Konto hinzufügen" }, "wizard": { "new_in_mastodon": "Neu in Mastodon", - "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.", + "multiple_account_switch_intro_description": "Wechsel zwischen mehreren Konten durch drücken der Profil-Schaltfläche.", "accessibility_hint": "Doppeltippen, um diesen Assistenten zu schließen" } } From dfd3516f013d9ef8925186d8160759f9ef876037 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 05:47:36 +0200 Subject: [PATCH 68/95] New translations Intents.strings (Arabic) --- .../StringsConvertor/Intents/input/ar_SA/Intents.strings | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings index bf3e77ed2..220b144ab 100644 --- a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings @@ -1,10 +1,10 @@ -"16wxgf" = "Post on Mastodon"; +"16wxgf" = "النَشر على ماستودون"; "751xkl" = "محتوى نصي"; "CsR7G2" = "انشر على ماستدون"; -"HZSGTr" = "What content to post?"; +"HZSGTr" = "ما المُحتوى المُراد نشره؟"; "HdGikU" = "Posting failed"; @@ -12,11 +12,11 @@ "RHxKOw" = "Send Post with text content"; -"RxSqsb" = "Post"; +"RxSqsb" = "مَنشور"; "WCIR3D" = "Post ${content} on Mastodon"; -"ZKJSNu" = "Post"; +"ZKJSNu" = "مَنشور"; "ZS1XaK" = "${content}"; From 831b19aedd1dc4fdeaa7746faa7e27b13b1aa8d3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 06:52:24 +0200 Subject: [PATCH 69/95] New translations Intents.strings (Arabic) --- .../Intents/input/ar_SA/Intents.strings | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings index 220b144ab..cde27dc97 100644 --- a/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/ar_SA/Intents.strings @@ -6,15 +6,15 @@ "HZSGTr" = "ما المُحتوى المُراد نشره؟"; -"HdGikU" = "Posting failed"; +"HdGikU" = "فَشَلَ النشر"; "KDNTJ4" = "سبب الإخفاق"; -"RHxKOw" = "Send Post with text content"; +"RHxKOw" = "إرسال مَنشور يَحوي نص"; "RxSqsb" = "مَنشور"; -"WCIR3D" = "Post ${content} on Mastodon"; +"WCIR3D" = "نَشر ${content} على ماستودون"; "ZKJSNu" = "مَنشور"; @@ -24,13 +24,13 @@ "Zo4jgJ" = "مدى ظهور المنشور"; -"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’."; +"apSxMG-dYQ5NN" = "هُناك عدد ${count} خِيار مُطابق لِـ\"عام\"."; -"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’."; +"apSxMG-ehFLjY" = "هُناك عدد ${count} خِيار مُطابق لِـ\"المُتابِعُون فقط\"."; -"ayoYEb-dYQ5NN" = "${content}, Public"; +"ayoYEb-dYQ5NN" = "${content}، عام"; -"ayoYEb-ehFLjY" = "${content}, Followers Only"; +"ayoYEb-ehFLjY" = "${content}، المُتابِعُون فقط"; "dUyuGg" = "النشر على ماستدون"; @@ -38,13 +38,13 @@ "ehFLjY" = "لمتابعيك فقط"; -"gfePDu" = "Posting failed. ${failureReason}"; +"gfePDu" = "فَشَلَ النشر، ${failureReason}"; -"k7dbKQ" = "Post was sent successfully."; +"k7dbKQ" = "تمَّ إرسال المنشور بِنجاح."; -"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?"; +"oGiqmY-dYQ5NN" = "للتأكيد، هل تَريد \"عام\"؟"; -"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?"; +"oGiqmY-ehFLjY" = "للتأكيد، هل تُريد \"للمُتابِعين فقط\"؟"; "rM6dvp" = "عنوان URL"; From 478004d3cd7a55073068fe69c3409791c5a638da Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 06:52:25 +0200 Subject: [PATCH 70/95] New translations app.json (Arabic) --- Localization/StringsConvertor/input/ar_SA/app.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Localization/StringsConvertor/input/ar_SA/app.json b/Localization/StringsConvertor/input/ar_SA/app.json index ef009776c..4bf55d918 100644 --- a/Localization/StringsConvertor/input/ar_SA/app.json +++ b/Localization/StringsConvertor/input/ar_SA/app.json @@ -28,7 +28,7 @@ } }, "edit_profile_failure": { - "title": "Edit Profile Error", + "title": "خطأ في تَحرير الملف الشخصي", "message": "لا يمكن تعديل الملف الشخصي. يُرجى المحاولة مرة أُخرى." }, "sign_out": { @@ -194,12 +194,12 @@ "slogan": "Social networking\nback in your hands." }, "server_picker": { - "title": "Pick a server,\nany server.", + "title": "اِختر خادِم،\nأي خادِم.", "button": { "category": { "all": "الكل", "all_accessiblity_description": "الفئة: الكل", - "academia": "academia", + "academia": "أكاديمي", "activism": "للنشطاء", "food": "الطعام", "furry": "فروي", From 8d6d075140c660f2efa93acceba67f31ac5ed1c3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 18:44:41 +0200 Subject: [PATCH 71/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index b2db1adb3..1514332e8 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -207,72 +207,72 @@ "general": "giştî", "journalism": "rojnamevanî", "lgbt": "lgbt", - "regional": "regional", - "art": "art", - "music": "music", - "tech": "tech" + "regional": "herêmî", + "art": "huner", + "music": "muzîk", + "tech": "teknolojî" }, - "see_less": "See Less", - "see_more": "See More" + "see_less": "Kêmtir bibîne", + "see_more": "Bêtir bibîne" }, "label": { - "language": "LANGUAGE", - "users": "USERS", - "category": "CATEGORY" + "language": "ZIMAN", + "users": "BIKARHÊNER", + "category": "KATEGORÎ" }, "input": { - "placeholder": "Find a server or join your own..." + "placeholder": "Serverek bibînin an jî beşdarî ya xwe bibin..." }, "empty_state": { - "finding_servers": "Finding available servers...", - "bad_network": "Something went wrong while loading the data. Check your internet connection.", - "no_results": "No results" + "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" } }, "register": { - "title": "Tell us about you.", + "title": "Ji me re hinekî qala xwe bike.", "input": { "avatar": { - "delete": "Delete" + "delete": "Jê bibe" }, "username": { - "placeholder": "username", - "duplicate_prompt": "This username is taken." + "placeholder": "navê bikarhêner", + "duplicate_prompt": "Navê vê bikarhêner tê girtin." }, "display_name": { - "placeholder": "display name" + "placeholder": "navê nîşanê" }, "email": { - "placeholder": "email" + "placeholder": "e-name" }, "password": { - "placeholder": "password", - "hint": "Your password needs at least eight characters" + "placeholder": "şîfre", + "hint": "Şîfreya we herî kêm heşt tîpan hewce dike" }, "invite": { - "registration_user_invite_request": "Why do you want to join?" + "registration_user_invite_request": "Tu çima dixwazî beşdar bibî?" } }, "error": { "item": { - "username": "Username", - "email": "Email", - "password": "Password", - "agreement": "Agreement", - "locale": "Locale", - "reason": "Reason" + "username": "Navê bikarhêner", + "email": "E-name", + "password": "Şîfre", + "agreement": "Lihevhatin", + "locale": "Herêm", + "reason": "Sedem" }, "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", - "blank": "%s is required", - "invalid": "%s is invalid", - "too_long": "%s is too long", - "too_short": "%s is too short", - "inclusion": "%s is not a supported value" + "blocked": "%s peydekerê e-nameya bêdestûr dihewîne", + "unreachable": "%s xuya nake", + "taken": "%s jixwe tê bikaranîn", + "reserved": "%s peyveke mifteya veqetandî ye", + "accepted": "%s divê were qebûlkirin", + "blank": "%s pêwist e", + "invalid": "%s ne derbasdar e", + "too_long": "%s gelekî dirêj e", + "too_short": "%s pir kurt e", + "inclusion": "%s nirxeke ku tê destekirin nîn e" }, "special": { "username_invalid": "Username must only contain alphanumeric characters and underscores", From 4c893a168d6c73c1dc7e31ea758a6ca0d7abb84b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 19:55:32 +0200 Subject: [PATCH 72/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 1514332e8..926002627 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -275,25 +275,25 @@ "inclusion": "%s nirxeke ku tê destekirin nîn e" }, "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)", - "email_invalid": "This is not a valid email address", - "password_too_short": "Password is too short (must be at least 8 characters)" + "username_invalid": "Navê bikarhêner divê tenê tîpên alfanumerîk û binxet hebe", + "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)" } } }, "server_rules": { - "title": "Some ground rules.", - "subtitle": "These rules are set by the admins of %s.", - "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.", - "terms_of_service": "terms of service", - "privacy_policy": "privacy policy", + "title": "Hin qaîdeyê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ê", "button": { - "confirm": "I Agree" + "confirm": "Ez tev dibim" } }, "confirm_email": { - "title": "One last thing.", + "title": "Tiştekî dawî.", "subtitle": "We just sent an email to %s,\ntap the link to confirm your account.", "button": { "open_email_app": "Open Email App", @@ -372,101 +372,101 @@ "remove_poll": "Remove Poll", "custom_emoji_picker": "Custom Emoji Picker", "enable_content_warning": "Enable Content Warning", - "disable_content_warning": "Disable Content Warning", - "post_visibility_menu": "Post Visibility Menu" + "disable_content_warning": "Hişyariya naverokê neçalak bike", + "post_visibility_menu": "Menuya Xuyabûna Şandiyê" }, "keyboard": { - "discard_post": "Discard Post", - "publish_post": "Publish Post", - "toggle_poll": "Toggle Poll", - "toggle_content_warning": "Toggle Content Warning", - "append_attachment_entry": "Add Attachment - %s", - "select_visibility_entry": "Select Visibility - %s" + "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" } }, "profile": { "dashboard": { - "posts": "posts", - "following": "following", - "followers": "followers" + "posts": "şandîyan", + "following": "dişopîne", + "followers": "şopîneran" }, "fields": { - "add_row": "Add Row", + "add_row": "Rêzê lê zêde bike", "placeholder": { - "label": "Label", - "content": "Content" + "label": "Nîşan", + "content": "Naverok" } }, "segmented_control": { - "posts": "Posts", - "replies": "Replies", - "media": "Media" + "posts": "Şandîyan", + "replies": "Bersivan", + "media": "Medya" }, "relationship_action_alert": { "confirm_unmute_user": { - "title": "Unmute Account", - "message": "Confirm to unmute %s" + "title": "Hesabê ji bê deng rake", + "message": "Ji bo vekirina bê dengkirinê bipejirin %s" }, "confirm_unblock_usre": { - "title": "Unblock Account", - "message": "Confirm to unblock %s" + "title": "Hesabê ji bloke rake", + "message": "Ji bo rakirina blokê bipejirin %s" } } }, "search": { - "title": "Search", + "title": "Bigere", "search_bar": { - "placeholder": "Search hashtags and users", - "cancel": "Cancel" + "placeholder": "Li etîketan û bikarhêneran bigerin", + "cancel": "Betal kirin" }, "recommend": { - "button_text": "See All", + "button_text": "Hemûyé bibîne", "hash_tag": { - "title": "Trending on Mastodon", - "description": "Hashtags that are getting quite a bit of attention", - "people_talking": "%s people are talking" + "title": "Trend li ser Mastodon", + "description": "Etîketên ku pir balê dikişînin", + "people_talking": "%s kes diaxivin" }, "accounts": { - "title": "Accounts you might like", - "description": "You may like to follow these accounts", - "follow": "Follow" + "title": "Hesabên ku hûn dikarin hez bikin", + "description": "Dibe ku tu bixwazî van hesaban bişopînî", + "follow": "Bişopîne" } }, "searching": { "segment": { - "all": "All", - "people": "People", - "hashtags": "Hashtags", - "posts": "Posts" + "all": "Hemû", + "people": "Mirov", + "hashtags": "Etîketan", + "posts": "Şandîyan" }, "empty_state": { - "no_results": "No results" + "no_results": "Encam tune" }, - "recent_search": "Recent searches", - "clear": "Clear" + "recent_search": "Lêgerînên dawî", + "clear": "Paqij bike" } }, "favorite": { - "title": "Your Favorites" + "title": "Bijareyên te" }, "notification": { "title": { - "Everything": "Everything", - "Mentions": "Mentions" + "Everything": "Her tişt", + "Mentions": "Behs" }, - "user_followed_you": "%s followed you", - "user_favorited your post": "%s favorited your post", - "user_reblogged_your_post": "%s reblogged your post", - "user_mentioned_you": "%s mentioned you", - "user_requested_to_follow_you": "%s requested to follow you", - "user_your_poll_has_ended": "%s Your poll has ended", + "user_followed_you": "%s te şopand", + "user_favorited your post": "%s posta we bijarte", + "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", "keyobard": { - "show_everything": "Show Everything", - "show_mentions": "Show Mentions" + "show_everything": "Her tiştî nîşan bide", + "show_mentions": "Behskirîya nîşan bike" } }, "thread": { - "back_title": "Post", + "back_title": "Şandî", "title": "Post from %s" }, "settings": { From 49955a638625ae65f9989726246af226e72b3822 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Oct 2021 19:55:33 +0200 Subject: [PATCH 73/95] New translations Intents.strings (Kurmanji (Kurdish)) --- .../Intents/input/kmr_TR/Intents.strings | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings index 687cac69f..3e1c69fc3 100644 --- a/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings +++ b/Localization/StringsConvertor/Intents/input/kmr_TR/Intents.strings @@ -24,28 +24,28 @@ "Zo4jgJ" = "Xuyaniya şandiyê"; -"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’."; +"apSxMG-dYQ5NN" = "Vebijarkên ${count} hene ku li gorî 'Giştî' ne."; -"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’."; +"apSxMG-ehFLjY" = "Vebijarkên ${count} hene ku li gorî 'Tenê Şopandin' hene."; -"ayoYEb-dYQ5NN" = "${content}, Public"; +"ayoYEb-dYQ5NN" = "${content}, Giştî"; -"ayoYEb-ehFLjY" = "${content}, Followers Only"; +"ayoYEb-ehFLjY" = "${content}, Tenê şopînêr"; -"dUyuGg" = "Post on Mastodon"; +"dUyuGg" = "Li ser Mastodon bişînin"; "dYQ5NN" = "Gelemperî"; "ehFLjY" = "Tenê şopîneran"; -"gfePDu" = "Posting failed. ${failureReason}"; +"gfePDu" = "Weşandin bi ser neket. ${failureReason}"; -"k7dbKQ" = "Post was sent successfully."; +"k7dbKQ" = "Şandî bi serkeftî hate şandin."; -"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?"; +"oGiqmY-dYQ5NN" = "Tenê ji bo pejirandinê, we 'Giştî' dixwest?"; -"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?"; +"oGiqmY-ehFLjY" = "Tenê ji bo piştrastkirinê, we 'Tenê Şopdarên' dixwest?"; "rM6dvp" = "Girêdan"; -"ryJLwG" = "Post was sent successfully. "; +"ryJLwG" = "Bi serkeftî hat şandin. "; From 8d9ac28d810cd2d3b2906883ac1e62b004086063 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 12:17:22 +0200 Subject: [PATCH 74/95] New translations app.json (Scottish Gaelic) --- Localization/StringsConvertor/input/gd_GB/app.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Localization/StringsConvertor/input/gd_GB/app.json b/Localization/StringsConvertor/input/gd_GB/app.json index 35f551fea..a73925bba 100644 --- a/Localization/StringsConvertor/input/gd_GB/app.json +++ b/Localization/StringsConvertor/input/gd_GB/app.json @@ -536,14 +536,14 @@ } }, "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" + "tab_bar_hint": "A’ phròifil air a taghadh: %s. Thoir gnogag dhùbailte is cùm sìos a ghearradh leum gu cunntas eile", + "dismiss_account_switcher": "Leig seachad taghadh a’ chunntais", + "add_account": "Cuir cunntas ris" }, "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": "Na tha ùr ann am Mastodon", + "multiple_account_switch_intro_description": "Geàrr leum eadar iomadh cunntas le cumail sìos putan na pròifil.", + "accessibility_hint": "Thoir gnogag dhùbailte a’ leigeil seachad an draoidh seo" } } } \ No newline at end of file From c13d1e2c0d0fe9041f590d8ec09f778b185a5c87 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 12:17:24 +0200 Subject: [PATCH 75/95] New translations Localizable.stringsdict (Scottish Gaelic) --- .../StringsConvertor/input/gd_GB/Localizable.stringsdict | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Localization/StringsConvertor/input/gd_GB/Localizable.stringsdict b/Localization/StringsConvertor/input/gd_GB/Localizable.stringsdict index 41e592a5e..7a54f553e 100644 --- a/Localization/StringsConvertor/input/gd_GB/Localizable.stringsdict +++ b/Localization/StringsConvertor/input/gd_GB/Localizable.stringsdict @@ -13,13 +13,13 @@ NSStringFormatValueTypeKey ld one - 1 unread notification + %ld bhrath nach deach a leughadh two - %ld unread notification + %ld bhrath nach deach a leughadh few - %ld unread notification + %ld brathan nach deach a leughadh other - %ld unread notification + %ld brath nach deach a leughadh a11y.plural.count.input_limit_exceeds From f778d37ea20f2b4ba31323c15b2c9eaeb298332a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 16:26:41 +0200 Subject: [PATCH 76/95] New translations app.json (Kurmanji (Kurdish)) --- Localization/StringsConvertor/input/kmr_TR/app.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 926002627..a2b990128 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -296,13 +296,13 @@ "title": "Tiştekî dawî.", "subtitle": "We just sent an email to %s,\ntap the link to confirm your account.", "button": { - "open_email_app": "Open Email App", - "dont_receive_email": "I never got an email" + "open_email_app": "Sepana e-nameyê veke", + "dont_receive_email": "Min hîç e-nameyeke nesitand" }, "dont_receive_email": { - "title": "Check your email", - "description": "Check if your email address is correct as well as your junk folder if you haven’t.", - "resend_email": "Resend Email" + "title": "E-nameyê xwe kontrol bike", + "description": "Kontrol bike ka navnîşana e-nameya te rast e û her wiha peldanka xwe ya spam.", + "resend_email": "E-namyê yê dîsa bişîne" }, "open_email_app": { "title": "Check your inbox.", From 62a932825e1958f984623c224d6dcc73251f9695 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 17:23:26 +0200 Subject: [PATCH 77/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index a2b990128..45c224fa4 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -307,69 +307,69 @@ "open_email_app": { "title": "Check your inbox.", "description": "We just sent you an email. Check your junk folder if you haven’t.", - "mail": "Mail", - "open_email_client": "Open Email Client" + "mail": "E-name", + "open_email_client": "Rajegirê e-nameyê veke" } }, "home_timeline": { - "title": "Home", + "title": "Serrûpel", "navigation_bar_state": { - "offline": "Offline", - "new_posts": "See new posts", - "published": "Published!", - "Publishing": "Publishing post..." + "offline": "Derhêl", + "new_posts": "Şandiyên nû bibîne", + "published": "Hate weşandin!", + "Publishing": "Şandî tê weşandin..." } }, "suggestion_account": { - "title": "Find People to Follow", - "follow_explain": "When you follow someone, you’ll see their posts in your home feed." + "title": "Kesên bo ku bişopînî bibîne", + "follow_explain": "Gava tu kesekî dişopînî, tu yê şandiyê wan di serrûpelê de bibîne." }, "compose": { "title": { - "new_post": "New Post", - "new_reply": "New Reply" + "new_post": "Şandiya nû", + "new_reply": "Bersiva nû" }, "media_selection": { - "camera": "Take Photo", - "photo_library": "Photo Library", - "browse": "Browse" + "camera": "Wêne bikişîne", + "photo_library": "Wênegeh", + "browse": "Bigere" }, "content_input_placeholder": "Type or paste what’s on your mind", - "compose_action": "Publish", - "replying_to_user": "replying to %s", + "compose_action": "Biweşîne", + "replying_to_user": "bersiv bide %s", "attachment": { - "photo": "photo", - "video": "video", - "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.", + "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..." }, "poll": { - "duration_time": "Duration: %s", - "thirty_minutes": "30 minutes", - "one_hour": "1 Hour", - "six_hours": "6 Hours", - "one_day": "1 Day", - "three_days": "3 Days", - "seven_days": "7 Days", - "option_number": "Option %ld" + "duration_time": "Dirêjî: %s", + "thirty_minutes": "30 xulek", + "one_hour": "1 Demjimêr", + "six_hours": "6 Demjimêr", + "one_day": "1 Roj", + "three_days": "3 Roj", + "seven_days": "7 Roj", + "option_number": "Vebijêrk %ld" }, "content_warning": { "placeholder": "Write an accurate warning here..." }, "visibility": { - "public": "Public", - "unlisted": "Unlisted", - "private": "Followers only", - "direct": "Only people I mention" + "public": "Gelemperî", + "unlisted": "Nerêzokkirî", + "private": "Tenê şopîneran", + "direct": "Tenê mirovên ku min qalkirî" }, "auto_complete": { "space_to_add": "Space to add" }, "accessibility": { - "append_attachment": "Add Attachment", - "append_poll": "Add Poll", - "remove_poll": "Remove Poll", + "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", "disable_content_warning": "Hişyariya naverokê neçalak bike", From 59faa09fe2268dd20ff378d110c4e7bab26f298d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 19:02:52 +0200 Subject: [PATCH 78/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 45c224fa4..5821049bb 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -470,30 +470,30 @@ "title": "Post from %s" }, "settings": { - "title": "Settings", + "title": "Sazkarî", "section": { "appearance": { - "title": "Appearance", - "automatic": "Automatic", - "light": "Always Light", - "dark": "Always Dark" + "title": "Xuyang", + "automatic": "Xweber", + "light": "Her dem ronî", + "dark": "Her dem tarî" }, "notifications": { - "title": "Notifications", - "favorites": "Favorites my post", - "follows": "Follows me", + "title": "Agahdarî", + "favorites": "Şandiyên min hez kir", + "follows": "Min şopand", "boosts": "Reblogs my post", - "mentions": "Mentions me", + "mentions": "Qale min kir", "trigger": { - "anyone": "anyone", - "follower": "a follower", - "follow": "anyone I follow", - "noone": "no one", - "title": "Notify me when" + "anyone": "her kes", + "follower": "şopînerek", + "follow": "her kesê ku dişopînim", + "noone": "ne yek", + "title": "Min agahdar bike gava" } }, "preference": { - "title": "Preferences", + "title": "Hilbijarte", "true_black_dark_mode": "True black dark mode", "disable_avatar_animation": "Disable animated avatars", "disable_emoji_animation": "Disable animated emojis", From bc10d782870450f67b437fa7d8673dd53bc910ee Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 27 Oct 2021 20:16:43 +0200 Subject: [PATCH 79/95] New translations app.json (Kurmanji (Kurdish)) --- .../StringsConvertor/input/kmr_TR/app.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Localization/StringsConvertor/input/kmr_TR/app.json b/Localization/StringsConvertor/input/kmr_TR/app.json index 5821049bb..9798c86c2 100644 --- a/Localization/StringsConvertor/input/kmr_TR/app.json +++ b/Localization/StringsConvertor/input/kmr_TR/app.json @@ -519,30 +519,30 @@ } }, "report": { - "title": "Report %s", - "step1": "Step 1 of 2", - "step2": "Step 2 of 2", + "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?", - "send": "Send Report", - "skip_to_send": "Send without comment", + "send": "Ragihandinê bişîne", + "skip_to_send": "Bêyî şirove bişîne", "text_placeholder": "Type or paste additional comments" }, "preview": { "keyboard": { - "close_preview": "Close Preview", - "show_next": "Show Next", - "show_previous": "Show Previous" + "close_preview": "Pêşdîtin bigire", + "show_next": "A pêş nîşan bide", + "show_previous": "A paş nîşan bide" } }, "account_list": { - "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher", + "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", - "add_account": "Add Account" + "add_account": "Ajimêr tevlî bike" }, "wizard": { - "new_in_mastodon": "New in Mastodon", - "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.", + "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" } } From 19db0afa3eb5bb09f79772b0e4e2d86e0067afdd Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 28 Oct 2021 19:17:41 +0800 Subject: [PATCH 80/95] feat: update for new iPad UI --- Mastodon.xcodeproj/project.pbxproj | 8 + .../xcschemes/xcschememanagement.plist | 8 +- Mastodon/Coordinator/SceneCoordinator.swift | 105 ++++--- Mastodon/Generated/Assets.swift | 20 +- .../Sidebar}/Contents.json | 0 .../Scene/Sidebar/logo.imageset/Contents.json | 15 + .../Scene/Sidebar/logo.imageset/logo.pdf | 108 +++++++ .../Contents.json | 6 +- .../sidebar.background.colorset/Contents.json | 6 +- .../Background/danger.colorset/Contents.json | 20 -- .../Contents.json | 20 -- .../Contents.json | 38 --- .../Contents.json | 38 --- .../system.background.colorset/Contents.json | 38 --- .../Contents.json | 38 --- .../Contents.json | 38 --- .../Contents.json | 38 --- .../Contents.json | 38 --- .../_Deprecated/Compose/Contents.json | 9 - .../Compose/background.colorset/Contents.json | 38 --- .../toolbar.background.colorset/Contents.json | 38 --- .../Assets.xcassets/_Deprecated/Contents.json | 9 - .../HomeTimelineViewController.swift | 30 +- .../HomeTimeline/HomeTimelineViewModel.swift | 3 +- .../Root/ContentSplitViewController.swift | 92 ++++++ .../MainTab/MainTabBarController+Wizard.swift | 3 + .../Scene/Root/RootSplitViewController.swift | 274 +++++++--------- .../Root/Sidebar/SidebarViewController.swift | 200 +++++++----- .../Scene/Root/Sidebar/SidebarViewModel.swift | 295 +++++------------- .../View/SidebarListCollectionViewCell.swift | 15 - .../Sidebar/View/SidebarListContentView.swift | 149 ++------- .../Sidebar/View/SidebarListHeaderView.swift | 42 +++ .../View/Button/CircleAvatarButton.swift | 7 +- .../Service/ThemeService/SystemTheme.swift | 2 +- 34 files changed, 705 insertions(+), 1083 deletions(-) rename Mastodon/Resources/Assets.xcassets/{_Deprecated/Background => Scene/Sidebar}/Contents.json (100%) create mode 100644 Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/Contents.json create mode 100644 Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/logo.pdf delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/danger.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/onboarding.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.grouped.system.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.system.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.elevated.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.grouped.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.grouped.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/toolbar.background.colorset/Contents.json delete mode 100644 Mastodon/Resources/Assets.xcassets/_Deprecated/Contents.json create mode 100644 Mastodon/Scene/Root/ContentSplitViewController.swift create mode 100644 Mastodon/Scene/Root/Sidebar/View/SidebarListHeaderView.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 3c44f617b..1ab4b2559 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -187,6 +187,8 @@ DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB029E94266A20430062874E /* MastodonAuthenticationController.swift */; }; DB02CDAB26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */; }; DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */; }; + DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */; }; + DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */; }; DB03F7F32689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */; }; DB03F7F52689B782007B274C /* ComposeTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F42689B782007B274C /* ComposeTableView.swift */; }; DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB040ED026538E3C00BEE9D8 /* Trie.swift */; }; @@ -958,6 +960,8 @@ DB029E94266A20430062874E /* MastodonAuthenticationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonAuthenticationController.swift; sourceTree = ""; }; DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadReplyLoaderTableViewCell.swift; sourceTree = ""; }; DB02CDBE2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdaptiveUserInterfaceStyleBarButtonItem.swift; sourceTree = ""; }; + DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListHeaderView.swift; sourceTree = ""; }; + DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSplitViewController.swift; sourceTree = ""; }; DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToStatusContentTableViewCell.swift; sourceTree = ""; }; DB03F7F42689B782007B274C /* ComposeTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTableView.swift; sourceTree = ""; }; DB040ED026538E3C00BEE9D8 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = ""; }; @@ -2125,6 +2129,7 @@ DBF156DE2701B17600EC00B7 /* SidebarAddAccountCollectionViewCell.swift */, DB0EF72A26FDB1D200347686 /* SidebarListCollectionViewCell.swift */, DB0EF72D26FDB24F00347686 /* SidebarListContentView.swift */, + DB03A792272A7E5700EE37C5 /* SidebarListHeaderView.swift */, ); path = View; sourceTree = ""; @@ -2587,6 +2592,7 @@ isa = PBXGroup; children = ( DB852D1B26FB021500FC9D81 /* RootSplitViewController.swift */, + DB03A794272A981400EE37C5 /* ContentSplitViewController.swift */, DB852D1A26FAED0100FC9D81 /* Sidebar */, DB8AF54E25C13703002E6C99 /* MainTab */, ); @@ -3873,6 +3879,7 @@ DBA94440265D137600C537E1 /* Mastodon+Entity+Field.swift in Sources */, DB49A61425FF2C5600B98345 /* EmojiService.swift in Sources */, DBBF1DC7265251D400E5B703 /* AutoCompleteViewModel+State.swift in Sources */, + DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */, DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */, DBCC3B9526157E6E0045B23D /* APIService+Relationship.swift in Sources */, 2D7631B325C159F700929FB9 /* Item.swift in Sources */, @@ -3935,6 +3942,7 @@ DB71FD4625F8C6D200512AE1 /* StatusProvider+UITableViewDataSourcePrefetching.swift in Sources */, DB297B1B2679FAE200704C90 /* PlaceholderImageCacheService.swift in Sources */, 2D8FCA082637EABB00137F46 /* APIService+FollowRequest.swift in Sources */, + DB03A795272A981400EE37C5 /* ContentSplitViewController.swift in Sources */, 2D152A8C25C295CC009AA50C /* StatusView.swift in Sources */, DBBC24DE26A54BCB00398BB9 /* MastodonMetricFormatter.swift in Sources */, DBB3BA2A26A81C020004F2D4 /* FLAnimatedImageView.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 62b4c7f21..e6093a218 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 42 + 35 CoreDataStack.xcscheme_^#shared#^_ orderHint - 43 + 38 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 41 + 36 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 44 + 37 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 5df6f7e98..23c2a6f44 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -90,45 +90,45 @@ final public class SceneCoordinator { // Delay in next run loop - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - // Note: - // show (push) on phone or pad (compact) - // showDetail in .secondary in UISplitViewController on pad (expand) - let from: UIViewController? = { - if let splitViewController = self.splitViewController { - if splitViewController.mainTabBarController.topMost?.view.window != nil { - // compact - return splitViewController.mainTabBarController.topMost - } else { - // expand - return splitViewController.viewController(for: .supplementary) - } - } else { - return self.tabBarController.topMost - } - }() - - // show notification related content - guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } - let notificationID = String(pushNotification.notificationID) - - switch type { - case .follow: - let profileViewModel = RemoteProfileViewModel(context: appContext, notificationID: notificationID) - self.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) - case .followRequest: - // do nothing - break - case .mention, .reblog, .favourite, .poll, .status: - let threadViewModel = RemoteThreadViewModel(context: appContext, notificationID: notificationID) - self.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show) - case ._other: - assertionFailure() - break - } - } +// DispatchQueue.main.async { [weak self] in +// guard let self = self else { return } +// +// // Note: +// // show (push) on phone or pad (compact) +// // showDetail in .secondary in UISplitViewController on pad (expand) +// let from: UIViewController? = { +// if let splitViewController = self.splitViewController { +// if splitViewController.mainTabBarController.topMost?.view.window != nil { +// // compact +// return splitViewController.mainTabBarController.topMost +// } else { +// // expand +// return splitViewController.viewController(for: .supplementary) +// } +// } else { +// return self.tabBarController.topMost +// } +// }() +// +// // show notification related content +// guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } +// let notificationID = String(pushNotification.notificationID) +// +// switch type { +// case .follow: +// let profileViewModel = RemoteProfileViewModel(context: appContext, notificationID: notificationID) +// self.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) +// case .followRequest: +// // do nothing +// break +// case .mention, .reblog, .favourite, .poll, .status: +// let threadViewModel = RemoteThreadViewModel(context: appContext, notificationID: notificationID) +// self.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show) +// case ._other: +// assertionFailure() +// break +// } +// } // end DispatchQueue.main.async } .store(in: &disposeBag) } @@ -227,7 +227,7 @@ extension SceneCoordinator { default: let splitViewController = RootSplitViewController(context: appContext, coordinator: self) self.splitViewController = splitViewController - self.tabBarController = splitViewController.mainTabBarController + self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController sceneDelegate.window?.rootViewController = splitViewController } } @@ -282,18 +282,19 @@ extension SceneCoordinator { switch transition { case .show: - if let splitViewController = splitViewController, !splitViewController.isCollapsed, - let supplementaryViewController = splitViewController.viewController(for: .supplementary) as? UINavigationController, - (supplementaryViewController === presentingViewController || supplementaryViewController.viewControllers.contains(presentingViewController)) || - (presentingViewController is UserTimelineViewController && presentingViewController.view.isDescendant(of: supplementaryViewController.view)) - { - fallthrough - } else { - if secondaryStackHashValues.contains(presentingViewController.hashValue) { - secondaryStackHashValues.insert(viewController.hashValue) - } - presentingViewController.show(viewController, sender: sender) - } +// if let splitViewController = splitViewController, !splitViewController.isCollapsed, +// let supplementaryViewController = splitViewController.viewController(for: .supplementary) as? UINavigationController, +// (supplementaryViewController === presentingViewController || supplementaryViewController.viewControllers.contains(presentingViewController)) || +// (presentingViewController is UserTimelineViewController && presentingViewController.view.isDescendant(of: supplementaryViewController.view)) +// { +// fallthrough +// } else { +// if secondaryStackHashValues.contains(presentingViewController.hashValue) { +// secondaryStackHashValues.insert(viewController.hashValue) +// } +// presentingViewController.show(viewController, sender: sender) +// } + presentingViewController.show(viewController, sender: sender) case .showDetail: secondaryStackHashValues.insert(viewController.hashValue) let navigationController = AdaptiveStatusBarStyleNavigationController(rootViewController: viewController) diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index 96fe0fca8..906dd74e2 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -96,6 +96,9 @@ internal enum Asset { internal static let usernameGray = ColorAsset(name: "Scene/Profile/Banner/username.gray") } } + internal enum Sidebar { + internal static let logo = ImageAsset(name: "Scene/Sidebar/logo") + } internal enum Welcome { internal enum Illustration { internal static let backgroundCyan = ColorAsset(name: "Scene/Welcome/illustration/background.cyan") @@ -160,23 +163,6 @@ internal enum Asset { internal static let tabBarItemInactiveIconColor = ColorAsset(name: "Theme/system/tab.bar.item.inactive.icon.color") } } - internal enum Deprecated { - internal enum Background { - internal static let danger = ColorAsset(name: "_Deprecated/Background/danger") - internal static let onboardingBackground = ColorAsset(name: "_Deprecated/Background/onboarding.background") - internal static let secondaryGroupedSystemBackground = ColorAsset(name: "_Deprecated/Background/secondary.grouped.system.background") - internal static let secondarySystemBackground = ColorAsset(name: "_Deprecated/Background/secondary.system.background") - internal static let systemBackground = ColorAsset(name: "_Deprecated/Background/system.background") - internal static let systemElevatedBackground = ColorAsset(name: "_Deprecated/Background/system.elevated.background") - internal static let systemGroupedBackground = ColorAsset(name: "_Deprecated/Background/system.grouped.background") - internal static let tertiarySystemBackground = ColorAsset(name: "_Deprecated/Background/tertiary.system.background") - internal static let tertiarySystemGroupedBackground = ColorAsset(name: "_Deprecated/Background/tertiary.system.grouped.background") - } - internal enum Compose { - internal static let background = ColorAsset(name: "_Deprecated/Compose/background") - internal static let toolbarBackground = ColorAsset(name: "_Deprecated/Compose/toolbar.background") - } - } } // swiftlint:enable identifier_name line_length nesting type_body_length type_name diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/Contents.json b/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/Contents.json similarity index 100% rename from Mastodon/Resources/Assets.xcassets/_Deprecated/Background/Contents.json rename to Mastodon/Resources/Assets.xcassets/Scene/Sidebar/Contents.json diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/Contents.json new file mode 100644 index 000000000..4f547d09b --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "logo.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/logo.pdf b/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/logo.pdf new file mode 100644 index 000000000..908727a57 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Scene/Sidebar/logo.imageset/logo.pdf @@ -0,0 +1,108 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 -0.103455 cm +0.168627 0.564706 0.850980 scn +27.796436 10.091343 m +33.035133 10.719734 37.596470 13.962151 38.169762 16.924883 c +39.073063 21.591980 38.998501 28.314186 38.998501 28.314186 c +38.998501 37.425270 33.056084 40.095867 33.056084 40.095867 c +30.059872 41.478233 24.914881 42.059555 19.569633 42.103455 c +19.438305 42.103455 l +14.093056 42.059555 8.951445 41.478233 5.955006 40.095867 c +5.955006 40.095867 0.012361 37.425270 0.012361 28.314186 c +0.012361 27.761837 0.009520 27.180878 0.006561 26.576080 c +-0.001656 24.896429 -0.010772 23.032921 0.037591 21.087820 c +0.253392 12.177679 1.663759 3.396290 9.864657 1.215820 c +13.645910 0.210445 16.892391 0.000000 19.507011 0.144371 c +24.248556 0.408443 26.910255 1.844212 26.910255 1.844212 c +26.753922 5.300014 l +26.753922 5.300014 23.365528 4.226753 19.560173 4.357544 c +15.789957 4.487431 11.809797 4.765984 11.200012 9.415886 c +11.143697 9.824329 11.115539 10.261055 11.115539 10.719732 c +11.115539 10.719732 14.816599 9.810978 19.507011 9.595104 c +22.375050 9.462955 25.064680 9.763912 27.796436 10.091343 c +h +31.989010 16.575367 m +31.989010 27.607372 l +31.989010 29.862061 31.417519 31.653776 30.269808 32.979347 c +29.085829 34.304916 27.535576 34.984444 25.611385 34.984444 c +23.384670 34.984444 21.698582 34.124794 20.583984 32.405266 c +19.500023 30.580288 l +18.416286 32.405266 l +17.301464 34.124794 15.615376 34.984444 13.388884 34.984444 c +11.464469 34.984444 9.914215 34.304916 8.730462 32.979347 c +7.582527 31.653776 7.011036 29.862061 7.011036 27.607372 c +7.011036 16.575367 l +11.361976 16.575367 l +11.361976 27.283108 l +11.361976 29.540287 12.307401 30.685961 14.198477 30.685961 c +16.289360 30.685961 17.337505 29.326900 17.337505 26.639557 c +17.337505 20.778585 l +21.662764 20.778585 l +21.662764 26.639557 l +21.662764 29.326900 22.710684 30.685961 24.801567 30.685961 c +26.692642 30.685961 27.638069 29.540287 27.638069 27.283108 c +27.638069 16.575367 l +31.989010 16.575367 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2035 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 39.000000 42.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000002125 00000 n +0000002148 00000 n +0000002321 00000 n +0000002395 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2454 +%%EOF \ No newline at end of file diff --git a/Mastodon/Resources/Assets.xcassets/Theme/system/Background/secondary.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/system/Background/secondary.system.background.colorset/Contents.json index b9a69ec7d..77d24b11d 100644 --- a/Mastodon/Resources/Assets.xcassets/Theme/system/Background/secondary.system.background.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/system/Background/secondary.system.background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "46", - "green" : "44", - "red" : "44" + "blue" : "0x2E", + "green" : "0x2C", + "red" : "0x2C" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Theme/system/Background/sidebar.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/system/Background/sidebar.background.colorset/Contents.json index e30d6cabe..ee5b1c373 100644 --- a/Mastodon/Resources/Assets.xcassets/Theme/system/Background/sidebar.background.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/system/Background/sidebar.background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.263", - "green" : "0.208", - "red" : "0.192" + "blue" : "0x2E", + "green" : "0x2C", + "red" : "0x2C" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/danger.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/danger.colorset/Contents.json deleted file mode 100644 index dabccc33e..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/danger.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.353", - "green" : "0.251", - "red" : "0.875" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/onboarding.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/onboarding.background.colorset/Contents.json deleted file mode 100644 index 0e4687fb4..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/onboarding.background.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.910", - "green" : "0.882", - "red" : "0.851" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.grouped.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.grouped.system.background.colorset/Contents.json deleted file mode 100644 index ef6c7f7b1..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.grouped.system.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.996", - "green" : "1.000", - "red" : "0.996" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.263", - "green" : "0.208", - "red" : "0.192" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.system.background.colorset/Contents.json deleted file mode 100644 index c915c8911..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/secondary.system.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.910", - "green" : "0.882", - "red" : "0.851" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.106", - "red" : "0.098" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.background.colorset/Contents.json deleted file mode 100644 index 4572c2409..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.996", - "green" : "1.000", - "red" : "0.996" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.173", - "red" : "0.157" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.elevated.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.elevated.background.colorset/Contents.json deleted file mode 100644 index 33b71ef90..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.elevated.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.173", - "red" : "0.157" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.grouped.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.grouped.background.colorset/Contents.json deleted file mode 100644 index c915c8911..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/system.grouped.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.910", - "green" : "0.882", - "red" : "0.851" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.106", - "red" : "0.098" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.background.colorset/Contents.json deleted file mode 100644 index 4572c2409..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.996", - "green" : "1.000", - "red" : "0.996" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.173", - "red" : "0.157" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.grouped.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.grouped.background.colorset/Contents.json deleted file mode 100644 index 98dd7bbde..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Background/tertiary.system.grouped.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.910", - "green" : "0.882", - "red" : "0.851" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.263", - "green" : "0.208", - "red" : "0.192" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/Contents.json deleted file mode 100644 index 6e965652d..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/background.colorset/Contents.json deleted file mode 100644 index 33b71ef90..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.173", - "red" : "0.157" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/toolbar.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/toolbar.background.colorset/Contents.json deleted file mode 100644 index da7b76069..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Compose/toolbar.background.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.871", - "green" : "0.847", - "red" : "0.839" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.920", - "blue" : "0.125", - "green" : "0.125", - "red" : "0.125" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mastodon/Resources/Assets.xcassets/_Deprecated/Contents.json b/Mastodon/Resources/Assets.xcassets/_Deprecated/Contents.json deleted file mode 100644 index 6e965652d..000000000 --- a/Mastodon/Resources/Assets.xcassets/_Deprecated/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index dbd552d97..55bf544ca 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -114,6 +114,24 @@ extension HomeTimelineViewController { #endif } .store(in: &disposeBag) + #if DEBUG + // long press to trigger debug menu + settingBarButtonItem.menu = debugMenu + #else + settingBarButtonItem.target = self + settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:)) + #endif + + viewModel.displayComposeBarButtonItem + .receive(on: DispatchQueue.main) + .sink { [weak self] displayComposeBarButtonItem in + guard let self = self else { return } + self.navigationItem.rightBarButtonItem = displayComposeBarButtonItem ? self.composeBarButtonItem : nil + } + .store(in: &disposeBag) + composeBarButtonItem.target = self + composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:)) + navigationItem.titleView = titleView titleView.delegate = self @@ -126,18 +144,6 @@ extension HomeTimelineViewController { } .store(in: &disposeBag) - #if DEBUG - // long press to trigger debug menu - settingBarButtonItem.menu = debugMenu - #else - settingBarButtonItem.target = self - settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:)) - #endif - - navigationItem.rightBarButtonItem = composeBarButtonItem - composeBarButtonItem.target = self - composeBarButtonItem.action = #selector(HomeTimelineViewController.composeBarButtonItemPressed(_:)) - tableView.refreshControl = refreshControl refreshControl.addTarget(self, action: #selector(HomeTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift index a3fbcbd74..c4681b40b 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift @@ -30,6 +30,8 @@ final class HomeTimelineViewModel: NSObject { let homeTimelineNavigationBarTitleViewModel: HomeTimelineNavigationBarTitleViewModel let lastAutomaticFetchTimestamp = CurrentValueSubject(nil) let scrollPositionRecord = CurrentValueSubject(nil) + let displaySettingBarButtonItem = CurrentValueSubject(true) + let displayComposeBarButtonItem = CurrentValueSubject(true) weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate? weak var tableView: UITableView? @@ -70,7 +72,6 @@ final class HomeTimelineViewModel: NSObject { let loadMiddleSateMachineList = CurrentValueSubject<[NSManagedObjectID: GKStateMachine], Never>([:]) // TimelineIndex.objectID : middle loading state machine var diffableDataSource: UITableViewDiffableDataSource? var cellFrameCache = NSCache() - let displaySettingBarButtonItem = CurrentValueSubject(true) init(context: AppContext) { self.context = context diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift new file mode 100644 index 000000000..42ce3afc3 --- /dev/null +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -0,0 +1,92 @@ +// +// ContentSplitViewController.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-28. +// + +import os.log +import UIKit +import Combine +import CoreDataStack + +final class ContentSplitViewController: UIViewController, NeedsDependency { + + var disposeBag = Set() + + static let sidebarWidth: CGFloat = 89 + + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } + weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } + + private(set) lazy var sidebarViewController: SidebarViewController = { + let sidebarViewController = SidebarViewController() + sidebarViewController.context = context + sidebarViewController.coordinator = coordinator + sidebarViewController.viewModel = SidebarViewModel(context: context) + sidebarViewController.delegate = self + return sidebarViewController + }() + + @Published var currentSupplementaryTab: MainTabBarController.Tab = .home + private(set) lazy var mainTabBarController: MainTabBarController = { + let mainTabBarController = MainTabBarController(context: context, coordinator: coordinator) + return mainTabBarController + }() + + deinit { + os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + } + +} + +extension ContentSplitViewController { + override func viewDidLoad() { + super.viewDidLoad() + + navigationController?.setNavigationBarHidden(true, animated: false) + + addChild(sidebarViewController) + sidebarViewController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(sidebarViewController.view) + sidebarViewController.didMove(toParent: self) + NSLayoutConstraint.activate([ + sidebarViewController.view.topAnchor.constraint(equalTo: view.topAnchor), + sidebarViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + sidebarViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + sidebarViewController.view.widthAnchor.constraint(equalToConstant: ContentSplitViewController.sidebarWidth), + ]) + + addChild(mainTabBarController) + mainTabBarController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(mainTabBarController.view) + sidebarViewController.didMove(toParent: self) + NSLayoutConstraint.activate([ + mainTabBarController.view.topAnchor.constraint(equalTo: view.topAnchor), + mainTabBarController.view.leadingAnchor.constraint(equalTo: sidebarViewController.view.trailingAnchor), + mainTabBarController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + mainTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + + $currentSupplementaryTab + .removeDuplicates() + .sink(receiveValue: { [weak self] tab in + guard let self = self else { return } + self.mainTabBarController.selectedIndex = tab.rawValue + self.mainTabBarController.currentTab.value = tab + }) + .store(in: &disposeBag) + } +} + +// MARK: - SidebarViewControllerDelegate +extension ContentSplitViewController: SidebarViewControllerDelegate { + + func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) { + guard let _ = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { + assertionFailure() + return + } + currentSupplementaryTab = tab + } +} diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController+Wizard.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController+Wizard.swift index 8f3f2eea4..b69a6b786 100644 --- a/Mastodon/Scene/Root/MainTab/MainTabBarController+Wizard.swift +++ b/Mastodon/Scene/Root/MainTab/MainTabBarController+Wizard.swift @@ -70,6 +70,9 @@ 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) diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 15464c9db..4f818ea9d 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -14,57 +14,44 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { var disposeBag = Set() + static let sidebarWidth: CGFloat = 89 + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } - private(set) lazy var sidebarViewController: SidebarViewController = { - let sidebarViewController = SidebarViewController() - sidebarViewController.context = context - sidebarViewController.coordinator = coordinator - sidebarViewController.viewModel = SidebarViewModel(context: context) - sidebarViewController.delegate = self - return sidebarViewController + private(set) lazy var contentSplitViewController: ContentSplitViewController = { + let contentSplitViewController = ContentSplitViewController() + contentSplitViewController.context = context + contentSplitViewController.coordinator = coordinator + return contentSplitViewController }() - - var currentSupplementaryTab: MainTabBarController.Tab = .home - private(set) lazy var supplementaryViewControllers: [UIViewController] = { - let viewControllers = MainTabBarController.Tab.allCases.map { tab in - tab.viewController(context: context, coordinator: coordinator) - } - for viewController in viewControllers { - guard let navigationController = viewController as? UINavigationController else { - assertionFailure() - continue - } - if let homeViewController = navigationController.topViewController as? HomeTimelineViewController { - homeViewController.viewModel.displaySettingBarButtonItem.value = false - } - } - return viewControllers - }() - - private(set) lazy var mainTabBarController = MainTabBarController(context: context, coordinator: coordinator) init(context: AppContext, coordinator: SceneCoordinator) { self.context = context self.coordinator = coordinator - super.init(style: .tripleColumn) + super.init(style: .doubleColumn) + primaryEdge = .trailing primaryBackgroundStyle = .sidebar - preferredDisplayMode = .oneBesideSecondary + preferredDisplayMode = .twoBesideSecondary preferredSplitBehavior = .tile delegate = self + // disable edge swipe gesture + presentsWithGesture = false + if #available(iOS 14.5, *) { - displayModeButtonVisibility = .always + displayModeButtonVisibility = .never } else { // Fallback on earlier versions } - setViewController(sidebarViewController, for: .primary) - setViewController(supplementaryViewControllers[0], for: .supplementary) - setViewController(SecondaryPlaceholderViewController(), for: .secondary) - setViewController(mainTabBarController, for: .compact) + setViewController(UIViewController(), for: .primary) + setViewController(contentSplitViewController, for: .secondary) + + contentSplitViewController.sidebarViewController.view.layer.zPosition = 100 + contentSplitViewController.mainTabBarController.view.layer.zPosition = 90 + view.layer.zPosition = 80 } required init?(coder: NSCoder) { @@ -83,16 +70,11 @@ extension RootSplitViewController { super.viewDidLoad() updateBehavior(size: view.frame.size) - - mainTabBarController.currentTab + contentSplitViewController.$currentSupplementaryTab .receive(on: DispatchQueue.main) - .sink { [weak self] tab in + .sink { [weak self] _ in guard let self = self else { return } - guard tab != self.currentSupplementaryTab else { return } - guard let index = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { return } - self.currentSupplementaryTab = tab - self.setViewController(self.supplementaryViewControllers[index], for: .supplementary) - + self.updateBehavior(size: self.view.frame.size) } .store(in: &disposeBag) } @@ -100,133 +82,111 @@ extension RootSplitViewController { override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) - updateBehavior(size: size) + coordinator.animate { [weak self] context in + guard let self = self else { return } + self.updateBehavior(size: size) + } completion: { context in + // do nothing + } } private func updateBehavior(size: CGSize) { - // fix secondary too small on iPad mini issue - if size.width > 960 { - preferredDisplayMode = .oneBesideSecondary - preferredSplitBehavior = .tile - } else { - preferredDisplayMode = .oneBesideSecondary - preferredSplitBehavior = .displace - } - } - -} - -// MARK: - SidebarViewControllerDelegate -extension RootSplitViewController: SidebarViewControllerDelegate { - - func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) { - - guard let index = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { - assertionFailure() - return - } - currentSupplementaryTab = tab - setViewController(supplementaryViewControllers[index], for: .supplementary) - } - - func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectSearchHistory searchHistoryViewModel: SidebarViewModel.SearchHistoryViewModel) { - // self.sidebarViewController(sidebarViewController, didSelectTab: .search) - - let supplementaryViewController = viewController(for: .supplementary) - let managedObjectContext = context.managedObjectContext - managedObjectContext.perform { - let searchHistory = managedObjectContext.object(with: searchHistoryViewModel.searchHistoryObjectID) as! SearchHistory - if let account = searchHistory.account { - DispatchQueue.main.async { - let profileViewModel = CachedProfileViewModel(context: self.context, mastodonUser: account) - self.coordinator.present(scene: .profile(viewModel: profileViewModel), from: supplementaryViewController, transition: .show) - } - } else if let hashtag = searchHistory.hashtag { - DispatchQueue.main.async { - let hashtagTimelineViewModel = HashtagTimelineViewModel(context: self.context, hashtag: hashtag.name) - self.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: supplementaryViewController, transition: .show) - } + switch contentSplitViewController.currentSupplementaryTab { + case .search: + hide(.primary) + default: + if size.width > 960 { + show(.primary) } else { - assertionFailure() + hide(.primary) } } } - + } // MARK: - UISplitViewControllerDelegate extension RootSplitViewController: UISplitViewControllerDelegate { - // .regular to .compact - // move navigation stack from .supplementary & .secondary to .compact - func splitViewController( - _ svc: UISplitViewController, - topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column - ) -> UISplitViewController.Column { - switch proposedTopColumn { - case .compact: - guard let index = MainTabBarController.Tab.allCases.firstIndex(of: currentSupplementaryTab) else { - assertionFailure() - break - } - mainTabBarController.selectedIndex = index - mainTabBarController.currentTab.value = currentSupplementaryTab - - guard let navigationController = mainTabBarController.selectedViewController as? UINavigationController else { break } - navigationController.popToRootViewController(animated: false) - var viewControllers = navigationController.viewControllers // init navigation stack with topMost - - if let supplementaryNavigationController = viewController(for: .supplementary) as? UINavigationController { - // append supplementary - viewControllers.append(contentsOf: supplementaryNavigationController.popToRootViewController(animated: true) ?? []) - } - if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { - // append secondary - viewControllers.append(contentsOf: secondaryNavigationController.popToRootViewController(animated: true) ?? []) - } - // set navigation stack - navigationController.setViewControllers(viewControllers, animated: false) - - default: - assertionFailure() - } - - return proposedTopColumn - } - - // .compact to .regular - // restore navigation stack to .supplementary & .secondary - func splitViewController( - _ svc: UISplitViewController, - displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode - ) -> UISplitViewController.DisplayMode { - let compactNavigationController = mainTabBarController.selectedViewController as? UINavigationController - - if let topMost = compactNavigationController?.topMost, - topMost is AccountListViewController { - topMost.dismiss(animated: false, completion: nil) - } - - let viewControllers = compactNavigationController?.popToRootViewController(animated: true) ?? [] - - var supplementaryViewControllers: [UIViewController] = [] - var secondaryViewControllers: [UIViewController] = [] - for viewController in viewControllers { - if coordinator.secondaryStackHashValues.contains(viewController.hashValue) { - secondaryViewControllers.append(viewController) - } else { - supplementaryViewControllers.append(viewController) - } - - } - if let supplementary = viewController(for: .supplementary) as? UINavigationController { - supplementary.setViewControllers(supplementary.viewControllers + supplementaryViewControllers, animated: false) - } - if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { - secondaryNavigationController.setViewControllers(secondaryNavigationController.viewControllers + secondaryViewControllers, animated: false) - } - - return proposedDisplayMode - } +// // .regular to .compact +// // move navigation stack from .supplementary & .secondary to .compact +// func splitViewController( +// _ svc: UISplitViewController, +// topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column +// ) -> UISplitViewController.Column { +// switch proposedTopColumn { +// case .compact: +// guard let index = MainTabBarController.Tab.allCases.firstIndex(of: currentSupplementaryTab) else { +// assertionFailure() +// break +// } +// mainTabBarController.selectedIndex = index +// mainTabBarController.currentTab.value = currentSupplementaryTab +// +// guard let navigationController = mainTabBarController.selectedViewController as? UINavigationController else { break } +// navigationController.popToRootViewController(animated: false) +// var viewControllers = navigationController.viewControllers // init navigation stack with topMost +// +// if let supplementaryNavigationController = viewController(for: .supplementary) as? UINavigationController { +// // append supplementary +// viewControllers.append(contentsOf: supplementaryNavigationController.popToRootViewController(animated: true) ?? []) +// } +// if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { +// // append secondary +// viewControllers.append(contentsOf: secondaryNavigationController.popToRootViewController(animated: true) ?? []) +// } +// // set navigation stack +// navigationController.setViewControllers(viewControllers, animated: false) +// +// default: +// assertionFailure() +// } +// +// return proposedTopColumn +// } +// +// // .compact to .regular +// // restore navigation stack to .supplementary & .secondary +// func splitViewController( +// _ svc: UISplitViewController, +// displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode +// ) -> UISplitViewController.DisplayMode { +// let compactNavigationController = mainTabBarController.selectedViewController as? UINavigationController +// +// if let topMost = compactNavigationController?.topMost, +// topMost is AccountListViewController { +// topMost.dismiss(animated: false, completion: nil) +// } +// +// let viewControllers = compactNavigationController?.popToRootViewController(animated: true) ?? [] +// +// var supplementaryViewControllers: [UIViewController] = [] +// var secondaryViewControllers: [UIViewController] = [] +// for viewController in viewControllers { +// if coordinator.secondaryStackHashValues.contains(viewController.hashValue) { +// secondaryViewControllers.append(viewController) +// } else { +// supplementaryViewControllers.append(viewController) +// } +// +// } +// if let supplementary = viewController(for: .supplementary) as? UINavigationController { +// supplementary.setViewControllers(supplementary.viewControllers + supplementaryViewControllers, animated: false) +// } +// if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { +// secondaryNavigationController.setViewControllers(secondaryNavigationController.viewControllers + secondaryViewControllers, animated: false) +// } +// +// return proposedDisplayMode +// } } + +//extension UIView { +// func setNeedsLayoutForSubviews() { +// self.subviews.forEach({ +// $0.setNeedsLayout() +// $0.setNeedsLayoutForSubviews() +// }) +// } +//} diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift index 69d9f55c8..060091922 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift @@ -12,7 +12,6 @@ import CoreDataStack protocol SidebarViewControllerDelegate: AnyObject { func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) - func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectSearchHistory searchHistoryViewModel: SidebarViewModel.SearchHistoryViewModel) } final class SidebarViewController: UIViewController, NeedsDependency { @@ -21,28 +20,46 @@ final class SidebarViewController: UIViewController, NeedsDependency { weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } var disposeBag = Set() + var observations = Set() var viewModel: SidebarViewModel! weak var delegate: SidebarViewControllerDelegate? - - let settingBarButtonItem: UIBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.tintColor = Asset.Colors.brandBlue.color - barButtonItem.image = UIImage(systemName: "gear")?.withRenderingMode(.alwaysTemplate) - return barButtonItem - }() static func createLayout() -> UICollectionViewLayout { let layout = UICollectionViewCompositionalLayout() { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in var configuration = UICollectionLayoutListConfiguration(appearance: .sidebar) configuration.backgroundColor = .clear - if sectionIndex == SidebarViewModel.Section.tab.rawValue { - // with indentation - configuration.headerMode = .none - } else { - // remove indentation - configuration.headerMode = .firstItemInSection + configuration.showsSeparators = false + let section = NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: layoutEnvironment) + switch sectionIndex { + case 0: + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(100)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top + ) + section.boundarySupplementaryItems = [header] + default: + break } + return section + } + return layout + } + + let collectionView: UICollectionView = { + let layout = SidebarViewController.createLayout() + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.alwaysBounceVertical = false + collectionView.backgroundColor = .clear + return collectionView + }() + + static func createSecondaryLayout() -> UICollectionViewLayout { + let layout = UICollectionViewCompositionalLayout() { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in + var configuration = UICollectionLayoutListConfiguration(appearance: .sidebar) + configuration.backgroundColor = .clear configuration.showsSeparators = false let section = NSCollectionLayoutSection.list(using: configuration, layoutEnvironment: layoutEnvironment) return section @@ -50,12 +67,15 @@ final class SidebarViewController: UIViewController, NeedsDependency { return layout } - let collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: SidebarViewController.createLayout()) + let secondaryCollectionView: UICollectionView = { + let layout = SidebarViewController.createSecondaryLayout() + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.isScrollEnabled = false + collectionView.alwaysBounceVertical = false collectionView.backgroundColor = .clear return collectionView }() - + var secondaryCollectionViewHeightLayoutConstraint: NSLayoutConstraint! } extension SidebarViewController { @@ -63,23 +83,7 @@ extension SidebarViewController { override func viewDidLoad() { super.viewDidLoad() - viewModel.context.authenticationService.activeMastodonAuthenticationBox - .receive(on: DispatchQueue.main) - .sink { [weak self] activeMastodonAuthenticationBox in - guard let self = self else { return } - let domain = activeMastodonAuthenticationBox?.domain - self.navigationItem.backBarButtonItem = { - let barButtonItem = UIBarButtonItem() - barButtonItem.image = UIImage(systemName: "sidebar.leading") - return barButtonItem - }() - self.navigationItem.title = domain - } - .store(in: &disposeBag) - navigationItem.rightBarButtonItem = settingBarButtonItem - settingBarButtonItem.target = self - settingBarButtonItem.action = #selector(SidebarViewController.settingBarButtonItemPressed(_:)) - navigationController?.navigationBar.prefersLargeTitles = true + navigationController?.setNavigationBarHidden(true, animated: false) setupBackground(theme: ThemeService.shared.currentTheme.value) ThemeService.shared.currentTheme @@ -99,65 +103,101 @@ extension SidebarViewController { collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) + secondaryCollectionView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(secondaryCollectionView) + secondaryCollectionViewHeightLayoutConstraint = secondaryCollectionView.heightAnchor.constraint(equalToConstant: 44).priority(.required - 1) + NSLayoutConstraint.activate([ + secondaryCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + secondaryCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + view.bottomAnchor.constraint(equalTo: secondaryCollectionView.bottomAnchor), + secondaryCollectionViewHeightLayoutConstraint, + ]) + collectionView.delegate = self - viewModel.setupDiffableDataSource(collectionView: collectionView) + secondaryCollectionView.delegate = self + viewModel.setupDiffableDataSource( + collectionView: collectionView, + secondaryCollectionView: secondaryCollectionView + ) + + secondaryCollectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] secondaryCollectionView, _ in + guard let self = self else { return } + let height = secondaryCollectionView.contentSize.height + self.secondaryCollectionViewHeightLayoutConstraint.constant = height + self.collectionView.contentInset.bottom = height + } + .store(in: &observations) } private func setupBackground(theme: Theme) { let color: UIColor = theme.sidebarBackgroundColor - let barAppearance = UINavigationBarAppearance() - barAppearance.configureWithOpaqueBackground() - barAppearance.backgroundColor = color - barAppearance.shadowColor = .clear - barAppearance.shadowImage = UIImage() // remove separator line - navigationItem.standardAppearance = barAppearance - navigationItem.compactAppearance = barAppearance - navigationItem.scrollEdgeAppearance = barAppearance - if #available(iOS 15.0, *) { - navigationItem.compactScrollEdgeAppearance = barAppearance - } else { - // Fallback on earlier versions - } - view.backgroundColor = color - collectionView.backgroundColor = color + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate { context in + self.collectionView.collectionViewLayout.invalidateLayout() +// // do nothing + } completion: { [weak self] context in +// guard let self = self else { return } + } + } } -extension SidebarViewController { - @objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) { - os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) - guard let setting = context.settingService.currentSetting.value else { return } - let settingsViewModel = SettingsViewModel(context: context, setting: setting) - coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil)) - } -} - // MARK: - UICollectionViewDelegate extension SidebarViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let diffableDataSource = viewModel.diffableDataSource else { return } - guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return } - switch item { - case .tab(let tab): - delegate?.sidebarViewController(self, didSelectTab: tab) - case .searchHistory(let viewModel): - delegate?.sidebarViewController(self, didSelectSearchHistory: viewModel) - case .header: - break - case .account(let viewModel): - assert(Thread.isMainThread) - let authentication = context.managedObjectContext.object(with: viewModel.authenticationObjectID) as! MastodonAuthentication - context.authenticationService.activeMastodonUser(domain: authentication.domain, userID: authentication.userID) - .receive(on: DispatchQueue.main) - .sink { [weak self] result in - guard let self = self else { return } - self.coordinator.setup() - } - .store(in: &disposeBag) - case .addAccount: - coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil)) + switch collectionView { + case self.collectionView: + guard let diffableDataSource = viewModel.diffableDataSource else { return } + guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return } + switch item { + case .tab(let tab): + delegate?.sidebarViewController(self, didSelectTab: tab) + case .setting: + guard let setting = context.settingService.currentSetting.value else { return } + let settingsViewModel = SettingsViewModel(context: context, setting: setting) + coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil)) + case .compose: + assertionFailure() + } + case secondaryCollectionView: + guard let diffableDataSource = viewModel.secondaryDiffableDataSource else { return } + guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return } + switch item { + case .compose: + let composeViewModel = ComposeViewModel(context: context, composeKind: .post) + coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil)) + default: + assertionFailure() + } + default: + assertionFailure() } +// switch item { +// case .tab(let tab): +// delegate?.sidebarViewController(self, didSelectTab: tab) +// case .searchHistory(let viewModel): +// delegate?.sidebarViewController(self, didSelectSearchHistory: viewModel) +// case .header: +// break +// case .account(let viewModel): +// assert(Thread.isMainThread) +// let authentication = context.managedObjectContext.object(with: viewModel.authenticationObjectID) as! MastodonAuthentication +// context.authenticationService.activeMastodonUser(domain: authentication.domain, userID: authentication.userID) +// .receive(on: DispatchQueue.main) +// .sink { [weak self] result in +// guard let self = self else { return } +// self.coordinator.setup() +// } +// .store(in: &disposeBag) +// case .addAccount: +// coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil)) +// } } } diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift index d7ec5b717..6dd46d08c 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift @@ -22,6 +22,8 @@ final class SidebarViewModel { // output var diffableDataSource: UICollectionViewDiffableDataSource? + var secondaryDiffableDataSource: UICollectionViewDiffableDataSource? + let activeMastodonAuthenticationObjectID = CurrentValueSubject(nil) init(context: AppContext) { @@ -47,38 +49,22 @@ final class SidebarViewModel { extension SidebarViewModel { enum Section: Int, Hashable, CaseIterable { - case tab - case account + case main + case secondary } enum Item: Hashable { case tab(MainTabBarController.Tab) - case searchHistory(SearchHistoryViewModel) - case header(HeaderViewModel) - case account(AccountViewModel) - case addAccount + case setting + case compose } - struct SearchHistoryViewModel: Hashable { - let searchHistoryObjectID: NSManagedObjectID - } - - struct HeaderViewModel: Hashable { - let title: String - } - - struct AccountViewModel: Hashable { - let authenticationObjectID: NSManagedObjectID - } - - struct AddAccountViewModel: Hashable { - let id = UUID() - } } extension SidebarViewModel { func setupDiffableDataSource( - collectionView: UICollectionView + collectionView: UICollectionView, + secondaryCollectionView: UICollectionView ) { let tabCellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, item in guard let self = self else { return } @@ -92,23 +78,10 @@ extension SidebarViewModel { return nil } }() - let headline: MetaContent = { - switch item { - case .me: - return PlaintextMetaContent(string: item.title) - // TODO: - // return PlaintextMetaContent(string: "Myself") - default: - return PlaintextMetaContent(string: item.title) - } - }() - let needsOutlineDisclosure = item == .search cell.item = SidebarListContentView.Item( + title: item.title, image: item.sidebarImage, - imageURL: imageURL, - headline: headline, - subheadline: nil, - needsOutlineDisclosure: needsOutlineDisclosure + imageURL: imageURL ) cell.setNeedsUpdateConfiguration() @@ -135,209 +108,91 @@ extension SidebarViewModel { } } - let searchHistoryCellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, item in + let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, item in guard let self = self else { return } - let managedObjectContext = self.searchHistoryFetchedResultController.fetchedResultsController.managedObjectContext - - guard let searchHistory = try? managedObjectContext.existingObject(with: item.searchHistoryObjectID) as? SearchHistory else { return } - - if let account = searchHistory.account { - let headline: MetaContent = { - do { - let content = MastodonContent(content: account.displayNameWithFallback, emojis: account.emojiMeta) - return try MastodonMetaContent.convert(document: content) - } catch { - return PlaintextMetaContent(string: account.displayNameWithFallback) - } - }() - cell.item = SidebarListContentView.Item( - image: .placeholder(color: .systemFill), - imageURL: account.avatarImageURL(), - headline: headline, - subheadline: PlaintextMetaContent(string: "@" + account.acctWithDomain), - needsOutlineDisclosure: false - ) - } else if let hashtag = searchHistory.hashtag { - let image = UIImage(systemName: "number.square.fill")!.withRenderingMode(.alwaysTemplate) - let headline = PlaintextMetaContent(string: "#" + hashtag.name) - cell.item = SidebarListContentView.Item( - image: image, - imageURL: nil, - headline: headline, - subheadline: nil, - needsOutlineDisclosure: false - ) - } else { - assertionFailure() - } - + cell.item = item cell.setNeedsUpdateConfiguration() } - let headerRegistration = UICollectionView.CellRegistration { (cell, indexPath, item) in - var content = UIListContentConfiguration.sidebarHeader() - content.text = item.title - cell.contentConfiguration = content - cell.accessories = [.outlineDisclosure()] - } - - let accountRegistration = UICollectionView.CellRegistration { [weak self] (cell, indexPath, item) in - guard let self = self else { return } - - // accounts maybe already sign-out - // check isDeleted before using - guard let authentication = try? AppContext.shared.managedObjectContext.existingObject(with: item.authenticationObjectID) as? MastodonAuthentication, - !authentication.isDeleted else { - return - } - let user = authentication.user - let imageURL = user.avatarImageURL() - let headline: MetaContent = { - do { - let content = MastodonContent(content: user.displayNameWithFallback, emojis: user.emojiMeta) - return try MastodonMetaContent.convert(document: content) - } catch { - return PlaintextMetaContent(string: user.displayNameWithFallback) - } - }() - cell.item = SidebarListContentView.Item( - image: .placeholder(color: .systemFill), - imageURL: imageURL, - headline: headline, - subheadline: PlaintextMetaContent(string: "@" + user.acctWithDomain), - needsOutlineDisclosure: false - ) - cell.setNeedsUpdateConfiguration() - - // FIXME: use notification, not timer - let accessToken = authentication.userAccessToken - AppContext.shared.timestampUpdatePublisher - .map { _ in UserDefaults.shared.getNotificationCountWithAccessToken(accessToken: accessToken) } - .removeDuplicates() - .receive(on: DispatchQueue.main) - .sink { [weak cell] count in - guard let cell = cell else { return } - cell._contentView?.badgeButton.setBadge(number: count) - } - .store(in: &cell.disposeBag) - - let authenticationObjectID = item.authenticationObjectID - self.activeMastodonAuthenticationObjectID - .receive(on: DispatchQueue.main) - .sink { [weak cell] objectID in - guard let cell = cell else { return } - cell._contentView?.checkmarkImageView.isHidden = authenticationObjectID != objectID - } - .store(in: &cell.disposeBag) - } - - let addAccountRegistration = UICollectionView.CellRegistration { (cell, indexPath, item) in - var content = UIListContentConfiguration.sidebarCell() - content.text = L10n.Scene.AccountList.addAccount - content.image = UIImage(systemName: "plus.square.fill")! - - cell.contentConfiguration = content - cell.accessories = [] + // header + let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in + // do nothing } let _diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item in switch item { case .tab(let tab): return collectionView.dequeueConfiguredReusableCell(using: tabCellRegistration, for: indexPath, item: tab) - case .searchHistory(let viewModel): - return collectionView.dequeueConfiguredReusableCell(using: searchHistoryCellRegistration, for: indexPath, item: viewModel) - case .header(let viewModel): - return collectionView.dequeueConfiguredReusableCell(using: headerRegistration, for: indexPath, item: viewModel) - case .account(let viewModel): - return collectionView.dequeueConfiguredReusableCell(using: accountRegistration, for: indexPath, item: viewModel) - case .addAccount: - return collectionView.dequeueConfiguredReusableCell(using: addAccountRegistration, for: indexPath, item: AddAccountViewModel()) + case .setting: + let item = SidebarListContentView.Item( + title: L10n.Common.Controls.Actions.settings, + image: UIImage(systemName: "gear")!, + imageURL: nil + ) + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item) + case .compose: + let item = SidebarListContentView.Item( + title: "Compose", // FIXME: + image: UIImage(systemName: "square.and.pencil")!, + imageURL: nil + ) + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item) + } + } + _diffableDataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in + switch elementKind { + case UICollectionView.elementKindSectionHeader: + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + default: + assertionFailure() + return UICollectionReusableView() } } diffableDataSource = _diffableDataSource var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections(Section.allCases) - _diffableDataSource.apply(snapshot) + snapshot.appendSections([.main]) - for section in Section.allCases { - switch section { - case .tab: - var sectionSnapshot = NSDiffableDataSourceSectionSnapshot() - let items: [Item] = [ - .tab(.home), - .tab(.search), - .tab(.notification), - .tab(.me), - ] - sectionSnapshot.append(items, to: nil) - _diffableDataSource.apply(sectionSnapshot, to: section) - case .account: - var sectionSnapshot = NSDiffableDataSourceSectionSnapshot() - let headerItem = Item.header(HeaderViewModel(title: "Accounts")) - sectionSnapshot.append([headerItem], to: nil) - sectionSnapshot.append([], to: headerItem) - sectionSnapshot.append([.addAccount], to: headerItem) - sectionSnapshot.expand([headerItem]) - _diffableDataSource.apply(sectionSnapshot, to: section) + var sectionSnapshot = NSDiffableDataSourceSectionSnapshot() + let items: [Item] = [ + .tab(.home), + .tab(.search), + .tab(.notification), + .tab(.me), + .setting, + ] + sectionSnapshot.append(items, to: nil) + _diffableDataSource.apply(sectionSnapshot, to: .main) + + + // secondary + let _secondaryDiffableDataSource = UICollectionViewDiffableDataSource(collectionView: secondaryCollectionView) { collectionView, indexPath, item in + guard case .compose = item else { + assertionFailure() + return UICollectionViewCell() } + + let item = SidebarListContentView.Item( + title: "Compose", // FIXME: + image: UIImage(systemName: "square.and.pencil")!, + imageURL: nil + ) + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item) } +// _secondaryDiffableDataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in +// return nil +// } + secondaryDiffableDataSource = _secondaryDiffableDataSource - // update .search tab - searchHistoryFetchedResultController.objectIDs - .removeDuplicates() - .receive(on: DispatchQueue.main) - .sink { [weak self] objectIDs in - guard let self = self else { return } - guard let diffableDataSource = self.diffableDataSource else { return } + var secondarySnapshot = NSDiffableDataSourceSnapshot() + secondarySnapshot.appendSections([.secondary]) - // update .search tab - var sectionSnapshot = diffableDataSource.snapshot(for: .tab) - - // remove children - let searchHistorySnapshot = sectionSnapshot.snapshot(of: .tab(.search)) - sectionSnapshot.delete(searchHistorySnapshot.items) - - // append children - let managedObjectContext = self.searchHistoryFetchedResultController.fetchedResultsController.managedObjectContext - let items: [Item] = objectIDs.compactMap { objectID -> Item? in - guard let searchHistory = try? managedObjectContext.existingObject(with: objectID) as? SearchHistory else { return nil } - guard searchHistory.account != nil || searchHistory.hashtag != nil else { return nil } - let viewModel = SearchHistoryViewModel(searchHistoryObjectID: objectID) - return Item.searchHistory(viewModel) - } - sectionSnapshot.append(Array(items.prefix(5)), to: .tab(.search)) - sectionSnapshot.expand([.tab(.search)]) - - // apply snapshot - diffableDataSource.apply(sectionSnapshot, to: .tab, animatingDifferences: false) - } - .store(in: &disposeBag) - - // update .me tab and .account section - context.authenticationService.mastodonAuthentications - .receive(on: DispatchQueue.main) - .sink { [weak self] authentications in - guard let self = self else { return } - guard let diffableDataSource = self.diffableDataSource else { return } - // tab - var snapshot = diffableDataSource.snapshot() - snapshot.reloadItems([.tab(.me)]) - diffableDataSource.apply(snapshot) - - // account - var accountSectionSnapshot = NSDiffableDataSourceSectionSnapshot() - let headerItem = Item.header(HeaderViewModel(title: "Accounts")) - accountSectionSnapshot.append([headerItem], to: nil) - let accountItems = authentications.map { authentication in - Item.account(AccountViewModel(authenticationObjectID: authentication.objectID)) - } - accountSectionSnapshot.append(accountItems, to: headerItem) - accountSectionSnapshot.append([.addAccount], to: headerItem) - accountSectionSnapshot.expand([headerItem]) - diffableDataSource.apply(accountSectionSnapshot, to: .account) - } - .store(in: &disposeBag) + var secondarySectionSnapshot = NSDiffableDataSourceSectionSnapshot() + let secondarySectionItems: [Item] = [ + .compose, + ] + secondarySectionSnapshot.append(secondarySectionItems, to: nil) + _secondaryDiffableDataSource.apply(secondarySectionSnapshot, to: .secondary) } } diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift index 1bb76f59e..dc71eadfb 100644 --- a/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift @@ -60,21 +60,6 @@ extension SidebarListCollectionViewCell { newBackgroundConfiguration.backgroundColorTransformer = .init { $0.withAlphaComponent(0.8) } } - backgroundConfiguration = newBackgroundConfiguration - - let needsOutlineDisclosure = item?.needsOutlineDisclosure ?? false - if !needsOutlineDisclosure { - accessories = [] - } else { - let tintColor: UIColor = state.isHighlighted || state.isSelected ? .white : Asset.Colors.brandBlue.color - accessories = [ - UICellAccessory.outlineDisclosure( - displayed: .always, - options: UICellAccessory.OutlineDisclosureOptions(tintColor: tintColor), - actionHandler: nil - ) - ] - } } } diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift index 62b188325..dedd47b88 100644 --- a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift @@ -15,15 +15,10 @@ final class SidebarListContentView: UIView, UIContentView { let logger = Logger(subsystem: "SidebarListContentView", category: "UI") let imageView = UIImageView() - let animationImageView = FLAnimatedImageView() // for animation image - let headlineLabel = MetaLabel(style: .sidebarHeadline(isSelected: false)) - let subheadlineLabel = MetaLabel(style: .sidebarSubheadline(isSelected: false)) - let badgeButton = BadgeButton() - let checkmarkImageView: UIImageView = { - let image = UIImage(systemName: "checkmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .semibold)) - let imageView = UIImageView(image: image) - imageView.tintColor = .label - return imageView + let avatarButton: CircleAvatarButton = { + let button = CircleAvatarButton() + button.borderWidth = 2 + return button }() private var currentConfiguration: ContentConfiguration! @@ -53,93 +48,31 @@ final class SidebarListContentView: UIView, UIContentView { extension SidebarListContentView { private func _init() { - let imageViewContainer = UIView() - imageViewContainer.translatesAutoresizingMaskIntoConstraints = false - addSubview(imageViewContainer) - NSLayoutConstraint.activate([ - imageViewContainer.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor), - imageViewContainer.centerYAnchor.constraint(equalTo: centerYAnchor), - ]) - imageViewContainer.setContentHuggingPriority(.defaultLow, for: .horizontal) - imageViewContainer.setContentHuggingPriority(.defaultLow, for: .vertical) - - animationImageView.translatesAutoresizingMaskIntoConstraints = false - imageViewContainer.addSubview(animationImageView) - NSLayoutConstraint.activate([ - animationImageView.centerXAnchor.constraint(equalTo: imageViewContainer.centerXAnchor), - animationImageView.centerYAnchor.constraint(equalTo: imageViewContainer.centerYAnchor), - animationImageView.widthAnchor.constraint(equalTo: imageViewContainer.widthAnchor, multiplier: 1.0).priority(.required - 1), - animationImageView.heightAnchor.constraint(equalTo: imageViewContainer.heightAnchor, multiplier: 1.0).priority(.required - 1), - ]) - animationImageView.setContentHuggingPriority(.defaultLow - 10, for: .vertical) - animationImageView.setContentHuggingPriority(.defaultLow - 10, for: .horizontal) - imageView.translatesAutoresizingMaskIntoConstraints = false - imageViewContainer.addSubview(imageView) + addSubview(imageView) NSLayoutConstraint.activate([ - imageView.centerXAnchor.constraint(equalTo: imageViewContainer.centerXAnchor), - imageView.centerYAnchor.constraint(equalTo: imageViewContainer.centerYAnchor), - imageView.widthAnchor.constraint(equalTo: imageViewContainer.widthAnchor, multiplier: 1.0).priority(.required - 1), - imageView.heightAnchor.constraint(equalTo: imageViewContainer.heightAnchor, multiplier: 1.0).priority(.required - 1), + imageView.topAnchor.constraint(equalTo: topAnchor, constant: 16), + imageView.centerXAnchor.constraint(equalTo: centerXAnchor), + bottomAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 16), + imageView.widthAnchor.constraint(equalToConstant: 40).priority(.required - 1), + imageView.heightAnchor.constraint(equalToConstant: 40).priority(.required - 1), ]) - imageView.setContentHuggingPriority(.defaultLow - 10, for: .vertical) - imageView.setContentHuggingPriority(.defaultLow - 10, for: .horizontal) - - let textContainer = UIStackView() - textContainer.axis = .vertical - textContainer.translatesAutoresizingMaskIntoConstraints = false - addSubview(textContainer) + + avatarButton.translatesAutoresizingMaskIntoConstraints = false + addSubview(avatarButton) NSLayoutConstraint.activate([ - textContainer.topAnchor.constraint(equalTo: topAnchor, constant: 10), - textContainer.leadingAnchor.constraint(equalTo: imageViewContainer.trailingAnchor, constant: 10), - // textContainer.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor), - bottomAnchor.constraint(equalTo: textContainer.bottomAnchor, constant: 12), + avatarButton.centerXAnchor.constraint(equalTo: imageView.centerXAnchor), + avatarButton.centerYAnchor.constraint(equalTo: imageView.centerYAnchor), + avatarButton.widthAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 1.0).priority(.required - 2), + avatarButton.heightAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 1.0).priority(.required - 2), ]) - - textContainer.addArrangedSubview(headlineLabel) - textContainer.addArrangedSubview(subheadlineLabel) - headlineLabel.setContentHuggingPriority(.required - 9, for: .vertical) - headlineLabel.setContentCompressionResistancePriority(.required - 9, for: .vertical) - subheadlineLabel.setContentHuggingPriority(.required - 10, for: .vertical) - subheadlineLabel.setContentCompressionResistancePriority(.required - 10, for: .vertical) - - badgeButton.translatesAutoresizingMaskIntoConstraints = false - addSubview(badgeButton) - NSLayoutConstraint.activate([ - badgeButton.leadingAnchor.constraint(equalTo: textContainer.trailingAnchor, constant: 4), - badgeButton.centerYAnchor.constraint(equalTo: centerYAnchor), - badgeButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 16).priority(.required - 1), - badgeButton.widthAnchor.constraint(equalTo: badgeButton.heightAnchor, multiplier: 1.0).priority(.required - 1), - ]) - badgeButton.setContentHuggingPriority(.required - 10, for: .horizontal) - badgeButton.setContentCompressionResistancePriority(.required - 10, for: .horizontal) - - NSLayoutConstraint.activate([ - imageViewContainer.heightAnchor.constraint(equalTo: headlineLabel.heightAnchor, multiplier: 1.0).priority(.required - 1), - imageViewContainer.widthAnchor.constraint(equalTo: imageViewContainer.heightAnchor, multiplier: 1.0).priority(.required - 1), - ]) - - checkmarkImageView.translatesAutoresizingMaskIntoConstraints = false - addSubview(checkmarkImageView) - NSLayoutConstraint.activate([ - checkmarkImageView.centerYAnchor.constraint(equalTo: centerYAnchor), - checkmarkImageView.leadingAnchor.constraint(equalTo: badgeButton.trailingAnchor, constant: 16), - checkmarkImageView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor), - ]) - checkmarkImageView.setContentHuggingPriority(.required - 9, for: .horizontal) - checkmarkImageView.setContentCompressionResistancePriority(.required - 9, for: .horizontal) - - animationImageView.isUserInteractionEnabled = false - headlineLabel.isUserInteractionEnabled = false - subheadlineLabel.isUserInteractionEnabled = false - + avatarButton.setContentHuggingPriority(.defaultLow - 10, for: .vertical) + avatarButton.setContentHuggingPriority(.defaultLow - 10, for: .horizontal) + imageView.contentMode = .scaleAspectFit - animationImageView.contentMode = .scaleAspectFit + avatarButton.contentMode = .scaleAspectFit imageView.tintColor = Asset.Colors.brandBlue.color - animationImageView.tintColor = Asset.Colors.brandBlue.color - - badgeButton.setBadge(number: 0) - checkmarkImageView.isHidden = true + avatarButton.tintColor = Asset.Colors.brandBlue.color } private func apply(configuration: ContentConfiguration) { @@ -152,31 +85,20 @@ extension SidebarListContentView { // configure state imageView.tintColor = item.isSelected ? .white : Asset.Colors.brandBlue.color - animationImageView.tintColor = item.isSelected ? .white : Asset.Colors.brandBlue.color - headlineLabel.setup(style: .sidebarHeadline(isSelected: item.isSelected)) - subheadlineLabel.setup(style: .sidebarSubheadline(isSelected: item.isSelected)) + avatarButton.tintColor = item.isSelected ? .white : Asset.Colors.brandBlue.color // configure model imageView.isHidden = item.imageURL != nil - animationImageView.isHidden = item.imageURL == nil + avatarButton.isHidden = item.imageURL == nil imageView.image = item.image.withRenderingMode(.alwaysTemplate) - animationImageView.setImage( + avatarButton.avatarImageView.setImage( url: item.imageURL, - placeholder: animationImageView.image ?? .placeholder(color: .systemFill), // reuse to avoid blink + placeholder: avatarButton.avatarImageView.image ?? .placeholder(color: .systemFill), // reuse to avoid blink scaleToSize: nil ) - animationImageView.layer.masksToBounds = true - animationImageView.layer.cornerCurve = .continuous - animationImageView.layer.cornerRadius = 4 - - headlineLabel.configure(content: item.headline) - - if let subheadline = item.subheadline { - subheadlineLabel.configure(content: subheadline) - subheadlineLabel.isHidden = false - } else { - subheadlineLabel.isHidden = true - } + avatarButton.layer.masksToBounds = true + avatarButton.layer.cornerCurve = .continuous + avatarButton.layer.cornerRadius = 4 } } @@ -186,27 +108,22 @@ extension SidebarListContentView { var isSelected: Bool = false // model + let title: String let image: UIImage let imageURL: URL? - let headline: MetaContent - let subheadline: MetaContent? - - let needsOutlineDisclosure: Bool - + static func == (lhs: SidebarListContentView.Item, rhs: SidebarListContentView.Item) -> Bool { return lhs.isSelected == rhs.isSelected + && lhs.title == rhs.title && lhs.image == rhs.image && lhs.imageURL == rhs.imageURL - && lhs.headline.string == rhs.headline.string - && lhs.subheadline?.string == rhs.subheadline?.string } func hash(into hasher: inout Hasher) { hasher.combine(isSelected) + hasher.combine(title) hasher.combine(image) imageURL.flatMap { hasher.combine($0) } - hasher.combine(headline.string) - subheadline.flatMap { hasher.combine($0.string) } } } diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListHeaderView.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListHeaderView.swift new file mode 100644 index 000000000..2056c5dcd --- /dev/null +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListHeaderView.swift @@ -0,0 +1,42 @@ +// +// SidebarListHeaderView.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-10-28. +// + +import UIKit + +final class SidebarListHeaderView: UICollectionReusableView { + + let imageView: UIImageView = { + let imageView = UIImageView() + imageView.image = Asset.Scene.Sidebar.logo.image + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension SidebarListHeaderView { + private func _init() { + imageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(imageView) + NSLayoutConstraint.activate([ + imageView.topAnchor.constraint(equalTo: topAnchor, constant: 8), + imageView.centerXAnchor.constraint(equalTo: centerXAnchor), + bottomAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 16), + imageView.widthAnchor.constraint(equalToConstant: 44).priority(.required - 1), + imageView.heightAnchor.constraint(equalToConstant: 44).priority(.required - 1), + ]) + } +} diff --git a/Mastodon/Scene/Share/View/Button/CircleAvatarButton.swift b/Mastodon/Scene/Share/View/Button/CircleAvatarButton.swift index 40272d290..0bc2aeefd 100644 --- a/Mastodon/Scene/Share/View/Button/CircleAvatarButton.swift +++ b/Mastodon/Scene/Share/View/Button/CircleAvatarButton.swift @@ -9,12 +9,15 @@ import UIKit final class CircleAvatarButton: AvatarButton { + var borderColor: CGColor = UIColor.systemFill.cgColor + var borderWidth: CGFloat = 1.0 + override func layoutSubviews() { super.layoutSubviews() layer.masksToBounds = true layer.cornerRadius = frame.width * 0.5 - layer.borderColor = UIColor.systemFill.cgColor - layer.borderWidth = 1 + layer.borderColor = borderColor + layer.borderWidth = borderWidth } } diff --git a/Mastodon/Service/ThemeService/SystemTheme.swift b/Mastodon/Service/ThemeService/SystemTheme.swift index 2e3b290db..1ea923979 100644 --- a/Mastodon/Service/ThemeService/SystemTheme.swift +++ b/Mastodon/Service/ThemeService/SystemTheme.swift @@ -23,7 +23,7 @@ struct SystemTheme: Theme { let navigationBarBackgroundColor = Asset.Theme.System.navigationBarBackground.color - let sidebarBackgroundColor = Asset.Theme.Mastodon.sidebarBackground.color + let sidebarBackgroundColor = Asset.Theme.System.sidebarBackground.color let tabBarBackgroundColor = Asset.Theme.System.tabBarBackground.color let tabBarItemSelectedIconColor = Asset.Colors.brandBlue.color From b1f3729f56450fde3a56b99d7341c488b63f63d9 Mon Sep 17 00:00:00 2001 From: CMK Date: Thu, 28 Oct 2021 19:33:29 +0800 Subject: [PATCH 81/95] chore: update version to 1.2.0 (80) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 9 --- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 12 files changed, 45 insertions(+), 54 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 7f509a3fe..cf50d3ac4 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 7f509a3fe..cf50d3ac4 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 7f509a3fe..cf50d3ac4 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 1ab4b2559..f7b373f59 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4800,7 +4800,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4829,7 +4829,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4937,11 +4937,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4968,11 +4968,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4997,11 +4997,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5027,11 +5027,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5094,7 +5094,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5119,7 +5119,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5144,7 +5144,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5169,7 +5169,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5194,7 +5194,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5219,7 +5219,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5244,7 +5244,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5269,7 +5269,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5360,7 +5360,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5427,11 +5427,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5476,7 +5476,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5501,11 +5501,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5597,7 +5597,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5664,11 +5664,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5713,7 +5713,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5738,11 +5738,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 79; + DYLIB_CURRENT_VERSION = 80; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5768,7 +5768,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5792,7 +5792,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 79; + CURRENT_PROJECT_VERSION = 80; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index e6093a218..cb88c3960 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 35 + 42 CoreDataStack.xcscheme_^#shared#^_ orderHint - 38 + 43 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 36 + 44 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 37 + 41 SuppressBuildableAutocreation diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index 26ba58d55..b305c8156 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -216,15 +216,6 @@ "revision": "dad97167bf1be16aeecd109130900995dd01c515", "version": "2.6.0" } - }, - { - "package": "UITextView+Placeholder", - "repositoryURL": "https://github.com/MainasuK/UITextView-Placeholder", - "state": { - "branch": null, - "revision": "20f513ded04a040cdf5467f0891849b1763ede3b", - "version": "1.4.1" - } } ] }, diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 43ae9e6d0..1c89f12a2 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 79 + 80 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 714ba3106..062a4bf1a 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 7f509a3fe..cf50d3ac4 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 7f509a3fe..cf50d3ac4 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index b5ee7a305..fb52c9dce 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 5643d878f..8870b320d 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 79 + 80 NSExtension NSExtensionAttributes From c6fc5cc09dd48f43017cad33f00c1dee87af51df Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 14:58:09 +0800 Subject: [PATCH 82/95] feat: update to new UI tint style --- .../Scene/Compose/View/ComposeToolbarView.swift | 2 +- .../HashtagTimelineViewController.swift | 2 +- .../HomeTimeline/HomeTimelineViewController.swift | 4 ++-- Mastodon/Scene/Report/ReportViewController.swift | 2 +- .../View/SidebarListCollectionViewCell.swift | 10 ++-------- .../Sidebar/View/SidebarListContentView.swift | 15 ++++++++------- .../Scene/Share/View/Content/StatusView.swift | 2 +- .../Scene/Share/View/Content/ThreadMetaView.swift | 8 ++++---- .../ThreadReplyLoaderTableViewCell.swift | 2 +- .../TimelineLoaderTableViewCell.swift | 4 ++-- Mastodon/Service/ThemeService/MastodonTheme.swift | 2 +- Mastodon/Service/ThemeService/SystemTheme.swift | 2 +- .../ThemeService/ThemeService+Appearance.swift | 6 +++--- Mastodon/Service/ThemeService/ThemeService.swift | 2 ++ Mastodon/Supporting Files/SceneDelegate.swift | 2 +- .../Scene/View/ComposeToolbarView.swift | 2 +- 16 files changed, 32 insertions(+), 35 deletions(-) diff --git a/Mastodon/Scene/Compose/View/ComposeToolbarView.swift b/Mastodon/Scene/Compose/View/ComposeToolbarView.swift index 373ce1a15..6b06973a2 100644 --- a/Mastodon/Scene/Compose/View/ComposeToolbarView.swift +++ b/Mastodon/Scene/Compose/View/ComposeToolbarView.swift @@ -262,7 +262,7 @@ extension ComposeToolbarView { } private static func configureToolbarButtonAppearance(button: UIButton) { - button.tintColor = Asset.Colors.brandBlue.color + button.tintColor = ThemeService.tintColor button.setBackgroundImage(.placeholder(size: ComposeToolbarView.toolbarButtonSize, color: .systemFill), for: .highlighted) button.layer.masksToBounds = true button.layer.cornerRadius = 5 diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift index 9801d701a..72f084fad 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift @@ -24,7 +24,7 @@ class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPre let composeBarButtonItem: UIBarButtonItem = { let barButtonItem = UIBarButtonItem() - barButtonItem.tintColor = Asset.Colors.brandBlue.color + // barButtonItem.tintColor = Asset.Colors.brandBlue.color barButtonItem.image = UIImage(systemName: "square.and.pencil")?.withRenderingMode(.alwaysTemplate) return barButtonItem }() diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index 55bf544ca..6b5476885 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -46,14 +46,14 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media let settingBarButtonItem: UIBarButtonItem = { let barButtonItem = UIBarButtonItem() - barButtonItem.tintColor = Asset.Colors.brandBlue.color + barButtonItem.tintColor = ThemeService.tintColor barButtonItem.image = UIImage(systemName: "gear")?.withRenderingMode(.alwaysTemplate) return barButtonItem }() let composeBarButtonItem: UIBarButtonItem = { let barButtonItem = UIBarButtonItem() - barButtonItem.tintColor = Asset.Colors.brandBlue.color + barButtonItem.tintColor = ThemeService.tintColor barButtonItem.image = UIImage(systemName: "square.and.pencil")?.withRenderingMode(.alwaysTemplate) return barButtonItem }() diff --git a/Mastodon/Scene/Report/ReportViewController.swift b/Mastodon/Scene/Report/ReportViewController.swift index efaa533e1..6a7161c91 100644 --- a/Mastodon/Scene/Report/ReportViewController.swift +++ b/Mastodon/Scene/Report/ReportViewController.swift @@ -251,7 +251,7 @@ class ReportViewController: UIViewController, NeedsDependency { = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.cancel, target: self, action: #selector(doneButtonDidClick)) - navigationItem.rightBarButtonItem?.tintColor = Asset.Colors.brandBlue.color + navigationItem.rightBarButtonItem?.tintColor = ThemeService.tintColor // fetch old mastodon user let beReportedUser: MastodonUser? = { diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift index dc71eadfb..998d3f9e2 100644 --- a/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListCollectionViewCell.swift @@ -51,15 +51,9 @@ extension SidebarListCollectionViewCell { newConfiguration.item = item contentConfiguration = newConfiguration + // remove background var newBackgroundConfiguration = UIBackgroundConfiguration.listSidebarCell().updated(for: state) - // Customize the background color to use the tint color when the cell is highlighted or selected. - if state.isSelected || state.isHighlighted { - newBackgroundConfiguration.backgroundColor = Asset.Colors.brandBlue.color - } - if state.isHighlighted { - newBackgroundConfiguration.backgroundColorTransformer = .init { $0.withAlphaComponent(0.8) } - } - + newBackgroundConfiguration.backgroundColor = .clear backgroundConfiguration = newBackgroundConfiguration } } diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift index dedd47b88..8c013efda 100644 --- a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift @@ -71,8 +71,6 @@ extension SidebarListContentView { imageView.contentMode = .scaleAspectFit avatarButton.contentMode = .scaleAspectFit - imageView.tintColor = Asset.Colors.brandBlue.color - avatarButton.tintColor = Asset.Colors.brandBlue.color } private func apply(configuration: ContentConfiguration) { @@ -84,8 +82,9 @@ extension SidebarListContentView { guard let item = configuration.item else { return } // configure state - imageView.tintColor = item.isSelected ? .white : Asset.Colors.brandBlue.color - avatarButton.tintColor = item.isSelected ? .white : Asset.Colors.brandBlue.color + let tintColor = item.isHighlighted ? ThemeService.tintColor.withAlphaComponent(0.5) : ThemeService.tintColor + imageView.tintColor = tintColor + avatarButton.tintColor = tintColor // configure model imageView.isHidden = item.imageURL != nil @@ -96,9 +95,6 @@ extension SidebarListContentView { placeholder: avatarButton.avatarImageView.image ?? .placeholder(color: .systemFill), // reuse to avoid blink scaleToSize: nil ) - avatarButton.layer.masksToBounds = true - avatarButton.layer.cornerCurve = .continuous - avatarButton.layer.cornerRadius = 4 } } @@ -106,6 +102,7 @@ extension SidebarListContentView { struct Item: Hashable { // state var isSelected: Bool = false + var isHighlighted: Bool = false // model let title: String @@ -114,6 +111,7 @@ extension SidebarListContentView { static func == (lhs: SidebarListContentView.Item, rhs: SidebarListContentView.Item) -> Bool { return lhs.isSelected == rhs.isSelected + && lhs.isHighlighted == rhs.isHighlighted && lhs.title == rhs.title && lhs.image == rhs.image && lhs.imageURL == rhs.imageURL @@ -121,6 +119,7 @@ extension SidebarListContentView { func hash(into hasher: inout Hasher) { hasher.combine(isSelected) + hasher.combine(isHighlighted) hasher.combine(title) hasher.combine(image) imageURL.flatMap { hasher.combine($0) } @@ -143,9 +142,11 @@ extension SidebarListContentView { if let state = state as? UICellConfigurationState { updatedConfiguration.item?.isSelected = state.isHighlighted || state.isSelected + updatedConfiguration.item?.isHighlighted = state.isHighlighted } else { assertionFailure() updatedConfiguration.item?.isSelected = false + updatedConfiguration.item?.isHighlighted = false } return updatedConfiguration diff --git a/Mastodon/Scene/Share/View/Content/StatusView.swift b/Mastodon/Scene/Share/View/Content/StatusView.swift index a2788f524..957764fa7 100644 --- a/Mastodon/Scene/Share/View/Content/StatusView.swift +++ b/Mastodon/Scene/Share/View/Content/StatusView.swift @@ -126,7 +126,7 @@ final class StatusView: UIView { let revealContentWarningButton: UIButton = { let button = HighlightDimmableButton() button.setImage(UIImage(systemName: "eye", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .medium)), for: .normal) - button.tintColor = Asset.Colors.brandBlue.color + // button.tintColor = Asset.Colors.brandBlue.color return button }() diff --git a/Mastodon/Scene/Share/View/Content/ThreadMetaView.swift b/Mastodon/Scene/Share/View/Content/ThreadMetaView.swift index 4bda525ae..c339654f5 100644 --- a/Mastodon/Scene/Share/View/Content/ThreadMetaView.swift +++ b/Mastodon/Scene/Share/View/Content/ThreadMetaView.swift @@ -23,8 +23,8 @@ final class ThreadMetaView: UIView { let button = UIButton() button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold)) button.setTitle("0 reblog", for: .normal) - button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal) - button.setTitleColor(Asset.Colors.brandBlue.color.withAlphaComponent(0.5), for: .highlighted) + button.setTitleColor(ThemeService.tintColor, for: .normal) + button.setTitleColor(ThemeService.tintColor.withAlphaComponent(0.5), for: .highlighted) return button }() @@ -32,8 +32,8 @@ final class ThreadMetaView: UIView { let button = UIButton() button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold)) button.setTitle("0 favorite", for: .normal) - button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal) - button.setTitleColor(Asset.Colors.brandBlue.color.withAlphaComponent(0.5), for: .highlighted) + button.setTitleColor(ThemeService.tintColor, for: .normal) + button.setTitleColor(ThemeService.tintColor.withAlphaComponent(0.5), for: .highlighted) return button }() diff --git a/Mastodon/Scene/Share/View/TableviewCell/ThreadReplyLoaderTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/ThreadReplyLoaderTableViewCell.swift index 5e5ac88d7..a819f301c 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/ThreadReplyLoaderTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/ThreadReplyLoaderTableViewCell.swift @@ -23,7 +23,7 @@ final class ThreadReplyLoaderTableViewCell: UITableViewCell { let loadMoreButton: UIButton = { let button = HighlightDimmableButton() button.titleLabel?.font = TimelineLoaderTableViewCell.labelFont - button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal) + button.setTitleColor(ThemeService.tintColor, for: .normal) button.setTitle(L10n.Common.Controls.Timeline.Loader.showMoreReplies, for: .normal) return button }() diff --git a/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift index 8c329d31e..da0b80fb4 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/TimelineLoaderTableViewCell.swift @@ -24,7 +24,7 @@ class TimelineLoaderTableViewCell: UITableViewCell { let loadMoreButton: UIButton = { let button = HighlightDimmableButton() button.titleLabel?.font = TimelineLoaderTableViewCell.labelFont - button.setTitleColor(Asset.Colors.brandBlue.color, for: .normal) + button.setTitleColor(ThemeService.tintColor, for: .normal) button.setTitle(L10n.Common.Controls.Timeline.Loader.loadMissingPosts, for: .normal) button.setTitle("", for: .disabled) return button @@ -68,7 +68,7 @@ class TimelineLoaderTableViewCell: UITableViewCell { func stopAnimating() { activityIndicatorView.stopAnimating() self.loadMoreButton.isEnabled = true - self.loadMoreLabel.textColor = Asset.Colors.brandBlue.color + self.loadMoreLabel.textColor = ThemeService.tintColor self.loadMoreLabel.text = "" } diff --git a/Mastodon/Service/ThemeService/MastodonTheme.swift b/Mastodon/Service/ThemeService/MastodonTheme.swift index 85b0d42db..1f0fd4e38 100644 --- a/Mastodon/Service/ThemeService/MastodonTheme.swift +++ b/Mastodon/Service/ThemeService/MastodonTheme.swift @@ -26,7 +26,7 @@ struct MastodonTheme: Theme { let sidebarBackgroundColor = Asset.Theme.Mastodon.sidebarBackground.color let tabBarBackgroundColor = Asset.Theme.Mastodon.tabBarBackground.color - let tabBarItemSelectedIconColor = Asset.Colors.brandBlue.color + let tabBarItemSelectedIconColor = ThemeService.tintColor let tabBarItemFocusedIconColor = Asset.Theme.Mastodon.tabBarItemInactiveIconColor.color let tabBarItemNormalIconColor = Asset.Theme.Mastodon.tabBarItemInactiveIconColor.color let tabBarItemDisabledIconColor = Asset.Theme.Mastodon.tabBarItemInactiveIconColor.color diff --git a/Mastodon/Service/ThemeService/SystemTheme.swift b/Mastodon/Service/ThemeService/SystemTheme.swift index 1ea923979..26673d57d 100644 --- a/Mastodon/Service/ThemeService/SystemTheme.swift +++ b/Mastodon/Service/ThemeService/SystemTheme.swift @@ -26,7 +26,7 @@ struct SystemTheme: Theme { let sidebarBackgroundColor = Asset.Theme.System.sidebarBackground.color let tabBarBackgroundColor = Asset.Theme.System.tabBarBackground.color - let tabBarItemSelectedIconColor = Asset.Colors.brandBlue.color + let tabBarItemSelectedIconColor = ThemeService.tintColor let tabBarItemFocusedIconColor = Asset.Theme.System.tabBarItemInactiveIconColor.color let tabBarItemNormalIconColor = Asset.Theme.System.tabBarItemInactiveIconColor.color let tabBarItemDisabledIconColor = Asset.Theme.System.tabBarItemInactiveIconColor.color diff --git a/Mastodon/Service/ThemeService/ThemeService+Appearance.swift b/Mastodon/Service/ThemeService/ThemeService+Appearance.swift index cffa45c7a..896ed888e 100644 --- a/Mastodon/Service/ThemeService/ThemeService+Appearance.swift +++ b/Mastodon/Service/ThemeService/ThemeService+Appearance.swift @@ -46,7 +46,7 @@ extension ThemeService { tabBarAppearance.compactInlineLayoutAppearance = tabBarItemAppearance tabBarAppearance.backgroundColor = theme.tabBarBackgroundColor - tabBarAppearance.selectionIndicatorTintColor = Asset.Colors.brandBlue.color + tabBarAppearance.selectionIndicatorTintColor = ThemeService.tintColor UITabBar.appearance().standardAppearance = tabBarAppearance if #available(iOS 15.0, *) { UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance @@ -61,9 +61,9 @@ extension ThemeService { UITableViewCell.appearance().selectionColor = theme.tableViewCellSelectionBackgroundColor // set search bar appearance - UISearchBar.appearance().tintColor = Asset.Colors.brandBlue.color + UISearchBar.appearance().tintColor = ThemeService.tintColor UISearchBar.appearance().barTintColor = theme.navigationBarBackgroundColor - let cancelButtonAttributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.foregroundColor: Asset.Colors.brandBlue.color] + let cancelButtonAttributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.foregroundColor: ThemeService.tintColor] UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(cancelButtonAttributes, for: .normal) } } diff --git a/Mastodon/Service/ThemeService/ThemeService.swift b/Mastodon/Service/ThemeService/ThemeService.swift index 35d5b3491..e3bd7c4ab 100644 --- a/Mastodon/Service/ThemeService/ThemeService.swift +++ b/Mastodon/Service/ThemeService/ThemeService.swift @@ -10,6 +10,8 @@ import Combine // ref: https://zamzam.io/protocol-oriented-themes-for-ios-apps/ final class ThemeService { + + static let tintColor: UIColor = .label // MARK: - Singleton public static let shared = ThemeService() diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 6c5752c4c..4809fe5f9 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -37,7 +37,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.window = window // set tint color - window.tintColor = Asset.Colors.brandBlue.color + window.tintColor = UIColor.label ThemeService.shared.currentTheme .receive(on: RunLoop.main) diff --git a/ShareActionExtension/Scene/View/ComposeToolbarView.swift b/ShareActionExtension/Scene/View/ComposeToolbarView.swift index e6842c744..d88bb018c 100644 --- a/ShareActionExtension/Scene/View/ComposeToolbarView.swift +++ b/ShareActionExtension/Scene/View/ComposeToolbarView.swift @@ -190,7 +190,7 @@ extension ComposeToolbarView { extension ComposeToolbarView { private static func configureToolbarButtonAppearance(button: UIButton) { - button.tintColor = Asset.Colors.brandBlue.color + button.tintColor = ThemeService.tintColor button.setBackgroundImage(.placeholder(size: ComposeToolbarView.toolbarButtonSize, color: .systemFill), for: .highlighted) button.layer.masksToBounds = true button.layer.cornerRadius = 5 From 0ec20c6c88784028549829171dec410823f693ad Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 14:58:59 +0800 Subject: [PATCH 83/95] feat: add split view layout update handler --- .../Root/ContentSplitViewController.swift | 2 +- .../Scene/Root/RootSplitViewController.swift | 133 +++++++----------- 2 files changed, 54 insertions(+), 81 deletions(-) diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift index 42ce3afc3..6f5e294cd 100644 --- a/Mastodon/Scene/Root/ContentSplitViewController.swift +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -47,7 +47,7 @@ extension ContentSplitViewController { navigationController?.setNavigationBarHidden(true, animated: false) addChild(sidebarViewController) - sidebarViewController.view.translatesAutoresizingMaskIntoConstraints = false + sidebarViewController.view.translatesAutoresizingMaskIntoConstraints = false view.addSubview(sidebarViewController.view) sidebarViewController.didMove(toParent: self) NSLayoutConstraint.activate([ diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 4f818ea9d..fc1e7a4f7 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -26,6 +26,8 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { return contentSplitViewController }() + lazy var compactMainTabBarViewController = MainTabBarController(context: context, coordinator: coordinator) + init(context: AppContext, coordinator: SceneCoordinator) { self.context = context self.coordinator = coordinator @@ -48,6 +50,7 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { setViewController(UIViewController(), for: .primary) setViewController(contentSplitViewController, for: .secondary) + setViewController(compactMainTabBarViewController, for: .compact) contentSplitViewController.sidebarViewController.view.layer.zPosition = 100 contentSplitViewController.mainTabBarController.view.layer.zPosition = 90 @@ -108,85 +111,55 @@ extension RootSplitViewController { // MARK: - UISplitViewControllerDelegate extension RootSplitViewController: UISplitViewControllerDelegate { -// // .regular to .compact -// // move navigation stack from .supplementary & .secondary to .compact -// func splitViewController( -// _ svc: UISplitViewController, -// topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column -// ) -> UISplitViewController.Column { -// switch proposedTopColumn { -// case .compact: -// guard let index = MainTabBarController.Tab.allCases.firstIndex(of: currentSupplementaryTab) else { -// assertionFailure() -// break -// } -// mainTabBarController.selectedIndex = index -// mainTabBarController.currentTab.value = currentSupplementaryTab -// -// guard let navigationController = mainTabBarController.selectedViewController as? UINavigationController else { break } -// navigationController.popToRootViewController(animated: false) -// var viewControllers = navigationController.viewControllers // init navigation stack with topMost -// -// if let supplementaryNavigationController = viewController(for: .supplementary) as? UINavigationController { -// // append supplementary -// viewControllers.append(contentsOf: supplementaryNavigationController.popToRootViewController(animated: true) ?? []) -// } -// if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { -// // append secondary -// viewControllers.append(contentsOf: secondaryNavigationController.popToRootViewController(animated: true) ?? []) -// } -// // set navigation stack -// navigationController.setViewControllers(viewControllers, animated: false) -// -// default: -// assertionFailure() -// } -// -// return proposedTopColumn -// } -// -// // .compact to .regular -// // restore navigation stack to .supplementary & .secondary -// func splitViewController( -// _ svc: UISplitViewController, -// displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode -// ) -> UISplitViewController.DisplayMode { -// let compactNavigationController = mainTabBarController.selectedViewController as? UINavigationController -// -// if let topMost = compactNavigationController?.topMost, -// topMost is AccountListViewController { -// topMost.dismiss(animated: false, completion: nil) -// } -// -// let viewControllers = compactNavigationController?.popToRootViewController(animated: true) ?? [] -// -// var supplementaryViewControllers: [UIViewController] = [] -// var secondaryViewControllers: [UIViewController] = [] -// for viewController in viewControllers { -// if coordinator.secondaryStackHashValues.contains(viewController.hashValue) { -// secondaryViewControllers.append(viewController) -// } else { -// supplementaryViewControllers.append(viewController) -// } -// -// } -// if let supplementary = viewController(for: .supplementary) as? UINavigationController { -// supplementary.setViewControllers(supplementary.viewControllers + supplementaryViewControllers, animated: false) -// } -// if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController { -// secondaryNavigationController.setViewControllers(secondaryNavigationController.viewControllers + secondaryViewControllers, animated: false) -// } -// -// return proposedDisplayMode -// } + private static func transform(from: UITabBarController, to: UITabBarController) { + let sourceNavigationControllers = from.viewControllers ?? [] + let targetNavigationControllers = to.viewControllers ?? [] + + for (source, target) in zip(sourceNavigationControllers, targetNavigationControllers) { + guard let source = source as? UINavigationController, + let target = target as? UINavigationController + else { continue } + let viewControllers = source.popToRootViewController(animated: false) ?? [] + _ = target.popToRootViewController(animated: false) + target.viewControllers.append(contentsOf: viewControllers) + } + + to.selectedIndex = from.selectedIndex + } + + // .regular to .compact + func splitViewController( + _ svc: UISplitViewController, + topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column + ) -> UISplitViewController.Column { + switch proposedTopColumn { + case .compact: + RootSplitViewController.transform(from: contentSplitViewController.mainTabBarController, to: compactMainTabBarViewController) + compactMainTabBarViewController.currentTab.value = contentSplitViewController.currentSupplementaryTab + + default: + assertionFailure() + } + + return proposedTopColumn + } + + // .compact to .regular + func splitViewController( + _ svc: UISplitViewController, + displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode + ) -> UISplitViewController.DisplayMode { + let compactNavigationController = compactMainTabBarViewController.selectedViewController as? UINavigationController + + if let topMost = compactNavigationController?.topMost, + topMost is AccountListViewController { + topMost.dismiss(animated: false, completion: nil) + } + + RootSplitViewController.transform(from: compactMainTabBarViewController, to: contentSplitViewController.mainTabBarController) + contentSplitViewController.currentSupplementaryTab = compactMainTabBarViewController.currentTab.value + + return proposedDisplayMode + } } - -//extension UIView { -// func setNeedsLayoutForSubviews() { -// self.subviews.forEach({ -// $0.setNeedsLayout() -// $0.setNeedsLayoutForSubviews() -// }) -// } -//} From bcddcf226b8e6527e30cd2b45600c3b89a4f36fc Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 17:26:26 +0800 Subject: [PATCH 84/95] feat: add account switcher for iPad sidebar --- .../xcschemes/xcschememanagement.plist | 8 ++-- Mastodon/Coordinator/SceneCoordinator.swift | 6 ++- .../Cell/AddAccountTableViewCell.swift | 26 ++++++++++- .../Root/ContentSplitViewController.swift | 18 +++++++- .../Scene/Root/RootSplitViewController.swift | 33 +++++++++++--- .../Root/Sidebar/SidebarViewController.swift | 44 ++++++++++--------- .../Sidebar/View/SidebarListContentView.swift | 4 ++ 7 files changed, 106 insertions(+), 33 deletions(-) diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index cb88c3960..879d91053 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 42 + 35 CoreDataStack.xcscheme_^#shared#^_ orderHint - 43 + 36 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 44 + 37 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 41 + 38 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 23c2a6f44..696b211bc 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -139,6 +139,7 @@ extension SceneCoordinator { case show // push case showDetail // replace case modal(animated: Bool, completion: (() -> Void)? = nil) + case popover(sourceView: UIView) case panModal case custom(transitioningDelegate: UIViewControllerTransitioningDelegate) case customPush @@ -326,7 +327,10 @@ extension SceneCoordinator { panModalPresentable.transitioningDelegate = PanModalPresentationDelegate.default presentingViewController.present(panModalPresentable, animated: true, completion: nil) //presentingViewController.presentPanModal(panModalPresentable) - + case .popover(let sourceView): + viewController.modalPresentationStyle = .popover + viewController.popoverPresentationController?.sourceView = sourceView + (splitViewController ?? presentingViewController)?.present(viewController, animated: true, completion: nil) case .custom(let transitioningDelegate): viewController.modalPresentationStyle = .custom viewController.transitioningDelegate = transitioningDelegate diff --git a/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift b/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift index 743ad1dc2..722896641 100644 --- a/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift +++ b/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift @@ -9,7 +9,7 @@ import UIKit import MetaTextKit final class AddAccountTableViewCell: UITableViewCell { - + let iconImageView: UIImageView = { let image = UIImage(systemName: "plus.circle.fill")! let imageView = UIImageView(image: image) @@ -51,6 +51,28 @@ extension AddAccountTableViewCell { ]) iconImageView.setContentHuggingPriority(.defaultLow, for: .horizontal) iconImageView.setContentHuggingPriority(.defaultLow, for: .vertical) + + // layout the same placeholder UI from `AccountListTableViewCell` + let placeholderLabelContainerStackView = UIStackView() + placeholderLabelContainerStackView.axis = .vertical + placeholderLabelContainerStackView.distribution = .equalCentering + placeholderLabelContainerStackView.spacing = 2 + placeholderLabelContainerStackView.distribution = .fillProportionally + placeholderLabelContainerStackView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(placeholderLabelContainerStackView) + NSLayoutConstraint.activate([ + placeholderLabelContainerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), + placeholderLabelContainerStackView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 10), + contentView.bottomAnchor.constraint(equalTo: placeholderLabelContainerStackView.bottomAnchor, constant: 10), + iconImageView.heightAnchor.constraint(equalTo: placeholderLabelContainerStackView.heightAnchor, multiplier: 0.8).priority(.required - 10), + ]) + let _nameLabel = MetaLabel(style: .accountListName) + _nameLabel.configure(content: PlaintextMetaContent(string: " ")) + let _usernameLabel = MetaLabel(style: .accountListUsername) + _usernameLabel.configure(content: PlaintextMetaContent(string: " ")) + placeholderLabelContainerStackView.addArrangedSubview(_nameLabel) + placeholderLabelContainerStackView.addArrangedSubview(_usernameLabel) + placeholderLabelContainerStackView.isHidden = true titleLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(titleLabel) @@ -58,7 +80,7 @@ extension AddAccountTableViewCell { titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 10), contentView.bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 15), - iconImageView.heightAnchor.constraint(equalTo: titleLabel.heightAnchor, multiplier: 1.0).priority(.required - 10), + // iconImageView.heightAnchor.constraint(equalTo: titleLabel.heightAnchor, multiplier: 1.0).priority(.required - 10), titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), ]) diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift index 6f5e294cd..6e595f2ee 100644 --- a/Mastodon/Scene/Root/ContentSplitViewController.swift +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -63,7 +63,7 @@ extension ContentSplitViewController { sidebarViewController.didMove(toParent: self) NSLayoutConstraint.activate([ mainTabBarController.view.topAnchor.constraint(equalTo: view.topAnchor), - mainTabBarController.view.leadingAnchor.constraint(equalTo: sidebarViewController.view.trailingAnchor), + mainTabBarController.view.leadingAnchor.constraint(equalTo: sidebarViewController.view.trailingAnchor, constant: UIView.separatorLineHeight(of: view)), mainTabBarController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), mainTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) @@ -87,6 +87,22 @@ extension ContentSplitViewController: SidebarViewControllerDelegate { assertionFailure() return } + let previousTab = currentSupplementaryTab currentSupplementaryTab = tab + + if previousTab == tab, + let navigationController = mainTabBarController.selectedViewController as? UINavigationController + { + navigationController.popToRootViewController(animated: true) + } } + + func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView) { + guard case let .tab(tab) = item, tab == .me else { return } + + 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) + } + } diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index fc1e7a4f7..22354751d 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -26,8 +26,17 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { return contentSplitViewController }() + private(set) lazy var searchViewController: SearchViewController = { + let searchViewController = SearchViewController() + searchViewController.context = context + searchViewController.coordinator = coordinator + return searchViewController + }() + lazy var compactMainTabBarViewController = MainTabBarController(context: context, coordinator: coordinator) + let separatorLine = UIView.separatorLine + init(context: AppContext, coordinator: SceneCoordinator) { self.context = context self.coordinator = coordinator @@ -48,13 +57,9 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { // Fallback on earlier versions } - setViewController(UIViewController(), for: .primary) + setViewController(searchViewController, for: .primary) setViewController(contentSplitViewController, for: .secondary) setViewController(compactMainTabBarViewController, for: .compact) - - contentSplitViewController.sidebarViewController.view.layer.zPosition = 100 - contentSplitViewController.mainTabBarController.view.layer.zPosition = 90 - view.layer.zPosition = 80 } required init?(coder: NSCoder) { @@ -80,6 +85,15 @@ extension RootSplitViewController { self.updateBehavior(size: self.view.frame.size) } .store(in: &disposeBag) + + setupBackground(theme: ThemeService.shared.currentTheme.value) + ThemeService.shared.currentTheme + .receive(on: DispatchQueue.main) + .sink { [weak self] theme in + guard let self = self else { return } + self.setupBackground(theme: theme) + } + .store(in: &disposeBag) } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -108,6 +122,15 @@ extension RootSplitViewController { } +extension RootSplitViewController { + + private func setupBackground(theme: Theme) { + // this set column separator line color + view.backgroundColor = theme.separator + } + +} + // MARK: - UISplitViewControllerDelegate extension RootSplitViewController: UISplitViewControllerDelegate { diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift index 060091922..7958c5080 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift @@ -12,10 +12,13 @@ import CoreDataStack protocol SidebarViewControllerDelegate: AnyObject { func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) + func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView) } final class SidebarViewController: UIViewController, NeedsDependency { + let logger = Logger(subsystem: "SidebarViewController", category: "ViewController") + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } @@ -127,6 +130,10 @@ extension SidebarViewController { self.collectionView.contentInset.bottom = height } .store(in: &observations) + + let sidebarLongPressGestureRecognizer = UILongPressGestureRecognizer() + sidebarLongPressGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarLongPressGestureRecognizerHandler(_:))) + collectionView.addGestureRecognizer(sidebarLongPressGestureRecognizer) } private func setupBackground(theme: Theme) { @@ -148,6 +155,23 @@ extension SidebarViewController { } +extension SidebarViewController { + @objc private func sidebarLongPressGestureRecognizerHandler(_ sender: UILongPressGestureRecognizer) { + guard sender.state == .began else { return } + + logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)") + assert(sender.view === collectionView) + + let position = sender.location(in: collectionView) + guard let indexPath = collectionView.indexPathForItem(at: position) else { return } + guard let diffableDataSource = viewModel.diffableDataSource else { return } + guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return } + guard let cell = collectionView.cellForItem(at: indexPath) else { return } + delegate?.sidebarViewController(self, didLongPressItem: item, sourceView: cell) + } + +} + // MARK: - UICollectionViewDelegate extension SidebarViewController: UICollectionViewDelegate { @@ -179,25 +203,5 @@ extension SidebarViewController: UICollectionViewDelegate { default: assertionFailure() } -// switch item { -// case .tab(let tab): -// delegate?.sidebarViewController(self, didSelectTab: tab) -// case .searchHistory(let viewModel): -// delegate?.sidebarViewController(self, didSelectSearchHistory: viewModel) -// case .header: -// break -// case .account(let viewModel): -// assert(Thread.isMainThread) -// let authentication = context.managedObjectContext.object(with: viewModel.authenticationObjectID) as! MastodonAuthentication -// context.authenticationService.activeMastodonUser(domain: authentication.domain, userID: authentication.userID) -// .receive(on: DispatchQueue.main) -// .sink { [weak self] result in -// guard let self = self else { return } -// self.coordinator.setup() -// } -// .store(in: &disposeBag) -// case .addAccount: -// coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil)) -// } } } diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift index 8c013efda..d85d3a8be 100644 --- a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift +++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift @@ -18,6 +18,7 @@ final class SidebarListContentView: UIView, UIContentView { let avatarButton: CircleAvatarButton = { let button = CircleAvatarButton() button.borderWidth = 2 + button.borderColor = UIColor.label.cgColor return button }() @@ -71,6 +72,9 @@ extension SidebarListContentView { imageView.contentMode = .scaleAspectFit avatarButton.contentMode = .scaleAspectFit + + imageView.isUserInteractionEnabled = false + avatarButton.isUserInteractionEnabled = false } private func apply(configuration: ContentConfiguration) { From e8e655ff5c7225657ac53e96b26f45199dca297b Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 18:08:47 +0800 Subject: [PATCH 85/95] feat: add accessibility supports for iPad sidebar --- Localization/app.json | 1 + Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Localization/app.json b/Localization/app.json index 3ec77cf10..3d0e36d03 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -67,6 +67,7 @@ "done": "Done", "confirm": "Confirm", "continue": "Continue", + "compose": "Compose", "cancel": "Cancel", "discard": "Discard", "try_again": "Try Again", diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift index 6dd46d08c..83abf4e6b 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift @@ -84,6 +84,8 @@ extension SidebarViewModel { imageURL: imageURL ) cell.setNeedsUpdateConfiguration() + cell.isAccessibilityElement = true + cell.accessibilityLabel = item.title switch item { case .notification: @@ -103,6 +105,10 @@ extension SidebarViewModel { cell._contentView?.imageView.image = image } .store(in: &cell.disposeBag) + case .me: + guard let authentication = self.context.authenticationService.activeMastodonAuthentication.value else { break } + let currentUserDisplayName = authentication.user.displayNameWithFallback ?? "no user" + cell.accessibilityHint = L10n.Scene.AccountList.tabBarHint(currentUserDisplayName) default: break } @@ -112,6 +118,8 @@ extension SidebarViewModel { guard let self = self else { return } cell.item = item cell.setNeedsUpdateConfiguration() + cell.isAccessibilityElement = true + cell.accessibilityLabel = item.title } // header @@ -132,7 +140,7 @@ extension SidebarViewModel { return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item) case .compose: let item = SidebarListContentView.Item( - title: "Compose", // FIXME: + title: "Compose", // TODO: update i18n image: UIImage(systemName: "square.and.pencil")!, imageURL: nil ) From bb79e574124fa772859a5362c5eae0def750b189 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 18:14:51 +0800 Subject: [PATCH 86/95] chore: set Home page navigation item hidden on iPad --- Mastodon/Scene/Root/ContentSplitViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift index 6e595f2ee..577f8d6a4 100644 --- a/Mastodon/Scene/Root/ContentSplitViewController.swift +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -31,6 +31,10 @@ final class ContentSplitViewController: UIViewController, NeedsDependency { @Published var currentSupplementaryTab: MainTabBarController.Tab = .home private(set) lazy var mainTabBarController: MainTabBarController = { let mainTabBarController = MainTabBarController(context: context, coordinator: coordinator) + if let homeTimelineViewController = mainTabBarController.viewController(of: HomeTimelineViewController.self) { + homeTimelineViewController.viewModel.displayComposeBarButtonItem.value = false + homeTimelineViewController.viewModel.displaySettingBarButtonItem.value = false + } return mainTabBarController }() From 2f0b321fd9023a6806d1ce5f3953e30a0489ad57 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 18:35:02 +0800 Subject: [PATCH 87/95] chore: update tab bar inactive color --- .../Contents.json | 12 ++++++------ .../Contents.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/tab.bar.item.inactive.icon.color.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/tab.bar.item.inactive.icon.color.colorset/Contents.json index 1accfacdf..bfc2a11b2 100644 --- a/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/tab.bar.item.inactive.icon.color.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/Mastodon/tab.bar.item.inactive.icon.color.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.549", - "green" : "0.510", - "red" : "0.431" + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "200", - "green" : "174", - "red" : "155" + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" } }, "idiom" : "universal" diff --git a/Mastodon/Resources/Assets.xcassets/Theme/system/tab.bar.item.inactive.icon.color.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Theme/system/tab.bar.item.inactive.icon.color.colorset/Contents.json index ece9000aa..bfc2a11b2 100644 --- a/Mastodon/Resources/Assets.xcassets/Theme/system/tab.bar.item.inactive.icon.color.colorset/Contents.json +++ b/Mastodon/Resources/Assets.xcassets/Theme/system/tab.bar.item.inactive.icon.color.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.549", - "green" : "0.510", - "red" : "0.431" + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "140", - "green" : "130", - "red" : "110" + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" } }, "idiom" : "universal" From e669d17d4922452dfda65ce0ce5161f60fd42d72 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 18:56:58 +0800 Subject: [PATCH 88/95] feat: make notification coordinator works --- Mastodon/Coordinator/SceneCoordinator.swift | 94 +++++++++---------- .../Header/ProfileHeaderViewController.swift | 23 +++-- .../Scene/Profile/ProfileViewController.swift | 3 + 3 files changed, 62 insertions(+), 58 deletions(-) diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 696b211bc..10d6fb84f 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -90,45 +90,44 @@ final public class SceneCoordinator { // Delay in next run loop -// DispatchQueue.main.async { [weak self] in -// guard let self = self else { return } -// -// // Note: -// // show (push) on phone or pad (compact) -// // showDetail in .secondary in UISplitViewController on pad (expand) -// let from: UIViewController? = { -// if let splitViewController = self.splitViewController { -// if splitViewController.mainTabBarController.topMost?.view.window != nil { -// // compact -// return splitViewController.mainTabBarController.topMost -// } else { -// // expand -// return splitViewController.viewController(for: .supplementary) -// } -// } else { -// return self.tabBarController.topMost -// } -// }() -// -// // show notification related content -// guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } -// let notificationID = String(pushNotification.notificationID) -// -// switch type { -// case .follow: -// let profileViewModel = RemoteProfileViewModel(context: appContext, notificationID: notificationID) -// self.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) -// case .followRequest: -// // do nothing -// break -// case .mention, .reblog, .favourite, .poll, .status: -// let threadViewModel = RemoteThreadViewModel(context: appContext, notificationID: notificationID) -// self.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show) -// case ._other: -// assertionFailure() -// break -// } -// } // end DispatchQueue.main.async + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } + + // Note: + // show (push) on phone and pad + let from: UIViewController? = { + if let splitViewController = self.splitViewController { + if splitViewController.compactMainTabBarViewController.topMost?.view.window != nil { + // compact + return splitViewController.compactMainTabBarViewController.topMost + } else { + // expand + return splitViewController.contentSplitViewController.mainTabBarController.topMost + } + } else { + return self.tabBarController.topMost + } + }() + + // show notification related content + guard let type = Mastodon.Entity.Notification.NotificationType(rawValue: pushNotification.notificationType) else { return } + let notificationID = String(pushNotification.notificationID) + + switch type { + case .follow: + let profileViewModel = RemoteProfileViewModel(context: appContext, notificationID: notificationID) + self.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) + case .followRequest: + // do nothing + break + case .mention, .reblog, .favourite, .poll, .status: + let threadViewModel = RemoteThreadViewModel(context: appContext, notificationID: notificationID) + self.present(scene: .thread(viewModel: threadViewModel), from: from, transition: .show) + case ._other: + assertionFailure() + break + } + } // end DispatchQueue.main.async } .store(in: &disposeBag) } @@ -283,18 +282,6 @@ extension SceneCoordinator { switch transition { case .show: -// if let splitViewController = splitViewController, !splitViewController.isCollapsed, -// let supplementaryViewController = splitViewController.viewController(for: .supplementary) as? UINavigationController, -// (supplementaryViewController === presentingViewController || supplementaryViewController.viewControllers.contains(presentingViewController)) || -// (presentingViewController is UserTimelineViewController && presentingViewController.view.isDescendant(of: supplementaryViewController.view)) -// { -// fallthrough -// } else { -// if secondaryStackHashValues.contains(presentingViewController.hashValue) { -// secondaryStackHashValues.insert(viewController.hashValue) -// } -// presentingViewController.show(viewController, sender: sender) -// } presentingViewController.show(viewController, sender: sender) case .showDetail: secondaryStackHashValues.insert(viewController.hashValue) @@ -362,6 +349,11 @@ extension SceneCoordinator { } func switchToTabBar(tab: MainTabBarController.Tab) { + splitViewController?.contentSplitViewController.currentSupplementaryTab = tab + + splitViewController?.compactMainTabBarViewController.selectedIndex = tab.rawValue + splitViewController?.compactMainTabBarViewController.currentTab.value = tab + tabBarController.selectedIndex = tab.rawValue tabBarController.currentTab.value = tab } diff --git a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift index 716b62307..34716dde5 100644 --- a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift +++ b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift @@ -44,10 +44,11 @@ final class ProfileHeaderViewController: UIViewController { let profileHeaderView = ProfileHeaderView() let pageSegmentedControl: UISegmentedControl = { - let segmenetedControl = UISegmentedControl(items: ["A", "B"]) - segmenetedControl.selectedSegmentIndex = 0 - return segmenetedControl + let segmentedControl = UISegmentedControl(items: ["A", "B"]) + segmentedControl.selectedSegmentIndex = 0 + return segmentedControl }() + var pageSegmentedControlLeadingLayoutConstraint: NSLayoutConstraint! private var isBannerPinned = false private var bottomShadowAlpha: CGFloat = 0.0 @@ -118,9 +119,10 @@ extension ProfileHeaderViewController { pageSegmentedControl.translatesAutoresizingMaskIntoConstraints = false view.addSubview(pageSegmentedControl) + pageSegmentedControlLeadingLayoutConstraint = pageSegmentedControl.leadingAnchor.constraint(equalTo: view.leadingAnchor) NSLayoutConstraint.activate([ pageSegmentedControl.topAnchor.constraint(equalTo: profileHeaderView.bottomAnchor, constant: ProfileHeaderViewController.segmentedControlMarginHeight), - pageSegmentedControl.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor), + pageSegmentedControlLeadingLayoutConstraint, // Fix iPad layout issue pageSegmentedControl.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor), view.bottomAnchor.constraint(equalTo: pageSegmentedControl.bottomAnchor, constant: ProfileHeaderViewController.segmentedControlMarginHeight), pageSegmentedControl.heightAnchor.constraint(equalToConstant: ProfileHeaderViewController.segmentedControlHeight).priority(.defaultHigh), @@ -133,10 +135,10 @@ extension ProfileHeaderViewController { viewModel.isTitleViewContentOffsetSet.eraseToAnyPublisher() ) .receive(on: DispatchQueue.main) - .sink { [weak self] viewDidAppear, isTitleViewContentOffsetDidSetted in + .sink { [weak self] viewDidAppear, isTitleViewContentOffsetDidSet in guard let self = self else { return } - self.titleView.titleLabel.alpha = viewDidAppear && isTitleViewContentOffsetDidSetted ? 1 : 0 - self.titleView.subtitleLabel.alpha = viewDidAppear && isTitleViewContentOffsetDidSetted ? 1 : 0 + self.titleView.titleLabel.alpha = viewDidAppear && isTitleViewContentOffsetDidSet ? 1 : 0 + self.titleView.subtitleLabel.alpha = viewDidAppear && isTitleViewContentOffsetDidSet ? 1 : 0 } .store(in: &disposeBag) @@ -283,6 +285,13 @@ extension ProfileHeaderViewController { setupBottomShadow() } + override func viewLayoutMarginsDidChange() { + super.viewLayoutMarginsDidChange() + + let margin = view.frame.maxX - view.readableContentGuide.layoutFrame.maxX + pageSegmentedControlLeadingLayoutConstraint.constant = margin + } + } extension ProfileHeaderViewController { diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 434836ab4..b864fc94c 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -517,6 +517,7 @@ extension ProfileViewController { .assign(to: \.value, on: profileHeaderViewController.viewModel.displayProfileInfo.note) .store(in: &disposeBag) viewModel.statusesCount + .receive(on: DispatchQueue.main) .sink { [weak self] count in guard let self = self else { return } let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-" @@ -526,6 +527,7 @@ extension ProfileViewController { } .store(in: &disposeBag) viewModel.followingCount + .receive(on: DispatchQueue.main) .sink { [weak self] count in guard let self = self else { return } let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-" @@ -535,6 +537,7 @@ extension ProfileViewController { } .store(in: &disposeBag) viewModel.followersCount + .receive(on: DispatchQueue.main) .sink { [weak self] count in guard let self = self else { return } let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-" From 9e2566e2a7e8c172c187f0e8b049827ddcef1dc3 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 19:00:23 +0800 Subject: [PATCH 89/95] chore: update i18n resources --- .../Resources/ar.lproj/Localizable.strings | 8 ++--- .../ca.lproj/Localizable.stringsdict | 28 ++++++++--------- .../Resources/de.lproj/Localizable.strings | 4 +-- .../Resources/gd-GB.lproj/Localizable.strings | 12 ++++---- .../gd-GB.lproj/Localizable.stringsdict | 8 ++--- .../Resources/ja.lproj/Localizable.strings | 9 +++--- .../ja.lproj/Localizable.stringsdict | 12 ++++---- MastodonIntent/ar.lproj/Intents.strings | 30 +++++++++---------- 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/Mastodon/Resources/ar.lproj/Localizable.strings b/Mastodon/Resources/ar.lproj/Localizable.strings index 98d8c09cb..5950546a9 100644 --- a/Mastodon/Resources/ar.lproj/Localizable.strings +++ b/Mastodon/Resources/ar.lproj/Localizable.strings @@ -9,7 +9,7 @@ "Common.Alerts.DiscardPostContent.Message" = "Confirm to discard composed post content."; "Common.Alerts.DiscardPostContent.Title" = "تجاهل المسودة"; "Common.Alerts.EditProfileFailure.Message" = "لا يمكن تعديل الملف الشخصي. يُرجى المحاولة مرة أُخرى."; -"Common.Alerts.EditProfileFailure.Title" = "Edit Profile Error"; +"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. @@ -275,7 +275,7 @@ uploaded to Mastodon."; "Scene.Search.Searching.Segment.People" = "الأشخاص"; "Scene.Search.Searching.Segment.Posts" = "المنشورات"; "Scene.Search.Title" = "بحث"; -"Scene.ServerPicker.Button.Category.Academia" = "academia"; +"Scene.ServerPicker.Button.Category.Academia" = "أكاديمي"; "Scene.ServerPicker.Button.Category.Activism" = "للنشطاء"; "Scene.ServerPicker.Button.Category.All" = "الكل"; "Scene.ServerPicker.Button.Category.AllAccessiblityDescription" = "الفئة: الكل"; @@ -298,8 +298,8 @@ uploaded to Mastodon."; "Scene.ServerPicker.Label.Category" = "الفئة"; "Scene.ServerPicker.Label.Language" = "اللغة"; "Scene.ServerPicker.Label.Users" = "مستخدمون·ات"; -"Scene.ServerPicker.Title" = "Pick a server, -any server."; +"Scene.ServerPicker.Title" = "اِختر خادِم، +أي خادِم."; "Scene.ServerRules.Button.Confirm" = "انا أوافق"; "Scene.ServerRules.PrivacyPolicy" = "سياسة الخصوصية"; "Scene.ServerRules.Prompt" = "إن اخترت المواصلة، فإنك تخضع لشروط الخدمة وسياسة الخصوصية لـ %@."; diff --git a/Mastodon/Resources/ca.lproj/Localizable.stringsdict b/Mastodon/Resources/ca.lproj/Localizable.stringsdict index cc7312938..140185bad 100644 --- a/Mastodon/Resources/ca.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/ca.lproj/Localizable.stringsdict @@ -21,7 +21,7 @@ a11y.plural.count.input_limit_exceeds NSStringLocalizedFormatKey - El límit d’entrada supera a %#@character_count@ + El límit de la entrada supera a %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -37,7 +37,7 @@ a11y.plural.count.input_limit_remains NSStringLocalizedFormatKey - El límit d’entrada continua sent %#@character_count@ + El límit de la entrada continua sent %#@character_count@ character_count NSStringFormatSpecTypeKey @@ -111,7 +111,7 @@ one 1 impuls other - %ld impuls + %ld impulsos plural.count.vote @@ -301,9 +301,9 @@ NSStringFormatValueTypeKey ld one - fa 1a + fa 1 any other - fa %ldy anys + fa %ld anys date.month.ago.abbr @@ -317,9 +317,9 @@ NSStringFormatValueTypeKey ld one - fa 1M + fa 1 mes other - fa %ldM mesos + fa %ld mesos date.day.ago.abbr @@ -333,9 +333,9 @@ NSStringFormatValueTypeKey ld one - fa 1d + fa 1 día other - fa %ldd dies + fa %ld dies date.hour.ago.abbr @@ -351,7 +351,7 @@ one fa 1h other - fa %ldh hores + fa %ld hores date.minute.ago.abbr @@ -365,9 +365,9 @@ NSStringFormatValueTypeKey ld one - fa 1m + fa 1 minut other - fa %ldm minuts + fa %ld minuts date.second.ago.abbr @@ -381,9 +381,9 @@ NSStringFormatValueTypeKey ld one - fa 1s + fa 1 segon other - fa %lds seg + fa %ld segons diff --git a/Mastodon/Resources/de.lproj/Localizable.strings b/Mastodon/Resources/de.lproj/Localizable.strings index 51028d7a8..2780723ed 100644 --- a/Mastodon/Resources/de.lproj/Localizable.strings +++ b/Mastodon/Resources/de.lproj/Localizable.strings @@ -135,7 +135,7 @@ Dein Profil sieht für diesen Benutzer auch so aus."; "Common.Controls.Timeline.Timestamp.Now" = "Gerade"; "Scene.AccountList.AddAccount" = "Konto hinzufügen"; "Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; -"Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; +"Scene.AccountList.TabBarHint" = "Aktuell ausgewähltes Profil: %@. Doppeltippen dann gedrückt halten, um den Kontoschalter anzuzeigen"; "Scene.Compose.Accessibility.AppendAttachment" = "Anhang hinzufügen"; "Scene.Compose.Accessibility.AppendPoll" = "Umfrage hinzufügen"; "Scene.Compose.Accessibility.CustomEmojiPicker" = "Benutzerdefinierter Emojiwähler"; @@ -341,5 +341,5 @@ beliebigen Server."; "Scene.Thread.Title" = "Beitrag von %@"; "Scene.Welcome.Slogan" = "Soziale Netzwerke wieder in deinen Händen."; "Scene.Wizard.AccessibilityHint" = "Doppeltippen, um diesen Assistenten zu schließen"; -"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button."; +"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Wechsel zwischen mehreren Konten durch drücken der Profil-Schaltfläche."; "Scene.Wizard.NewInMastodon" = "Neu in Mastodon"; \ No newline at end of file diff --git a/Mastodon/Resources/gd-GB.lproj/Localizable.strings b/Mastodon/Resources/gd-GB.lproj/Localizable.strings index f24bd24e9..6c01adb0a 100644 --- a/Mastodon/Resources/gd-GB.lproj/Localizable.strings +++ b/Mastodon/Resources/gd-GB.lproj/Localizable.strings @@ -133,9 +133,9 @@ Seo an coltas a th’ air a’ phròifil agad dhaibh-san."; "Common.Controls.Timeline.Loader.LoadingMissingPosts" = "A’ luchdadh nam post a tha a dhìth…"; "Common.Controls.Timeline.Loader.ShowMoreReplies" = "Seall barrachd freagairtean"; "Common.Controls.Timeline.Timestamp.Now" = "An-dràsta"; -"Scene.AccountList.AddAccount" = "Add Account"; -"Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; -"Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; +"Scene.AccountList.AddAccount" = "Cuir cunntas ris"; +"Scene.AccountList.DismissAccountSwitcher" = "Leig seachad taghadh a’ chunntais"; +"Scene.AccountList.TabBarHint" = "A’ phròifil air a taghadh: %@. Thoir gnogag dhùbailte is cùm sìos a ghearradh leum gu cunntas eile"; "Scene.Compose.Accessibility.AppendAttachment" = "Cuir ceanglachan ris"; "Scene.Compose.Accessibility.AppendPoll" = "Cuir cunntas-bheachd ris"; "Scene.Compose.Accessibility.CustomEmojiPicker" = "Roghnaichear nan Emoji gnàthaichte"; @@ -340,6 +340,6 @@ thoir gnogag air a’ chunntas a dhearbhadh a’ chunntais agad."; "Scene.Thread.Title" = "Post le %@"; "Scene.Welcome.Slogan" = "A’ cur nan lìonraidhean sòisealta ’nad làmhan fhèin."; -"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"; \ No newline at end of file +"Scene.Wizard.AccessibilityHint" = "Thoir gnogag dhùbailte a’ leigeil seachad an draoidh seo"; +"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Geàrr leum eadar iomadh cunntas le cumail sìos putan na pròifil."; +"Scene.Wizard.NewInMastodon" = "Na tha ùr ann am Mastodon"; \ No newline at end of file diff --git a/Mastodon/Resources/gd-GB.lproj/Localizable.stringsdict b/Mastodon/Resources/gd-GB.lproj/Localizable.stringsdict index 41e592a5e..7a54f553e 100644 --- a/Mastodon/Resources/gd-GB.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/gd-GB.lproj/Localizable.stringsdict @@ -13,13 +13,13 @@ NSStringFormatValueTypeKey ld one - 1 unread notification + %ld bhrath nach deach a leughadh two - %ld unread notification + %ld bhrath nach deach a leughadh few - %ld unread notification + %ld brathan nach deach a leughadh other - %ld unread notification + %ld brath nach deach a leughadh a11y.plural.count.input_limit_exceeds diff --git a/Mastodon/Resources/ja.lproj/Localizable.strings b/Mastodon/Resources/ja.lproj/Localizable.strings index e83278e34..beadccf22 100644 --- a/Mastodon/Resources/ja.lproj/Localizable.strings +++ b/Mastodon/Resources/ja.lproj/Localizable.strings @@ -129,7 +129,7 @@ "Common.Controls.Timeline.Loader.LoadingMissingPosts" = "読込中..."; "Common.Controls.Timeline.Loader.ShowMoreReplies" = "リプライをもっとみる"; "Common.Controls.Timeline.Timestamp.Now" = "今"; -"Scene.AccountList.AddAccount" = "Add Account"; +"Scene.AccountList.AddAccount" = "アカウントを追加"; "Scene.AccountList.DismissAccountSwitcher" = "Dismiss Account Switcher"; "Scene.AccountList.TabBarHint" = "Current selected profile: %@. Double tap then hold to show account switcher"; "Scene.Compose.Accessibility.AppendAttachment" = "アタッチメントの追加"; @@ -332,8 +332,7 @@ "Scene.SuggestionAccount.Title" = "フォローする人を探す"; "Scene.Thread.BackTitle" = "投稿"; "Scene.Thread.Title" = "%@の投稿"; -"Scene.Welcome.Slogan" = "Social networking -back in your hands."; +"Scene.Welcome.Slogan" = "ソーシャルネットワーキングを、あなたの手の中に."; "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"; \ No newline at end of file +"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "プロフィールボタンを押して複数のアカウントを切り替えます。"; +"Scene.Wizard.NewInMastodon" = "Mastodon の新機能"; \ No newline at end of file diff --git a/Mastodon/Resources/ja.lproj/Localizable.stringsdict b/Mastodon/Resources/ja.lproj/Localizable.stringsdict index 0300d9dc3..c51a9a29d 100644 --- a/Mastodon/Resources/ja.lproj/Localizable.stringsdict +++ b/Mastodon/Resources/ja.lproj/Localizable.stringsdict @@ -13,7 +13,7 @@ NSStringFormatValueTypeKey ld other - %ld unread notification + %ld 件の未読通知 a11y.plural.count.input_limit_exceeds @@ -27,7 +27,7 @@ NSStringFormatValueTypeKey ld other - %ld characters + %ld 文字 a11y.plural.count.input_limit_remains @@ -41,7 +41,7 @@ NSStringFormatValueTypeKey ld other - %ld characters + %ld 文字 plural.count.metric_formatted.post @@ -111,7 +111,7 @@ NSStringFormatValueTypeKey ld other - %ld votes + %ld票 plural.count.voter @@ -195,7 +195,7 @@ NSStringFormatValueTypeKey ld other - %ld months left + %ldか月前 date.day.left @@ -279,7 +279,7 @@ NSStringFormatValueTypeKey ld other - %ldM ago + %ld分前 date.day.ago.abbr diff --git a/MastodonIntent/ar.lproj/Intents.strings b/MastodonIntent/ar.lproj/Intents.strings index bf3e77ed2..cde27dc97 100644 --- a/MastodonIntent/ar.lproj/Intents.strings +++ b/MastodonIntent/ar.lproj/Intents.strings @@ -1,22 +1,22 @@ -"16wxgf" = "Post on Mastodon"; +"16wxgf" = "النَشر على ماستودون"; "751xkl" = "محتوى نصي"; "CsR7G2" = "انشر على ماستدون"; -"HZSGTr" = "What content to post?"; +"HZSGTr" = "ما المُحتوى المُراد نشره؟"; -"HdGikU" = "Posting failed"; +"HdGikU" = "فَشَلَ النشر"; "KDNTJ4" = "سبب الإخفاق"; -"RHxKOw" = "Send Post with text content"; +"RHxKOw" = "إرسال مَنشور يَحوي نص"; -"RxSqsb" = "Post"; +"RxSqsb" = "مَنشور"; -"WCIR3D" = "Post ${content} on Mastodon"; +"WCIR3D" = "نَشر ${content} على ماستودون"; -"ZKJSNu" = "Post"; +"ZKJSNu" = "مَنشور"; "ZS1XaK" = "${content}"; @@ -24,13 +24,13 @@ "Zo4jgJ" = "مدى ظهور المنشور"; -"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’."; +"apSxMG-dYQ5NN" = "هُناك عدد ${count} خِيار مُطابق لِـ\"عام\"."; -"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’."; +"apSxMG-ehFLjY" = "هُناك عدد ${count} خِيار مُطابق لِـ\"المُتابِعُون فقط\"."; -"ayoYEb-dYQ5NN" = "${content}, Public"; +"ayoYEb-dYQ5NN" = "${content}، عام"; -"ayoYEb-ehFLjY" = "${content}, Followers Only"; +"ayoYEb-ehFLjY" = "${content}، المُتابِعُون فقط"; "dUyuGg" = "النشر على ماستدون"; @@ -38,13 +38,13 @@ "ehFLjY" = "لمتابعيك فقط"; -"gfePDu" = "Posting failed. ${failureReason}"; +"gfePDu" = "فَشَلَ النشر، ${failureReason}"; -"k7dbKQ" = "Post was sent successfully."; +"k7dbKQ" = "تمَّ إرسال المنشور بِنجاح."; -"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?"; +"oGiqmY-dYQ5NN" = "للتأكيد، هل تَريد \"عام\"؟"; -"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?"; +"oGiqmY-ehFLjY" = "للتأكيد، هل تُريد \"للمُتابِعين فقط\"؟"; "rM6dvp" = "عنوان URL"; From b4c240967f0bf04e68ae89997449c518c9f92328 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 19:29:58 +0800 Subject: [PATCH 90/95] feat: add Kurmanji (Kurdish) i18n strings from Crowdin --- .../Sources/StringsConvertor/main.swift | 1 + Mastodon.xcodeproj/project.pbxproj | 11 + .../xcschemes/xcschememanagement.plist | 6 +- .../Resources/ku-TR.lproj/InfoPlist.strings | 4 + .../Resources/ku-TR.lproj/Localizable.strings | 346 ++++++++++++++++ .../ku-TR.lproj/Localizable.stringsdict | 390 ++++++++++++++++++ MastodonIntent/ku-TR.lproj/Intents.strings | 51 +++ .../ku-TR.lproj/Intents.stringsdict | 54 +++ 8 files changed, 860 insertions(+), 3 deletions(-) create mode 100644 Mastodon/Resources/ku-TR.lproj/InfoPlist.strings create mode 100644 Mastodon/Resources/ku-TR.lproj/Localizable.strings create mode 100644 Mastodon/Resources/ku-TR.lproj/Localizable.stringsdict create mode 100644 MastodonIntent/ku-TR.lproj/Intents.strings create mode 100644 MastodonIntent/ku-TR.lproj/Intents.stringsdict diff --git a/Localization/StringsConvertor/Sources/StringsConvertor/main.swift b/Localization/StringsConvertor/Sources/StringsConvertor/main.swift index 124612e5c..6507986be 100644 --- a/Localization/StringsConvertor/Sources/StringsConvertor/main.swift +++ b/Localization/StringsConvertor/Sources/StringsConvertor/main.swift @@ -51,6 +51,7 @@ private func map(language: String) -> String? { case "fr_FR": return "fr" // French case "de_DE": return "de" // German case "ja_JP": return "ja" // Japanese + case "kmr_TR": return "ku-TR" // Kurmanji (Kurdish) case "ru_RU": return "ru" // Russian case "gd_GB": return "gd-GB" // Scottish Gaelic case "es_ES": return "es" // Spanish diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index f7b373f59..cc98e398b 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -1365,6 +1365,11 @@ DBD376AB2692ECDB007FEC24 /* ThemePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemePreference.swift; sourceTree = ""; }; DBD376B1269302A4007FEC24 /* UITableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCell.swift; sourceTree = ""; }; DBD9148F25DF6D8D00903DFD /* APIService+Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Onboarding.swift"; sourceTree = ""; }; + DBDC1CF9272C0FD600055C3D /* ku-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ku-TR"; path = "ku-TR.lproj/Intents.strings"; sourceTree = ""; }; + DBDC1CFA272C0FD600055C3D /* ku-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "ku-TR"; path = "ku-TR.lproj/Localizable.stringsdict"; sourceTree = ""; }; + DBDC1CFB272C0FD600055C3D /* ku-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ku-TR"; path = "ku-TR.lproj/Localizable.strings"; sourceTree = ""; }; + DBDC1CFC272C0FD600055C3D /* ku-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ku-TR"; path = "ku-TR.lproj/InfoPlist.strings"; sourceTree = ""; }; + DBDC1CFD272C0FD600055C3D /* ku-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "ku-TR"; path = "ku-TR.lproj/Intents.stringsdict"; sourceTree = ""; }; DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterViewController.swift; sourceTree = ""; }; DBE0822325CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterViewModel.swift; sourceTree = ""; }; DBE3CDBA261C427900430CC6 /* TimelineHeaderTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineHeaderTableViewCell.swift; sourceTree = ""; }; @@ -3530,6 +3535,7 @@ ru, "gd-GB", th, + "ku-TR", ); mainGroup = DB427DC925BAA00100D1B89D; packageReferences = ( @@ -4560,6 +4566,7 @@ DB4B777F26CA4EFA00B087B3 /* ru */, DB4B778426CA500E00B087B3 /* gd-GB */, DB4B779226CA50BA00B087B3 /* th */, + DBDC1CF9272C0FD600055C3D /* ku-TR */, ); name = Intents.intentdefinition; sourceTree = ""; @@ -4580,6 +4587,7 @@ DB4B778226CA4EFA00B087B3 /* ru */, DB4B778726CA500E00B087B3 /* gd-GB */, DB4B779526CA50BA00B087B3 /* th */, + DBDC1CFC272C0FD600055C3D /* ku-TR */, ); name = InfoPlist.strings; sourceTree = ""; @@ -4600,6 +4608,7 @@ DB4B778126CA4EFA00B087B3 /* ru */, DB4B778626CA500E00B087B3 /* gd-GB */, DB4B779426CA50BA00B087B3 /* th */, + DBDC1CFB272C0FD600055C3D /* ku-TR */, ); name = Localizable.strings; sourceTree = ""; @@ -4636,6 +4645,7 @@ DB4B778026CA4EFA00B087B3 /* ru */, DB4B778526CA500E00B087B3 /* gd-GB */, DB4B779326CA50BA00B087B3 /* th */, + DBDC1CFA272C0FD600055C3D /* ku-TR */, ); name = Localizable.stringsdict; sourceTree = ""; @@ -4656,6 +4666,7 @@ DB4B779026CA504900B087B3 /* fr */, DB4B779126CA504A00B087B3 /* ja */, DB4B779626CA50BA00B087B3 /* th */, + DBDC1CFD272C0FD600055C3D /* ku-TR */, ); name = Intents.stringsdict; sourceTree = ""; diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 879d91053..e0afdf7fc 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 35 + 36 CoreDataStack.xcscheme_^#shared#^_ orderHint - 36 + 35 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 37 + 40 MastodonIntents.xcscheme_^#shared#^_ diff --git a/Mastodon/Resources/ku-TR.lproj/InfoPlist.strings b/Mastodon/Resources/ku-TR.lproj/InfoPlist.strings new file mode 100644 index 000000000..669ecfacf --- /dev/null +++ b/Mastodon/Resources/ku-TR.lproj/InfoPlist.strings @@ -0,0 +1,4 @@ +"NSCameraUsageDescription" = "Bo kişandina wêneyê ji bo rewşa şandiyan tê bikaranîn"; +"NSPhotoLibraryAddUsageDescription" = "Ji bo tomarkirina wêneyê di pirtûkxaneya wêneyan de tê bikaranîn"; +"NewPostShortcutItemTitle" = "Şandiya nû"; +"SearchShortcutItemTitle" = "Bigere"; \ No newline at end of file diff --git a/Mastodon/Resources/ku-TR.lproj/Localizable.strings b/Mastodon/Resources/ku-TR.lproj/Localizable.strings new file mode 100644 index 000000000..345f10cf9 --- /dev/null +++ b/Mastodon/Resources/ku-TR.lproj/Localizable.strings @@ -0,0 +1,346 @@ +"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.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.EditProfileFailure.Message" = "Nikare profîlê serrast bike. Jkx dîsa biceribîne."; +"Common.Alerts.EditProfileFailure.Title" = "Çewtiya profîlê biguherîne"; +"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.Title" = "Tomarkirina wêneyê têkçû"; +"Common.Alerts.ServerError.Title" = "Çewtiya rajekar"; +"Common.Alerts.SignOut.Confirm" = "Derkeve"; +"Common.Alerts.SignOut.Message" = "Ma tu dixwazî ku derkevî?"; +"Common.Alerts.SignOut.Title" = "Derkeve"; +"Common.Alerts.SignUpFailure.Title" = "Tomarkirin têkçû"; +"Common.Alerts.VoteFailure.PollEnded" = "Rapirsîya qediya"; +"Common.Alerts.VoteFailure.Title" = "Dengdayîn têkçû"; +"Common.Controls.Actions.Add" = "Tevlî bike"; +"Common.Controls.Actions.Back" = "Vegere"; +"Common.Controls.Actions.BlockDomain" = "%@ asteng bike"; +"Common.Controls.Actions.Cancel" = "Dev jê berde"; +"Common.Controls.Actions.Confirm" = "Bipejirîne"; +"Common.Controls.Actions.Continue" = "Bidomîne"; +"Common.Controls.Actions.CopyPhoto" = "Wêne kopî bikin"; +"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.Next" = "Pêş"; +"Common.Controls.Actions.Ok" = "BAŞ E"; +"Common.Controls.Actions.Open" = "Veke"; +"Common.Controls.Actions.OpenInSafari" = "Di Safariyê de veke"; +"Common.Controls.Actions.Preview" = "Pêşdîtin"; +"Common.Controls.Actions.Previous" = "Paş"; +"Common.Controls.Actions.Remove" = "Rake"; +"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.SeeMore" = "Bêtir bibîne"; +"Common.Controls.Actions.Settings" = "Sazkarî"; +"Common.Controls.Actions.Share" = "Parve bike"; +"Common.Controls.Actions.SharePost" = "Şandiyê parve bike"; +"Common.Controls.Actions.ShareUser" = "%@ parve bike"; +"Common.Controls.Actions.SignIn" = "Têkeve"; +"Common.Controls.Actions.SignUp" = "Tomar bibe"; +"Common.Controls.Actions.Skip" = "Derbas bike"; +"Common.Controls.Actions.TakePhoto" = "Wêne bikişîne"; +"Common.Controls.Actions.TryAgain" = "Dîsa biceribîne"; +"Common.Controls.Actions.UnblockDomain" = "%@ asteng neke"; +"Common.Controls.Friendship.Block" = "Asteng bike"; +"Common.Controls.Friendship.BlockDomain" = "%@ asteng bike"; +"Common.Controls.Friendship.BlockUser" = "%@ asteng bike"; +"Common.Controls.Friendship.Blocked" = "Astengkirî"; +"Common.Controls.Friendship.EditInfo" = "Zanyariyan serrast bike"; +"Common.Controls.Friendship.Follow" = "Bişopîne"; +"Common.Controls.Friendship.Following" = "Dişopîne"; +"Common.Controls.Friendship.Mute" = "Bêdeng 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.Unblock" = "Astengiyê rake"; +"Common.Controls.Friendship.UnblockUser" = "%@ asteng neke"; +"Common.Controls.Friendship.Unmute" = "Bêdeng neke"; +"Common.Controls.Friendship.UnmuteUser" = "%@ bêdeng neke"; +"Common.Controls.Keyboard.Common.ComposeNewPost" = "Şandiyeke nû binivsîne"; +"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.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.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.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.ContentWarning" = "Hişyariya naverokê"; +"Common.Controls.Status.MediaContentWarning" = "Ji bo aşkerakirinê derekî bitikîne"; +"Common.Controls.Status.Poll.Closed" = "Girtî"; +"Common.Controls.Status.Poll.Vote" = "Deng"; +"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.Link" = "Girêdan"; +"Common.Controls.Status.Tag.Mention" = "Behs"; +"Common.Controls.Status.Tag.Url" = "URL"; +"Common.Controls.Status.UserReblogged" = "%@ ji nû ve hat blogkirin"; +"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î. +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.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î. +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.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.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.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.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.Photo" = "wêne"; +"Scene.Compose.Attachment.Video" = "vîdyo"; +"Scene.Compose.AutoComplete.SpaceToAdd" = "Space to add"; +"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.MediaSelection.Browse" = "Bigere"; +"Scene.Compose.MediaSelection.Camera" = "Wêne bikişîne"; +"Scene.Compose.MediaSelection.PhotoLibrary" = "Wênegeh"; +"Scene.Compose.Poll.DurationTime" = "Dirêjî: %@"; +"Scene.Compose.Poll.OneDay" = "1 Roj"; +"Scene.Compose.Poll.OneHour" = "1 Demjimêr"; +"Scene.Compose.Poll.OptionNumber" = "Vebijêrk %ld"; +"Scene.Compose.Poll.SevenDays" = "7 Roj"; +"Scene.Compose.Poll.SixHours" = "6 Demjimêr"; +"Scene.Compose.Poll.ThirtyMinutes" = "30 xulek"; +"Scene.Compose.Poll.ThreeDays" = "3 Roj"; +"Scene.Compose.ReplyingToUser" = "bersiv bide %@"; +"Scene.Compose.Title.NewPost" = "Şandiya nû"; +"Scene.Compose.Title.NewReply" = "Bersiva nû"; +"Scene.Compose.Visibility.Direct" = "Tenê mirovên ku min qalkirî"; +"Scene.Compose.Visibility.Private" = "Tenê şopîneran"; +"Scene.Compose.Visibility.Public" = "Gelemperî"; +"Scene.Compose.Visibility.Unlisted" = "Nerêzokkirî"; +"Scene.ConfirmEmail.Button.DontReceiveEmail" = "Min hîç e-nameyeke nesitand"; +"Scene.ConfirmEmail.Button.OpenEmailApp" = "Sepana e-nameyê veke"; +"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.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.Title" = "Tiştekî dawî."; +"Scene.Favorite.Title" = "Bijareyên te"; +"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.Title.Everything" = "Her tişt"; +"Scene.Notification.Title.Mentions" = "Behs"; +"Scene.Notification.UserFavorited Your Post" = "%@ posta we bijarte"; +"Scene.Notification.UserFollowedYou" = "%@ te şopand"; +"Scene.Notification.UserMentionedYou" = "%@ behsa 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.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.Following" = "dişopîne"; +"Scene.Profile.Dashboard.Posts" = "şandîyan"; +"Scene.Profile.Fields.AddRow" = "Rêzê lê zêde 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.SegmentedControl.Media" = "Medya"; +"Scene.Profile.SegmentedControl.Posts" = "Şandîyan"; +"Scene.Profile.SegmentedControl.Replies" = "Bersivan"; +"Scene.Register.Error.Item.Agreement" = "Lihevhatin"; +"Scene.Register.Error.Item.Email" = "E-name"; +"Scene.Register.Error.Item.Locale" = "Herêm"; +"Scene.Register.Error.Item.Password" = "Şîfre"; +"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.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.Invalid" = "%@ ne derbasdar e"; +"Scene.Register.Error.Reason.Reserved" = "%@ peyveke mifteya veqetandî ye"; +"Scene.Register.Error.Reason.Taken" = "%@ jixwe tê bikaranîn"; +"Scene.Register.Error.Reason.TooLong" = "%@ gelekî 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.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.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.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.Title" = "%@ ragihîne"; +"Scene.Search.Recommend.Accounts.Description" = "Dibe ku tu bixwazî van hesaban 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.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.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.People" = "Mirov"; +"Scene.Search.Searching.Segment.Posts" = "Şandîyan"; +"Scene.Search.Title" = "Bigere"; +"Scene.ServerPicker.Button.Category.Academia" = "akademî"; +"Scene.ServerPicker.Button.Category.Activism" = "çalakî"; +"Scene.ServerPicker.Button.Category.All" = "Hemû"; +"Scene.ServerPicker.Button.Category.AllAccessiblityDescription" = "Beş: Hemû"; +"Scene.ServerPicker.Button.Category.Art" = "huner"; +"Scene.ServerPicker.Button.Category.Food" = "xwarin"; +"Scene.ServerPicker.Button.Category.Furry" = "furry"; +"Scene.ServerPicker.Button.Category.Games" = "lîsk"; +"Scene.ServerPicker.Button.Category.General" = "giştî"; +"Scene.ServerPicker.Button.Category.Journalism" = "rojnamevanî"; +"Scene.ServerPicker.Button.Category.Lgbt" = "lgbt"; +"Scene.ServerPicker.Button.Category.Music" = "muzîk"; +"Scene.ServerPicker.Button.Category.Regional" = "herêmî"; +"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.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.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.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.Notifications.Favorites" = "Şandiyên min hez kir"; +"Scene.Settings.Section.Notifications.Follows" = "Min şopand"; +"Scene.Settings.Section.Notifications.Mentions" = "Qale min kir"; +"Scene.Settings.Section.Notifications.Title" = "Agahdarî"; +"Scene.Settings.Section.Notifications.Trigger.Anyone" = "her kes"; +"Scene.Settings.Section.Notifications.Trigger.Follow" = "her kesê ku dişopînim"; +"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.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.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.Welcome.Slogan" = "Torên civakî +di destên te de."; +"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard"; +"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"; \ No newline at end of file diff --git a/Mastodon/Resources/ku-TR.lproj/Localizable.stringsdict b/Mastodon/Resources/ku-TR.lproj/Localizable.stringsdict new file mode 100644 index 000000000..064b8bf2b --- /dev/null +++ b/Mastodon/Resources/ku-TR.lproj/Localizable.stringsdict @@ -0,0 +1,390 @@ + + + + + a11y.plural.count.unread.notification + + NSStringLocalizedFormatKey + %#@notification_count_unread_notification@ + notification_count_unread_notification + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 agahdariya nexwendî + other + %ld agahdariyên nexwendî + + + a11y.plural.count.input_limit_exceeds + + NSStringLocalizedFormatKey + Sînorê têketinê derbas kir %#@character_count@ + character_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 tîp + other + %ld tîp + + + a11y.plural.count.input_limit_remains + + NSStringLocalizedFormatKey + Sînorê têketinê %#@character_count@ maye + character_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 tîp + other + %ld tîp + + + plural.count.metric_formatted.post + + NSStringLocalizedFormatKey + %@ %#@post_count@ + post_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + şandî + other + şandî + + + plural.count.post + + NSStringLocalizedFormatKey + %#@post_count@ + post_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 şandî + other + %ld şandî + + + plural.count.favorite + + NSStringLocalizedFormatKey + %#@favorite_count@ + favorite_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 hezkirin + other + %ld hezkirin + + + plural.count.reblog + + NSStringLocalizedFormatKey + %#@reblog_count@ + reblog_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 reblog + other + %ld reblogs + + + plural.count.vote + + NSStringLocalizedFormatKey + %#@vote_count@ + vote_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 deng + other + %ld deng + + + plural.count.voter + + NSStringLocalizedFormatKey + %#@voter_count@ + voter_count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 hilbijêr + other + %ld hilbijêr + + + plural.people_talking + + NSStringLocalizedFormatKey + %#@count_people_talking@ + count_people_talking + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 mirov diaxive + other + %ld mirov diaxive + + + plural.count.following + + NSStringLocalizedFormatKey + %#@count_following@ + count_following + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 dişopîne + other + %ld dişopîne + + + plural.count.follower + + NSStringLocalizedFormatKey + %#@count_follower@ + count_follower + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 şopîner + other + %ld şopîner + + + date.year.left + + NSStringLocalizedFormatKey + %#@count_year_left@ + count_year_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 sal berê + other + %ld sal berê + + + date.month.left + + NSStringLocalizedFormatKey + %#@count_month_left@ + count_month_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 meh berê + other + %ld meh berê + + + date.day.left + + NSStringLocalizedFormatKey + %#@count_day_left@ + count_day_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 roj berê + other + %ld roj berê + + + date.hour.left + + NSStringLocalizedFormatKey + %#@count_hour_left@ + count_hour_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 demjimêr berê + other + %ld demjimêr berê + + + date.minute.left + + NSStringLocalizedFormatKey + %#@count_minute_left@ + count_minute_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 xulek berê + other + %ld xulek berê + + + date.second.left + + NSStringLocalizedFormatKey + %#@count_second_left@ + count_second_left + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 çirke berê + other + %ld çirke berê + + + date.year.ago.abbr + + NSStringLocalizedFormatKey + %#@count_year_ago_abbr@ + count_year_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 sal berê + other + %ld sal berê + + + date.month.ago.abbr + + NSStringLocalizedFormatKey + %#@count_month_ago_abbr@ + count_month_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 xulek berê + other + %ld xulek berê + + + date.day.ago.abbr + + NSStringLocalizedFormatKey + %#@count_day_ago_abbr@ + count_day_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 roj berê + other + %ld roj berê + + + date.hour.ago.abbr + + NSStringLocalizedFormatKey + %#@count_hour_ago_abbr@ + count_hour_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 demjimêr berê + other + %ld demjimêr berê + + + date.minute.ago.abbr + + NSStringLocalizedFormatKey + %#@count_minute_ago_abbr@ + count_minute_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 xulek berê + other + %ld xulek berê + + + date.second.ago.abbr + + NSStringLocalizedFormatKey + %#@count_second_ago_abbr@ + count_second_ago_abbr + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + one + 1 çirke berê + other + %ld çirke berê + + + + diff --git a/MastodonIntent/ku-TR.lproj/Intents.strings b/MastodonIntent/ku-TR.lproj/Intents.strings new file mode 100644 index 000000000..3e1c69fc3 --- /dev/null +++ b/MastodonIntent/ku-TR.lproj/Intents.strings @@ -0,0 +1,51 @@ +"16wxgf" = "Di Mastodon de biweşîne"; + +"751xkl" = "Naveroka nivîsê"; + +"CsR7G2" = "Di Mastodon de biweşîne"; + +"HZSGTr" = "Kîjan naverok bila bê şandin?"; + +"HdGikU" = "Şandin têkçû"; + +"KDNTJ4" = "Sedema têkçûnê"; + +"RHxKOw" = "Bi naveroka nivîsî şandiyan bişîne"; + +"RxSqsb" = "Şandî"; + +"WCIR3D" = "${content} biweşîne di Mastodon de"; + +"ZKJSNu" = "Şandî"; + +"ZS1XaK" = "${content}"; + +"ZbSjzC" = "Xuyanî"; + +"Zo4jgJ" = "Xuyaniya şandiyê"; + +"apSxMG-dYQ5NN" = "Vebijarkên ${count} hene ku li gorî 'Giştî' ne."; + +"apSxMG-ehFLjY" = "Vebijarkên ${count} hene ku li gorî 'Tenê Şopandin' hene."; + +"ayoYEb-dYQ5NN" = "${content}, Giştî"; + +"ayoYEb-ehFLjY" = "${content}, Tenê şopînêr"; + +"dUyuGg" = "Li ser Mastodon bişînin"; + +"dYQ5NN" = "Gelemperî"; + +"ehFLjY" = "Tenê şopîneran"; + +"gfePDu" = "Weşandin bi ser neket. ${failureReason}"; + +"k7dbKQ" = "Şandî bi serkeftî hate şandin."; + +"oGiqmY-dYQ5NN" = "Tenê ji bo pejirandinê, we 'Giştî' dixwest?"; + +"oGiqmY-ehFLjY" = "Tenê ji bo piştrastkirinê, we 'Tenê Şopdarên' dixwest?"; + +"rM6dvp" = "Girêdan"; + +"ryJLwG" = "Bi serkeftî hat şandin. "; diff --git a/MastodonIntent/ku-TR.lproj/Intents.stringsdict b/MastodonIntent/ku-TR.lproj/Intents.stringsdict new file mode 100644 index 000000000..5a39d5e64 --- /dev/null +++ b/MastodonIntent/ku-TR.lproj/Intents.stringsdict @@ -0,0 +1,54 @@ + + + + + There are ${count} options matching ‘${content}’. - 2 + + NSStringLocalizedFormatKey + There are %#@count_option@ matching ‘${content}’. + count_option + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + %ld + zero + 0 options + one + 1 option + two + 2 options + few + %ld options + many + %ld options + other + %ld options + + + There are ${count} options matching ‘${visibility}’. + + NSStringLocalizedFormatKey + There are %#@count_option@ matching ‘${visibility}’. + count_option + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + %ld + zero + 0 options + one + 1 option + two + 2 options + few + %ld options + many + %ld options + other + %ld options + + + + From d66dfccad0a253201b5861f08a1396240f550a05 Mon Sep 17 00:00:00 2001 From: CMK Date: Fri, 29 Oct 2021 19:38:21 +0800 Subject: [PATCH 91/95] chore: update to version 1.2.0 (81) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index cf50d3ac4..9da0855b7 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index cf50d3ac4..9da0855b7 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index cf50d3ac4..9da0855b7 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index cc98e398b..1e5021a31 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4811,7 +4811,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4840,7 +4840,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4948,11 +4948,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4979,11 +4979,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5008,11 +5008,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5038,11 +5038,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5105,7 +5105,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5130,7 +5130,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5155,7 +5155,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5180,7 +5180,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5205,7 +5205,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5230,7 +5230,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5255,7 +5255,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5280,7 +5280,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5371,7 +5371,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5438,11 +5438,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5487,7 +5487,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5512,11 +5512,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5608,7 +5608,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5675,11 +5675,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5724,7 +5724,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5749,11 +5749,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 80; + DYLIB_CURRENT_VERSION = 81; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5779,7 +5779,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5803,7 +5803,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 80; + CURRENT_PROJECT_VERSION = 81; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index e0afdf7fc..1e9186391 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 36 + 43 CoreDataStack.xcscheme_^#shared#^_ orderHint - 35 + 42 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 40 + 41 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 38 + 44 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index 1c89f12a2..dc3e86199 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 80 + 81 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 062a4bf1a..6fabe37eb 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index cf50d3ac4..9da0855b7 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index cf50d3ac4..9da0855b7 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index fb52c9dce..43c0dff37 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 8870b320d..73d43d819 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 80 + 81 NSExtension NSExtensionAttributes From f0a570ea0cb6683eaa635515ede8376ec0159019 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 1 Nov 2021 19:54:07 +0800 Subject: [PATCH 92/95] feat: add follower list for user --- Mastodon.xcodeproj/project.pbxproj | 52 +++++ .../xcschemes/xcschememanagement.plist | 8 +- Mastodon/Coordinator/SceneCoordinator.swift | 5 + Mastodon/Diffiable/Item/UserItem.swift | 15 ++ Mastodon/Diffiable/Section/UserSection.swift | 63 ++++++ ...erProviderFacade+UITableViewDelegate.swift | 22 ++ .../UserProvider/UserProviderFacade.swift | 22 ++ .../FollowerListViewController+Provider.swift | 50 +++++ .../Follower/FollowerListViewController.swift | 111 ++++++++++ .../FollowerListViewModel+Diffable.swift | 58 ++++++ .../FollowerListViewModel+State.swift | 196 ++++++++++++++++++ .../Follower/FollowerListViewModel.swift | 53 +++++ .../Header/View/ProfileHeaderView.swift | 19 +- .../View/ProfileStatusDashboardView.swift | 28 ++- .../Scene/Profile/ProfileViewController.swift | 33 ++- .../Timeline/UserTimelineViewController.swift | 1 - .../TimelineFooterTableViewCell.swift | 51 +++++ .../TableviewCell/UserTableViewCell.swift | 131 ++++++++++++ .../APIService/APIService+Follower.swift | 65 ++++++ .../Mastodon+API+Account+FollowRequest.swift | 6 +- .../API/Mastodon+API+Account+Followers.swift | 81 ++++++++ 21 files changed, 1028 insertions(+), 42 deletions(-) create mode 100644 Mastodon/Diffiable/Item/UserItem.swift create mode 100644 Mastodon/Diffiable/Section/UserSection.swift create mode 100644 Mastodon/Protocol/UserProvider/UserProviderFacade+UITableViewDelegate.swift create mode 100644 Mastodon/Scene/Profile/Follower/FollowerListViewController+Provider.swift create mode 100644 Mastodon/Scene/Profile/Follower/FollowerListViewController.swift create mode 100644 Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift create mode 100644 Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift create mode 100644 Mastodon/Scene/Profile/Follower/FollowerListViewModel.swift create mode 100644 Mastodon/Scene/Share/View/TableviewCell/TimelineFooterTableViewCell.swift create mode 100644 Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell.swift create mode 100644 Mastodon/Service/APIService/APIService+Follower.swift create mode 100644 MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Followers.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 1e5021a31..11914fe75 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -333,6 +333,17 @@ DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A06225E905E000CFDF14 /* UIApplication.swift */; }; DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B35172601FA3400DC1E11 /* MastodonAttachmentService.swift */; }; DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift */; }; + DB6B74EF272FB55000C70B6E /* FollowerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74EE272FB55000C70B6E /* FollowerListViewController.swift */; }; + DB6B74F2272FB67600C70B6E /* FollowerListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74F1272FB67600C70B6E /* FollowerListViewModel.swift */; }; + DB6B74F4272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74F3272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift */; }; + DB6B74F6272FBCDB00C70B6E /* FollowerListViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74F5272FBCDB00C70B6E /* FollowerListViewModel+State.swift */; }; + DB6B74F8272FBFB100C70B6E /* FollowerListViewController+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74F7272FBFB100C70B6E /* FollowerListViewController+Provider.swift */; }; + DB6B74FA272FC2B500C70B6E /* APIService+Follower.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */; }; + DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FB272FF55800C70B6E /* UserSection.swift */; }; + DB6B74FE272FF59000C70B6E /* UserItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FD272FF59000C70B6E /* UserItem.swift */; }; + DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */; }; + DB6B75022730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B75012730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift */; }; + DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */; }; DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */; }; DB6D1B3D2636857500ACB481 /* AppearancePreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B3C2636857500ACB481 /* AppearancePreference.swift */; }; DB6D1B44263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */; }; @@ -1132,6 +1143,17 @@ DB68A06225E905E000CFDF14 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; DB6B35172601FA3400DC1E11 /* MastodonAttachmentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonAttachmentService.swift; sourceTree = ""; }; DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentCollectionViewCell.swift; sourceTree = ""; }; + DB6B74EE272FB55000C70B6E /* FollowerListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowerListViewController.swift; sourceTree = ""; }; + DB6B74F1272FB67600C70B6E /* FollowerListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowerListViewModel.swift; sourceTree = ""; }; + DB6B74F3272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowerListViewModel+Diffable.swift"; sourceTree = ""; }; + DB6B74F5272FBCDB00C70B6E /* FollowerListViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowerListViewModel+State.swift"; sourceTree = ""; }; + DB6B74F7272FBFB100C70B6E /* FollowerListViewController+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowerListViewController+Provider.swift"; sourceTree = ""; }; + DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Follower.swift"; sourceTree = ""; }; + DB6B74FB272FF55800C70B6E /* UserSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSection.swift; sourceTree = ""; }; + DB6B74FD272FF59000C70B6E /* UserItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserItem.swift; sourceTree = ""; }; + DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTableViewCell.swift; sourceTree = ""; }; + DB6B75012730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProviderFacade+UITableViewDelegate.swift"; sourceTree = ""; }; + DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineFooterTableViewCell.swift; sourceTree = ""; }; DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error.swift"; sourceTree = ""; }; DB6D1B3C2636857500ACB481 /* AppearancePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePreference.swift; sourceTree = ""; }; DB6D1B43263691CF00ACB481 /* Mastodon+API+Subscriptions+Policy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+API+Subscriptions+Policy.swift"; sourceTree = ""; }; @@ -1866,6 +1888,7 @@ 2D4AD8A126316CD200613EFC /* SelectedAccountSection.swift */, DB6D9F7C26358ED4008423CD /* SettingsSection.swift */, DBA94433265CBB5300C537E1 /* ProfileFieldSection.swift */, + DB6B74FB272FF55800C70B6E /* UserSection.swift */, ); path = Section; sourceTree = ""; @@ -1909,8 +1932,10 @@ 2DA7D04925CA52CB00804E11 /* TimelineBottomLoaderTableViewCell.swift */, 2D32EAAB25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift */, DBE3CDBA261C427900430CC6 /* TimelineHeaderTableViewCell.swift */, + DB6B750327300B4000C70B6E /* TimelineFooterTableViewCell.swift */, DB02CDAA26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift */, DB92CF7125E7BB98002C1017 /* PollOptionTableViewCell.swift */, + DB6B74FF272FF73800C70B6E /* UserTableViewCell.swift */, ); path = TableviewCell; sourceTree = ""; @@ -1919,6 +1944,7 @@ isa = PBXGroup; children = ( 2D7631B225C159F700929FB9 /* Item.swift */, + DB6B74FD272FF59000C70B6E /* UserItem.swift */, 2D198642261BF09500F0B013 /* SearchResultItem.swift */, DB4F097C26A03A5B00D62E92 /* SearchHistoryItem.swift */, 2D4AD8A726316D3500613EFC /* SelectedAccountItem.swift */, @@ -2289,6 +2315,7 @@ 2D34D9D026148D9E0081BFC0 /* APIService+Recommend.swift */, 2D34D9DA261494120081BFC0 /* APIService+Search.swift */, 0F202212261351F5000C64BF /* APIService+HashtagTimeline.swift */, + DB6B74F9272FC2B500C70B6E /* APIService+Follower.swift */, DBCC3B9426157E6E0045B23D /* APIService+Relationship.swift */, 5B24BBE1262DB19100A9381B /* APIService+Report.swift */, DBAE3F932616E28B004B8251 /* APIService+Follow.swift */, @@ -2508,6 +2535,18 @@ path = NavigationController; sourceTree = ""; }; + DB6B74F0272FB55400C70B6E /* Follower */ = { + isa = PBXGroup; + children = ( + DB6B74EE272FB55000C70B6E /* FollowerListViewController.swift */, + DB6B74F7272FBFB100C70B6E /* FollowerListViewController+Provider.swift */, + DB6B74F1272FB67600C70B6E /* FollowerListViewModel.swift */, + DB6B74F3272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift */, + DB6B74F5272FBCDB00C70B6E /* FollowerListViewModel+State.swift */, + ); + path = Follower; + sourceTree = ""; + }; DB6C8C0525F0921200AAA452 /* MastodonSDK */ = { isa = PBXGroup; children = ( @@ -2862,6 +2901,7 @@ DBB525462611ED57002F1F29 /* Header */, DBB5253B2611ECF5002F1F29 /* Timeline */, DBE3CDF1261C6B3100430CC6 /* Favorite */, + DB6B74F0272FB55400C70B6E /* Follower */, DB9D6BFE25E4F5940051B173 /* ProfileViewController.swift */, DBAE3F812615DDA3004B8251 /* ProfileViewController+UserProvider.swift */, DBB5255D2611F07A002F1F29 /* ProfileViewModel.swift */, @@ -2982,6 +3022,7 @@ children = ( DBAE3F672615DD60004B8251 /* UserProvider.swift */, DBAE3F872615DDF4004B8251 /* UserProviderFacade.swift */, + DB6B75012730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift */, ); path = UserProvider; sourceTree = ""; @@ -3965,6 +4006,7 @@ DB75BF1E263C1C1B00EDBF1F /* CustomScheduler.swift in Sources */, 0FAA102725E1126A0017CCDE /* MastodonPickServerViewController.swift in Sources */, DB59F0FE25EF5D96001F1DAB /* StatusProvider+UITableViewDelegate.swift in Sources */, + DB6B74FE272FF59000C70B6E /* UserItem.swift in Sources */, DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */, DBE3CE07261D6A0E00430CC6 /* FavoriteViewModel+Diffable.swift in Sources */, 2D61335825C188A000CAE157 /* APIService+Persist+Status.swift in Sources */, @@ -4094,6 +4136,9 @@ DB9A486C26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift in Sources */, 5D0393902612D259007FE196 /* WebViewController.swift in Sources */, DB4481CC25EE2AFE00BEFB67 /* PollItem.swift in Sources */, + DB6B74FA272FC2B500C70B6E /* APIService+Follower.swift in Sources */, + DB6B74F4272FBAE700C70B6E /* FollowerListViewModel+Diffable.swift in Sources */, + DB6B74F2272FB67600C70B6E /* FollowerListViewModel.swift in Sources */, DB44767B260B3B8C00B66B82 /* CustomEmojiPickerInputView.swift in Sources */, 0F20222D261457EE000C64BF /* HashtagTimelineViewModel+LoadOldestState.swift in Sources */, DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */, @@ -4106,6 +4151,7 @@ DB35FC1F2612F1D9006193C9 /* ProfileRelationshipActionButton.swift in Sources */, DBC7A672260C897100E57475 /* StatusContentWarningEditorView.swift in Sources */, DB3667A6268AE2620027D07F /* ComposeStatusPollSection.swift in Sources */, + DB6B750427300B4000C70B6E /* TimelineFooterTableViewCell.swift in Sources */, DB59F10E25EF724F001F1DAB /* APIService+Poll.swift in Sources */, DB852D1F26FB037800FC9D81 /* SidebarViewModel.swift in Sources */, DB47229725F9EFAD00DA7F53 /* NSManagedObjectContext.swift in Sources */, @@ -4122,6 +4168,7 @@ DBF156E42702DB3F00EC00B7 /* HandleTapAction.swift in Sources */, DB023295267F0AB800031745 /* ASMetaEditableTextNode.swift in Sources */, 2D5981A125E4A593000FB903 /* MastodonConfirmEmailViewModel.swift in Sources */, + DB6B74F6272FBCDB00C70B6E /* FollowerListViewModel+State.swift in Sources */, DB4F096C269EFA2000D62E92 /* SearchResultViewController+StatusProvider.swift in Sources */, DB87D4452609BE0500D12C0D /* ComposeStatusPollOptionCollectionViewCell.swift in Sources */, DB9F58EF26EF491E00E7BBE9 /* AccountListViewModel.swift in Sources */, @@ -4139,6 +4186,7 @@ DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */, DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */, DBB525642612C988002F1F29 /* MeProfileViewModel.swift in Sources */, + DB6B74EF272FB55000C70B6E /* FollowerListViewController.swift in Sources */, 5BB04FE9262EFC300043BFF6 /* ReportedStatusTableviewCell.swift in Sources */, DBAE3F822615DDA3004B8251 /* ProfileViewController+UserProvider.swift in Sources */, DBBC24C426A544B900398BB9 /* Theme.swift in Sources */, @@ -4180,6 +4228,7 @@ DB49A63D25FF609300B98345 /* PlayerContainerView+MediaTypeIndicotorView.swift in Sources */, DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */, DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */, + DB6B75022730060700C70B6E /* UserProviderFacade+UITableViewDelegate.swift in Sources */, DB44384F25E8C1FA008912A2 /* CALayer.swift in Sources */, 2D34D9CB261489930081BFC0 /* SearchViewController+Recommend.swift in Sources */, DB71C7CB271D5A0300BE3819 /* LineChartView.swift in Sources */, @@ -4245,6 +4294,7 @@ DBE54AC62636C89F004E7C0B /* NotificationPreference.swift in Sources */, 2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */, DB98339C25C96DE600AD9700 /* APIService+Account.swift in Sources */, + DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */, 2DF75BA725D10E1000694EC8 /* APIService+Favorite.swift in Sources */, DB9D6C3825E508BE0051B173 /* Attachment.swift in Sources */, 5DFC35DF262068D20045711D /* SearchViewController+Follow.swift in Sources */, @@ -4261,6 +4311,7 @@ 0F202227261411BB000C64BF /* HashtagTimelineViewController+Provider.swift in Sources */, 2D7631A825C1535600929FB9 /* StatusTableViewCell.swift in Sources */, 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */, + DB6B7500272FF73800C70B6E /* UserTableViewCell.swift in Sources */, DB1D842E26552C4D000346B3 /* StatusTableViewControllerNavigateable.swift in Sources */, DB938F1F2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift in Sources */, 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */, @@ -4291,6 +4342,7 @@ DB647C5726F1E97300F7F82C /* MainTabBarController+Wizard.swift in Sources */, DB6F5E38264E994A009108F4 /* AutoCompleteTopChevronView.swift in Sources */, DBB525412611ED54002F1F29 /* ProfileHeaderViewController.swift in Sources */, + DB6B74F8272FBFB100C70B6E /* FollowerListViewController+Provider.swift in Sources */, DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */, DB4932B326F2054200EF46D4 /* CircleAvatarButton.swift in Sources */, 0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 1e9186391..05869c6aa 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 43 + 35 CoreDataStack.xcscheme_^#shared#^_ orderHint - 42 + 39 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 41 + 36 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 44 + 37 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 10d6fb84f..cda20255b 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -178,6 +178,7 @@ extension SceneCoordinator { case accountList case profile(viewModel: ProfileViewModel) case favorite(viewModel: FavoriteViewModel) + case follower(viewModel: FollowerListViewModel) // setting case settings(viewModel: SettingsViewModel) @@ -424,6 +425,10 @@ private extension SceneCoordinator { let _viewController = FavoriteViewController() _viewController.viewModel = viewModel viewController = _viewController + case .follower(let viewModel): + let _viewController = FollowerListViewController() + _viewController.viewModel = viewModel + viewController = _viewController case .suggestionAccount(let viewModel): let _viewController = SuggestionAccountViewController() _viewController.viewModel = viewModel diff --git a/Mastodon/Diffiable/Item/UserItem.swift b/Mastodon/Diffiable/Item/UserItem.swift new file mode 100644 index 000000000..6f3c591b1 --- /dev/null +++ b/Mastodon/Diffiable/Item/UserItem.swift @@ -0,0 +1,15 @@ +// +// UserItem.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import Foundation +import CoreData + +enum UserItem: Hashable { + case follower(objectID: NSManagedObjectID) + case bottomLoader + case bottomHeader(text: String) +} diff --git a/Mastodon/Diffiable/Section/UserSection.swift b/Mastodon/Diffiable/Section/UserSection.swift new file mode 100644 index 000000000..58e80c6e3 --- /dev/null +++ b/Mastodon/Diffiable/Section/UserSection.swift @@ -0,0 +1,63 @@ +// +// UserSection.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import os.log +import UIKit +import CoreData +import CoreDataStack +import MetaTextKit +import MastodonMeta + +enum UserSection: Hashable { + case main +} + +extension UserSection { + + static let logger = Logger(subsystem: "StatusSection", category: "logic") + + static func tableViewDiffableDataSource( + for tableView: UITableView, + dependency: NeedsDependency, + managedObjectContext: NSManagedObjectContext + ) -> UITableViewDiffableDataSource { + UITableViewDiffableDataSource(tableView: tableView) { [ + weak dependency + ] tableView, indexPath, item -> UITableViewCell? in + guard let dependency = dependency else { return UITableViewCell() } + switch item { + case .follower(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 + configure(cell: cell, user: user) + } + return cell + case .bottomLoader: + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineBottomLoaderTableViewCell.self), for: indexPath) as! TimelineBottomLoaderTableViewCell + cell.startAnimating() + return cell + case .bottomHeader(let text): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TimelineFooterTableViewCell.self), for: indexPath) as! TimelineFooterTableViewCell + cell.messageLabel.text = text + return cell + } // end switch + } // end UITableViewDiffableDataSource + } // end static func tableViewDiffableDataSource { … } + +} + +extension UserSection { + + static func configure( + cell: UserTableViewCell, + user: MastodonUser + ) { + cell.configure(user: user) + } + +} diff --git a/Mastodon/Protocol/UserProvider/UserProviderFacade+UITableViewDelegate.swift b/Mastodon/Protocol/UserProvider/UserProviderFacade+UITableViewDelegate.swift new file mode 100644 index 000000000..a6e3cf215 --- /dev/null +++ b/Mastodon/Protocol/UserProvider/UserProviderFacade+UITableViewDelegate.swift @@ -0,0 +1,22 @@ +// +// UserProviderFacade+UITableViewDelegate.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import Combine +import CoreDataStack +import MastodonSDK +import os.log +import UIKit + +extension UserTableViewCellDelegate where Self: UserProvider { + + func handleTableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let cell = tableView.cellForRow(at: indexPath) else { return } + let user = self.mastodonUser(for: cell) + UserProviderFacade.coordinatorToUserProfileScene(provider: self, user: user) + } + +} diff --git a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift index f85881943..edbe311c7 100644 --- a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift +++ b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift @@ -440,3 +440,25 @@ extension UserProviderFacade { return activityViewController } } + +extension UserProviderFacade { + static func coordinatorToUserProfileScene(provider: UserProvider, user: Future) { + user + .sink { [weak provider] mastodonUser in + guard let provider = provider else { return } + guard let mastodonUser = mastodonUser else { return } + let profileViewModel = CachedProfileViewModel(context: provider.context, mastodonUser: mastodonUser) + DispatchQueue.main.async { + if provider.navigationController == nil { + let from = provider.presentingViewController ?? provider + provider.dismiss(animated: true) { + provider.coordinator.present(scene: .profile(viewModel: profileViewModel), from: from, transition: .show) + } + } else { + provider.coordinator.present(scene: .profile(viewModel: profileViewModel), from: provider, transition: .show) + } + } + } + .store(in: &provider.disposeBag) + } +} diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewController+Provider.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewController+Provider.swift new file mode 100644 index 000000000..627ed7772 --- /dev/null +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewController+Provider.swift @@ -0,0 +1,50 @@ +// +// FollowerListViewController+Provider.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import os.log +import UIKit +import Combine +import CoreData +import CoreDataStack + +extension FollowerListViewController: UserProvider { + + func mastodonUser() -> Future { + Future { promise in + promise(.success(nil)) + } + } + + func mastodonUser(for cell: UITableViewCell?) -> Future { + 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): + managedObjectContext.perform { + let user = managedObjectContext.object(with: objectID) as? MastodonUser + promise(.success(user)) + } + case .bottomLoader, .bottomHeader: + promise(.success(nil)) + } + } + } +} diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift new file mode 100644 index 000000000..428448666 --- /dev/null +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift @@ -0,0 +1,111 @@ +// +// FollowerListViewController.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import os.log +import UIKit +import AVKit +import GameplayKit +import Combine + +final class FollowerListViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { + + var disposeBag = Set() + + weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } + 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)) + 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 FollowerListViewController { + + 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(FollowerListViewModel.State.Reloading.self) + } + .store(in: &disposeBag) + } + +} + +// MARK: - LoadMoreConfigurableTableViewContainer +extension FollowerListViewController: LoadMoreConfigurableTableViewContainer { + typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell + typealias LoadingState = FollowerListViewModel.State.Loading + var loadMoreConfigurableTableView: UITableView { tableView } + var loadMoreConfigurableStateMachine: GKStateMachine { viewModel.stateMachine } +} + +// MARK: - UIScrollViewDelegate +extension FollowerListViewController { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + handleScrollViewDidScroll(scrollView) + } +} + + +// MARK: - UITableViewDelegate +extension FollowerListViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + handleTableView(tableView, didSelectRowAt: indexPath) + } +} + +// MARK: - UserTableViewCellDelegate +extension FollowerListViewController: UserTableViewCellDelegate { } diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift new file mode 100644 index 000000000..90b9cb311 --- /dev/null +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift @@ -0,0 +1,58 @@ +// +// FollowerListViewModel+Diffable.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import UIKit + +extension FollowerListViewModel { + func setupDiffableDataSource( + for tableView: UITableView, + dependency: NeedsDependency + ) { + diffableDataSource = UserSection.tableViewDiffableDataSource( + for: tableView, + dependency: dependency, + managedObjectContext: userFetchedResultsController.fetchedResultsController.managedObjectContext + ) + + // set empty section to make update animation top-to-bottom style + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([.main]) + diffableDataSource?.apply(snapshot) + + // workaround to append loader wrong animation issue + snapshot.appendItems([.bottomLoader], toSection: .main) + diffableDataSource?.apply(snapshot) + + userFetchedResultsController.objectIDs.removeDuplicates() + .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() + snapshot.appendSections([.main]) + let items: [UserItem] = objectIDs.map { + UserItem.follower(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: + break + default: + break + } + } + + diffableDataSource.apply(snapshot) + } + .store(in: &disposeBag) + } +} diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift new file mode 100644 index 000000000..b012a59bb --- /dev/null +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift @@ -0,0 +1,196 @@ +// +// FollowerListViewModel+State.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import os.log +import Foundation +import GameplayKit +import MastodonSDK + +extension FollowerListViewModel { + class State: GKState { + weak var viewModel: FollowerListViewModel? + + init(viewModel: FollowerListViewModel) { + 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 FollowerListViewModel.State { + class Initial: FollowerListViewModel.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: FollowerListViewModel.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: FollowerListViewModel.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: FollowerListViewModel.State { + override func isValidNextState(_ stateClass: AnyClass) -> Bool { + switch stateClass { + case is Reloading.Type, is Loading.Type: + return true + default: + return false + } + } + } + + class Loading: FollowerListViewModel.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.followers( + 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: FollowerListViewModel.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) + 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) + } + } + } +} diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel.swift new file mode 100644 index 000000000..f62441cf1 --- /dev/null +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel.swift @@ -0,0 +1,53 @@ +// +// FollowerListViewModel.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import Foundation +import Combine +import Combine +import CoreData +import CoreDataStack +import GameplayKit +import MastodonSDK + +final class FollowerListViewModel { + + var disposeBag = Set() + + // input + let context: AppContext + let domain: CurrentValueSubject + let userID: CurrentValueSubject + let userFetchedResultsController: UserFetchedResultsController + + // output + var diffableDataSource: UITableViewDiffableDataSource? + 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() + + } +} diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift index 90f2e7a11..016b31a1e 100644 --- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift +++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift @@ -17,9 +17,7 @@ protocol ProfileHeaderViewDelegate: AnyObject { func profileHeaderView(_ profileHeaderView: ProfileHeaderView, relationshipButtonDidPressed button: ProfileRelationshipActionButton) func profileHeaderView(_ profileHeaderView: ProfileHeaderView, metaTextView: MetaTextView, metaDidPressed meta: Meta) - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed followingDashboardMeterView: ProfileStatusDashboardMeterView) - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed followersDashboardMeterView: ProfileStatusDashboardMeterView) + func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView dashboardView: ProfileStatusDashboardView, dashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView, meter: ProfileStatusDashboardView.Meter) } final class ProfileHeaderView: UIView { @@ -443,6 +441,7 @@ extension ProfileHeaderView { bringSubviewToFront(bannerContainerView) bringSubviewToFront(nameContainerStackView) + statusDashboardView.delegate = self bioMetaText.textView.delegate = self bioMetaText.textView.linkDelegate = self @@ -549,19 +548,9 @@ extension ProfileHeaderView: MetaTextViewDelegate { // MARK: - ProfileStatusDashboardViewDelegate extension ProfileHeaderView: ProfileStatusDashboardViewDelegate { - - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) { - delegate?.profileHeaderView(self, profileStatusDashboardView: dashboardView, postDashboardMeterViewDidPressed: dashboardMeterView) + func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, dashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView, meter: ProfileStatusDashboardView.Meter) { + delegate?.profileHeaderView(self, profileStatusDashboardView: dashboardView, dashboardMeterViewDidPressed: dashboardMeterView, meter: meter) } - - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) { - delegate?.profileHeaderView(self, profileStatusDashboardView: dashboardView, followingDashboardMeterViewDidPressed: dashboardMeterView) - } - - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) { - delegate?.profileHeaderView(self, profileStatusDashboardView: dashboardView, followersDashboardMeterViewDidPressed: dashboardMeterView) - } - } // MARK: - AvatarConfigurableView diff --git a/Mastodon/Scene/Profile/Header/View/ProfileStatusDashboardView.swift b/Mastodon/Scene/Profile/Header/View/ProfileStatusDashboardView.swift index 0360421a8..c21703c08 100644 --- a/Mastodon/Scene/Profile/Header/View/ProfileStatusDashboardView.swift +++ b/Mastodon/Scene/Profile/Header/View/ProfileStatusDashboardView.swift @@ -9,9 +9,7 @@ import os.log import UIKit protocol ProfileStatusDashboardViewDelegate: AnyObject { - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) - func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) + func profileStatusDashboardView(_ dashboardView: ProfileStatusDashboardView, dashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView, meter: ProfileStatusDashboardView.Meter) } final class ProfileStatusDashboardView: UIView { @@ -34,6 +32,14 @@ final class ProfileStatusDashboardView: UIView { } +extension ProfileStatusDashboardView { + enum Meter: Hashable { + case post + case following + case follower + } +} + extension ProfileStatusDashboardView { private func _init() { let containerStackView = UIStackView() @@ -67,7 +73,6 @@ extension ProfileStatusDashboardView { tapGestureRecognizer.addTarget(self, action: #selector(ProfileStatusDashboardView.tapGestureRecognizerHandler(_:))) meterView.addGestureRecognizer(tapGestureRecognizer) } - } } @@ -78,12 +83,15 @@ extension ProfileStatusDashboardView { assertionFailure() return } - if sourceView === postDashboardMeterView { - delegate?.profileStatusDashboardView(self, postDashboardMeterViewDidPressed: sourceView) - } else if sourceView === followingDashboardMeterView { - delegate?.profileStatusDashboardView(self, followingDashboardMeterViewDidPressed: sourceView) - } else if sourceView === followersDashboardMeterView { - delegate?.profileStatusDashboardView(self, followersDashboardMeterViewDidPressed: sourceView) + switch sourceView { + case postDashboardMeterView: + delegate?.profileStatusDashboardView(self, dashboardMeterViewDidPressed: sourceView, meter: .post) + case followingDashboardMeterView: + delegate?.profileStatusDashboardView(self, dashboardMeterViewDidPressed: sourceView, meter: .following) + case followersDashboardMeterView: + delegate?.profileStatusDashboardView(self, dashboardMeterViewDidPressed: sourceView, meter: .follower) + default: + assertionFailure() } } } diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index b864fc94c..04d582315 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -769,7 +769,6 @@ extension ProfileViewController: ProfilePagingViewControllerDelegate { // MARK: - ProfileHeaderViewDelegate extension ProfileViewController: ProfileHeaderViewDelegate { - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, avatarImageViewDidPressed imageView: UIImageView) { guard let mastodonUser = viewModel.mastodonUser.value else { return } guard let avatar = imageView.image else { return } @@ -982,15 +981,29 @@ extension ProfileViewController: ProfileHeaderViewDelegate { } } - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, postDashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView) { - - } - - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, followingDashboardMeterViewDidPressed followingDashboardMeterView: ProfileStatusDashboardMeterView) { - - } - - func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView: ProfileStatusDashboardView, followersDashboardMeterViewDidPressed followersDashboardMeterView: ProfileStatusDashboardMeterView) { + func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView dashboardView: ProfileStatusDashboardView, dashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView, meter: ProfileStatusDashboardView.Meter) { + switch meter { + case .post: + // do nothing + break + case .follower: + guard let domain = viewModel.domain.value, + let userID = viewModel.userID.value + else { return } + let followerListViewModel = FollowerListViewModel( + context: context, + domain: domain, + userID: userID + ) + coordinator.present( + scene: .follower(viewModel: followerListViewModel), + from: self, + transition: .show + ) + case .following: + // TODO: + break + } } } diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift index f3803da01..42e9376cf 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift @@ -12,7 +12,6 @@ import Combine import CoreDataStack import GameplayKit -// TODO: adopt MediaPreviewableViewController final class UserTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } diff --git a/Mastodon/Scene/Share/View/TableviewCell/TimelineFooterTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/TimelineFooterTableViewCell.swift new file mode 100644 index 000000000..43dd2c6fa --- /dev/null +++ b/Mastodon/Scene/Share/View/TableviewCell/TimelineFooterTableViewCell.swift @@ -0,0 +1,51 @@ +// +// TimelineFooterTableViewCell.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import UIKit + +final class TimelineFooterTableViewCell: UITableViewCell { + + let messageLabel: UILabel = { + let label = UILabel() + label.font = .systemFont(ofSize: 17) + label.textAlignment = .center + label.textColor = Asset.Colors.Label.secondary.color + label.text = "info" + label.numberOfLines = 0 + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension TimelineFooterTableViewCell { + + private func _init() { + selectionStyle = .none + backgroundColor = .clear + + messageLabel.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(messageLabel) + NSLayoutConstraint.activate([ + messageLabel.topAnchor.constraint(equalTo: contentView.topAnchor), + messageLabel.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + messageLabel.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + messageLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + messageLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 68).priority(.required - 1), // same height to bottom loader + ]) + } + +} diff --git a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell.swift new file mode 100644 index 000000000..29e28415e --- /dev/null +++ b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell.swift @@ -0,0 +1,131 @@ +// +// UserTableViewCell.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import CoreData +import CoreDataStack +import MastodonSDK +import UIKit +import MetaTextKit +import MastodonMeta +import FLAnimatedImage + +protocol UserTableViewCellDelegate: AnyObject { } + +final class UserTableViewCell: UITableViewCell { + + weak var delegate: UserTableViewCellDelegate? + + let avatarImageView: AvatarImageView = { + let imageView = AvatarImageView() + imageView.tintColor = Asset.Colors.Label.primary.color + imageView.layer.cornerRadius = 4 + imageView.clipsToBounds = true + return imageView + }() + + let nameLabel = MetaLabel(style: .statusName) + + let usernameLabel: UILabel = { + let label = UILabel() + label.textColor = Asset.Colors.Label.secondary.color + label.font = .preferredFont(forTextStyle: .body) + return label + }() + + let separatorLine = UIView.separatorLine + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension UserTableViewCell { + + private func _init() { + let containerStackView = UIStackView() + containerStackView.axis = .horizontal + containerStackView.distribution = .fill + containerStackView.spacing = 12 + containerStackView.layoutMargins = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0) + containerStackView.isLayoutMarginsRelativeArrangement = true + containerStackView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(containerStackView) + NSLayoutConstraint.activate([ + containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor), + containerStackView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + containerStackView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + + avatarImageView.translatesAutoresizingMaskIntoConstraints = false + containerStackView.addArrangedSubview(avatarImageView) + NSLayoutConstraint.activate([ + avatarImageView.widthAnchor.constraint(equalToConstant: 42).priority(.required - 1), + avatarImageView.heightAnchor.constraint(equalToConstant: 42).priority(.required - 1), + ]) + + let textStackView = UIStackView() + textStackView.axis = .vertical + textStackView.distribution = .fill + textStackView.translatesAutoresizingMaskIntoConstraints = false + nameLabel.translatesAutoresizingMaskIntoConstraints = false + textStackView.addArrangedSubview(nameLabel) + usernameLabel.translatesAutoresizingMaskIntoConstraints = false + textStackView.addArrangedSubview(usernameLabel) + usernameLabel.setContentHuggingPriority(.defaultLow - 1, for: .vertical) + + containerStackView.addArrangedSubview(textStackView) + + separatorLine.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(separatorLine) + NSLayoutConstraint.activate([ + separatorLine.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + separatorLine.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)), + ]) + + + nameLabel.isUserInteractionEnabled = false + usernameLabel.isUserInteractionEnabled = false + avatarImageView.isUserInteractionEnabled = false + } + +} + +// MARK: - AvatarStackedImageView +extension UserTableViewCell: AvatarConfigurableView { + static var configurableAvatarImageSize: CGSize { CGSize(width: 42, height: 42) } + static var configurableAvatarImageCornerRadius: CGFloat { 4 } + var configurableAvatarImageView: FLAnimatedImageView? { avatarImageView } +} + +extension UserTableViewCell { + func configure(user: MastodonUser) { + // avatar + configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: user.avatarImageURL())) + // name + let name = user.displayNameWithFallback + do { + let mastodonContent = MastodonContent(content: name, emojis: user.emojiMeta) + let metaContent = try MastodonMetaContent.convert(document: mastodonContent) + nameLabel.configure(content: metaContent) + } catch { + let metaContent = PlaintextMetaContent(string: name) + nameLabel.configure(content: metaContent) + } + // username + usernameLabel.text = "@" + user.acct + } +} diff --git a/Mastodon/Service/APIService/APIService+Follower.swift b/Mastodon/Service/APIService/APIService+Follower.swift new file mode 100644 index 000000000..db29a0a29 --- /dev/null +++ b/Mastodon/Service/APIService/APIService+Follower.swift @@ -0,0 +1,65 @@ +// +// APIService+Follower.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import UIKit +import Combine +import CoreData +import CoreDataStack +import CommonOSLog +import MastodonSDK + +extension APIService { + + func followers( + userID: Mastodon.Entity.Account.ID, + maxID: String?, + authorizationBox: MastodonAuthenticationBox + ) -> AnyPublisher, Error> { + let domain = authorizationBox.domain + let authorization = authorizationBox.userAuthorization + let requestMastodonUserID = authorizationBox.userID + + return Mastodon.API.Account.followers( + session: session, + domain: domain, + userID: userID, + authorization: authorization + ) + .flatMap { response -> AnyPublisher, Error> in + let managedObjectContext = self.backgroundManagedObjectContext + return managedObjectContext.performChanges { + let requestMastodonUserRequest = MastodonUser.sortedFetchRequest + requestMastodonUserRequest.predicate = MastodonUser.predicate(domain: domain, id: requestMastodonUserID) + requestMastodonUserRequest.fetchLimit = 1 + guard let requestMastodonUser = managedObjectContext.safeFetch(requestMastodonUserRequest).first else { return } + + for entity in response.value { + _ = APIService.CoreData.createOrMergeMastodonUser( + into: managedObjectContext, + for: requestMastodonUser, + in: domain, + entity: entity, + userCache: nil, + networkDate: response.networkDate, + log: .api + ) + } + } + .tryMap { result -> Mastodon.Response.Content<[Mastodon.Entity.Account]> in + switch result { + case .success: + return response + case .failure(let error): + throw error + } + } + .eraseToAnyPublisher() + } + .eraseToAnyPublisher() + } + +} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+FollowRequest.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+FollowRequest.swift index 87c879ea0..7adbcdeff 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+FollowRequest.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+FollowRequest.swift @@ -12,13 +12,15 @@ import Combine extension Mastodon.API.Account { static func acceptFollowRequestEndpointURL(domain: String, userID: Mastodon.Entity.Account.ID) -> URL { - return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("follow_requests") + return Mastodon.API.endpointURL(domain: domain) + .appendingPathComponent("follow_requests") .appendingPathComponent(userID) .appendingPathComponent("authorize") } static func rejectFollowRequestEndpointURL(domain: String, userID: Mastodon.Entity.Account.ID) -> URL { - return Mastodon.API.endpointURL(domain: domain).appendingPathComponent("follow_requests") + return Mastodon.API.endpointURL(domain: domain) + .appendingPathComponent("follow_requests") .appendingPathComponent(userID) .appendingPathComponent("reject") } diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Followers.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Followers.swift new file mode 100644 index 000000000..b09a5f07b --- /dev/null +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+Account+Followers.swift @@ -0,0 +1,81 @@ +// +// Mastodon+API+Account+Followers.swift +// +// +// Created by Cirno MainasuK on 2021-11-1. +// + +import Foundation +import Combine + +extension Mastodon.API.Account { + + static func followersEndpointURL(domain: String, userID: Mastodon.Entity.Account.ID) -> URL { + return Mastodon.API.endpointURL(domain: domain) + .appendingPathComponent("accounts") + .appendingPathComponent(userID) + .appendingPathComponent("followers") + } + + /// Followers + /// + /// Accounts which follow the given account, if network is not hidden by the account owner. + /// + /// - Since: 0.0.0 + /// - Version: 3.4.1 + /// # Reference + /// [Document](https://docs.joinmastodon.org/methods/accounts/) + /// - Parameters: + /// - session: `URLSession` + /// - domain: Mastodon instance domain. e.g. "example.com" + /// - userID: ID of the account in the database + /// - authorization: User token + /// - Returns: `AnyPublisher` contains `[Account]` nested in the response + public static func followers( + session: URLSession, + domain: String, + userID: Mastodon.Entity.Account.ID, + authorization: Mastodon.API.OAuth.Authorization + ) -> AnyPublisher, Error> { + let request = Mastodon.API.get( + url: followersEndpointURL(domain: domain, userID: userID), + query: nil, + authorization: authorization + ) + return session.dataTaskPublisher(for: request) + .tryMap { data, response in + let value = try Mastodon.API.decode(type: [Mastodon.Entity.Account].self, from: data, response: response) + return Mastodon.Response.Content(value: value, response: response) + } + .eraseToAnyPublisher() + } + + public struct FollowerQuery: Codable, GetQuery { + + public let maxID: String? + public let limit: Int? // default 40 + + enum CodingKeys: String, CodingKey { + case maxID = "max_id" + case limit + } + + public init( + maxID: String?, + limit: Int? + ) { + self.maxID = maxID + self.limit = limit + } + + var queryItems: [URLQueryItem]? { + var items: [URLQueryItem] = [] + maxID.flatMap { items.append(URLQueryItem(name: "max_id", value: $0)) } + limit.flatMap { items.append(URLQueryItem(name: "limit", value: String($0))) } + guard !items.isEmpty else { return nil } + return items + } + + } + +} From 14236c27b8c58791e2cfc34d8ecbc2ed66aa22af Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 1 Nov 2021 20:09:30 +0800 Subject: [PATCH 93/95] feat: coordinator sidebar search tab to trigger searching --- .../Root/ContentSplitViewController.swift | 19 ++++----- .../Scene/Root/RootSplitViewController.swift | 39 ++++++++++++++++++- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift index 577f8d6a4..850b1429f 100644 --- a/Mastodon/Scene/Root/ContentSplitViewController.swift +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -10,6 +10,10 @@ import UIKit import Combine import CoreDataStack +protocol ContentSplitViewControllerDelegate: AnyObject { + func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) +} + final class ContentSplitViewController: UIViewController, NeedsDependency { var disposeBag = Set() @@ -19,6 +23,8 @@ final class ContentSplitViewController: UIViewController, NeedsDependency { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } + weak var delegate: ContentSplitViewControllerDelegate? + private(set) lazy var sidebarViewController: SidebarViewController = { let sidebarViewController = SidebarViewController() sidebarViewController.context = context @@ -87,18 +93,7 @@ extension ContentSplitViewController { extension ContentSplitViewController: SidebarViewControllerDelegate { func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) { - guard let _ = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { - assertionFailure() - return - } - let previousTab = currentSupplementaryTab - currentSupplementaryTab = tab - - if previousTab == tab, - let navigationController = mainTabBarController.selectedViewController as? UINavigationController - { - navigationController.popToRootViewController(animated: true) - } + delegate?.contentSplitViewController(self, sidebarViewController: sidebarViewController, didSelectTab: tab) } func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView) { diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 22354751d..7c03287f1 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -23,6 +23,7 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { let contentSplitViewController = ContentSplitViewController() contentSplitViewController.context = context contentSplitViewController.coordinator = coordinator + contentSplitViewController.delegate = self return contentSplitViewController }() @@ -131,6 +132,36 @@ extension RootSplitViewController { } +// MARK: - ContentSplitViewControllerDelegate +extension RootSplitViewController: ContentSplitViewControllerDelegate { + func contentSplitViewController(_ contentSplitViewController: ContentSplitViewController, sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab) { + guard let _ = MainTabBarController.Tab.allCases.firstIndex(of: tab) else { + assertionFailure() + return + } + switch tab { + case .search: + guard let navigationController = searchViewController.navigationController else { return } + if navigationController.viewControllers.count == 1 { + searchViewController.searchBarTapPublisher.send() + } else { + navigationController.popToRootViewController(animated: true) + } + + default: + let previousTab = contentSplitViewController.currentSupplementaryTab + contentSplitViewController.currentSupplementaryTab = tab + + if previousTab == tab, + let navigationController = contentSplitViewController.mainTabBarController.selectedViewController as? UINavigationController + { + navigationController.popToRootViewController(animated: true) + } + + } + } +} + // MARK: - UISplitViewControllerDelegate extension RootSplitViewController: UISplitViewControllerDelegate { @@ -180,7 +211,13 @@ extension RootSplitViewController: UISplitViewControllerDelegate { } RootSplitViewController.transform(from: compactMainTabBarViewController, to: contentSplitViewController.mainTabBarController) - contentSplitViewController.currentSupplementaryTab = compactMainTabBarViewController.currentTab.value + + let tab = compactMainTabBarViewController.currentTab.value + if tab == .search { + contentSplitViewController.currentSupplementaryTab = .home + } else { + contentSplitViewController.currentSupplementaryTab = compactMainTabBarViewController.currentTab.value + } return proposedDisplayMode } From 0eba513d5c57e44b29e0123bd42dcdcb2c292f7e Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 1 Nov 2021 20:11:18 +0800 Subject: [PATCH 94/95] chore: update version to 1.2.0 (82) --- AppShared/Info.plist | 2 +- CoreDataStack/Info.plist | 2 +- CoreDataStackTests/Info.plist | 2 +- Mastodon.xcodeproj/project.pbxproj | 64 +++++++++---------- .../xcschemes/xcschememanagement.plist | 8 +-- Mastodon/Info.plist | 2 +- MastodonIntent/Info.plist | 2 +- MastodonTests/Info.plist | 2 +- MastodonUITests/Info.plist | 2 +- NotificationService/Info.plist | 2 +- ShareActionExtension/Info.plist | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/AppShared/Info.plist b/AppShared/Info.plist index 9da0855b7..16c084cec 100644 --- a/AppShared/Info.plist +++ b/AppShared/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 diff --git a/CoreDataStack/Info.plist b/CoreDataStack/Info.plist index 9da0855b7..16c084cec 100644 --- a/CoreDataStack/Info.plist +++ b/CoreDataStack/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 diff --git a/CoreDataStackTests/Info.plist b/CoreDataStackTests/Info.plist index 9da0855b7..16c084cec 100644 --- a/CoreDataStackTests/Info.plist +++ b/CoreDataStackTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 11914fe75..60f0f5d74 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -4863,7 +4863,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -4892,7 +4892,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5000,11 +5000,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5031,11 +5031,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5060,11 +5060,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5090,11 +5090,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5157,7 +5157,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5182,7 +5182,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5207,7 +5207,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5232,7 +5232,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = MastodonIntent/MastodonIntent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = MastodonIntent/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5257,7 +5257,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5282,7 +5282,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5307,7 +5307,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5332,7 +5332,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = ShareActionExtension/ShareActionExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = ShareActionExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5423,7 +5423,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5490,11 +5490,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5539,7 +5539,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5564,11 +5564,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5660,7 +5660,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Mastodon/Mastodon.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets"; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = Mastodon/Info.plist; @@ -5727,11 +5727,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = CoreDataStack/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5776,7 +5776,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5801,11 +5801,11 @@ APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5Z4GVSS33P; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 81; + DYLIB_CURRENT_VERSION = 82; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = AppShared/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -5831,7 +5831,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -5855,7 +5855,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 81; + CURRENT_PROJECT_VERSION = 82; DEVELOPMENT_TEAM = 5Z4GVSS33P; INFOPLIST_FILE = NotificationService/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 05869c6aa..cb88c3960 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 35 + 42 CoreDataStack.xcscheme_^#shared#^_ orderHint - 39 + 43 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -97,7 +97,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 36 + 44 MastodonIntents.xcscheme_^#shared#^_ @@ -117,7 +117,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 37 + 41 SuppressBuildableAutocreation diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index dc3e86199..a75982e39 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -30,7 +30,7 @@ CFBundleVersion - 81 + 82 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 6fabe37eb..ed243297d 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 NSExtension NSExtensionAttributes diff --git a/MastodonTests/Info.plist b/MastodonTests/Info.plist index 9da0855b7..16c084cec 100644 --- a/MastodonTests/Info.plist +++ b/MastodonTests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 diff --git a/MastodonUITests/Info.plist b/MastodonUITests/Info.plist index 9da0855b7..16c084cec 100644 --- a/MastodonUITests/Info.plist +++ b/MastodonUITests/Info.plist @@ -17,6 +17,6 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 diff --git a/NotificationService/Info.plist b/NotificationService/Info.plist index 43c0dff37..89e562534 100644 --- a/NotificationService/Info.plist +++ b/NotificationService/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 NSExtension NSExtensionPointIdentifier diff --git a/ShareActionExtension/Info.plist b/ShareActionExtension/Info.plist index 73d43d819..79ba82cef 100644 --- a/ShareActionExtension/Info.plist +++ b/ShareActionExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.2.0 CFBundleVersion - 81 + 82 NSExtension NSExtensionAttributes From 86d475fe5658fd37592ddad045d2c3e4b29dc148 Mon Sep 17 00:00:00 2001 From: CMK Date: Mon, 1 Nov 2021 20:14:26 +0800 Subject: [PATCH 95/95] feat: add i18n string for follower/following list footer --- Localization/app.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Localization/app.json b/Localization/app.json index 3d0e36d03..5c01ae7e0 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -414,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": {