From 3ac78c82c9efac38986ca1699aad1f7654192440 Mon Sep 17 00:00:00 2001 From: Conny Duck Date: Mon, 8 May 2023 19:24:57 +0200 Subject: [PATCH 001/177] don't crash on unexpected json responses --- .../tusky/components/accountlist/AccountListFragment.kt | 2 +- .../tusky/components/timeline/util/TimelineUtils.kt | 3 ++- .../timeline/viewmodel/CachedTimelineRemoteMediator.kt | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt index 552744586..15f36f268 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt @@ -313,7 +313,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct val linkHeader = response.headers()["Link"] onFetchAccountsSuccess(accountList, linkHeader) - } catch (exception: IOException) { + } catch (exception: Exception) { onFetchAccountsFailure(exception) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt index c8d95fd81..4a1d75f92 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt @@ -1,9 +1,10 @@ package com.keylesspalace.tusky.components.timeline.util +import com.google.gson.JsonParseException import retrofit2.HttpException import java.io.IOException -fun Throwable.isExpected() = this is IOException || this is HttpException +fun Throwable.isExpected() = this is IOException || this is HttpException || this is JsonParseException inline fun ifExpected( t: Throwable, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt index 89afefecd..2746b1acf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt @@ -15,6 +15,7 @@ package com.keylesspalace.tusky.components.timeline.viewmodel +import android.util.Log import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingState @@ -117,6 +118,7 @@ class CachedTimelineRemoteMediator( return MediatorResult.Success(endOfPaginationReached = statuses.isEmpty()) } catch (e: Exception) { return ifExpected(e) { + Log.w(TAG, "Failed to load timeline", e) MediatorResult.Error(e) } } @@ -175,4 +177,8 @@ class CachedTimelineRemoteMediator( } return overlappedStatuses } + + companion object { + private const val TAG = "CachedTimelineRM" + } } From b18193e6c3eee776471c79e021ba673419df85d1 Mon Sep 17 00:00:00 2001 From: Conny Duck Date: Mon, 8 May 2023 19:27:00 +0200 Subject: [PATCH 002/177] add logging to NetworkTimelineRemoteMediator --- .../timeline/viewmodel/NetworkTimelineRemoteMediator.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineRemoteMediator.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineRemoteMediator.kt index 98da15bea..40b475e06 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineRemoteMediator.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineRemoteMediator.kt @@ -15,6 +15,7 @@ package com.keylesspalace.tusky.components.timeline.viewmodel +import android.util.Log import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingState @@ -106,8 +107,13 @@ class NetworkTimelineRemoteMediator( return MediatorResult.Success(endOfPaginationReached = statuses.isEmpty()) } catch (e: Exception) { return ifExpected(e) { + Log.w(TAG, "Failed to load timeline", e) MediatorResult.Error(e) } } } + + companion object { + private const val TAG = "NetworkTimelineRM" + } } From 28e1ec8f4bdde7f85f274adf3122fe2e5249e044 Mon Sep 17 00:00:00 2001 From: "Gera, Zoltan" Date: Tue, 4 Jul 2023 18:35:55 +0000 Subject: [PATCH 003/177] Translated using Weblate (Hungarian) Currently translated at 100.0% (29 of 29 strings) Translation: Tusky/Tusky description Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/hu/ --- .../metadata/android/hu/changelogs/103.txt | 18 +++++++++++++++++ .../metadata/android/hu/changelogs/104.txt | 10 ++++++++++ .../metadata/android/hu/changelogs/105.txt | 11 ++++++++++ .../metadata/android/hu/changelogs/106.txt | 5 +++++ .../metadata/android/hu/changelogs/107.txt | 6 ++++++ .../metadata/android/hu/changelogs/108.txt | 5 +++++ .../metadata/android/hu/changelogs/109.txt | 10 ++++++++++ .../metadata/android/hu/changelogs/110.txt | 20 +++++++++++++++++++ .../metadata/android/hu/changelogs/111.txt | 15 ++++++++++++++ 9 files changed, 100 insertions(+) create mode 100644 fastlane/metadata/android/hu/changelogs/103.txt create mode 100644 fastlane/metadata/android/hu/changelogs/104.txt create mode 100644 fastlane/metadata/android/hu/changelogs/105.txt create mode 100644 fastlane/metadata/android/hu/changelogs/106.txt create mode 100644 fastlane/metadata/android/hu/changelogs/107.txt create mode 100644 fastlane/metadata/android/hu/changelogs/108.txt create mode 100644 fastlane/metadata/android/hu/changelogs/109.txt create mode 100644 fastlane/metadata/android/hu/changelogs/110.txt create mode 100644 fastlane/metadata/android/hu/changelogs/111.txt diff --git a/fastlane/metadata/android/hu/changelogs/103.txt b/fastlane/metadata/android/hu/changelogs/103.txt new file mode 100644 index 000000000..8496a0517 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/103.txt @@ -0,0 +1,18 @@ +Tusky 22.0 beta 1 + +Új funkciók: + +- Trendi hashtag-ek megtekintése +- Képleírás és fókuszpont szerkesztése +- "Frissítés" menü az elérhetőségért +- Mastodon v4 szűrők támogatása +- Részletes különbségek mutatása, amikor egy bejegyzést szerkesztettek +- Opció bejegyzés-statisztikák idővonalon való megjelenítésére + +Javítások: + +- Lejátszó vezérlőinek mutatása hangvisszajátszás közben +- Bejegyzés hosszkalkuláció javítása +- Mindig közzétesszük a képfeliratokat + +és még sok más diff --git a/fastlane/metadata/android/hu/changelogs/104.txt b/fastlane/metadata/android/hu/changelogs/104.txt new file mode 100644 index 000000000..fe9867925 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/104.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 2 + +Javítások: + +- Javított értesítés-betöltési sebesség +- Újra mutatjuk a 0/1/1+ a válaszoknál +- Szűrőcímek mutatása, nem a kulcsszavaké a szűrt bejegyzéseken +- Hibajavítás: egy bejegyzés megnyitása megnyithatott egy nem kapcsolódó hivatkozást +- Mutassuk a "Hozzáadás" a megfelelő helyen, ha nincsenek szűrők +- Különböző programleállások javítása diff --git a/fastlane/metadata/android/hu/changelogs/105.txt b/fastlane/metadata/android/hu/changelogs/105.txt new file mode 100644 index 000000000..4457628bf --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/105.txt @@ -0,0 +1,11 @@ +Tusky 22.0 beta 3 + +Javítások: + +- Szálak nézegetése közbeni programleállás javítása +- Mastodon szűrők feldolgozása közbeni programleállás javítása +- Profilokon a követési hivatkozások kattinthatóak +- Androidos értesítés-kezelési frissítések + - Android értesítés a Mastodonról csak egyszer mutatandó + - Android értesítések Mastodon értesítési típus (követés, említés, megtolás, stb) szerint vannak csoportosítva + - Lehetséges értesítés-elhagyás megoldva diff --git a/fastlane/metadata/android/hu/changelogs/106.txt b/fastlane/metadata/android/hu/changelogs/106.txt new file mode 100644 index 000000000..25cf2ab07 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/106.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 4 + +Javítások: + +- Több fiók használata esetén az értesítések újra történő lekérésének megoldása diff --git a/fastlane/metadata/android/hu/changelogs/107.txt b/fastlane/metadata/android/hu/changelogs/107.txt new file mode 100644 index 000000000..fd151c7e0 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/107.txt @@ -0,0 +1,6 @@ +Tusky 22.0 beta 5 + +Javítások: + +- APNG könytár vissza a régire a törött animált emojik javításáért +- Értesítésjelző helyi mentése arra az esetre, ha a kiszolgáló nem támogatja az API-t diff --git a/fastlane/metadata/android/hu/changelogs/108.txt b/fastlane/metadata/android/hu/changelogs/108.txt new file mode 100644 index 000000000..c88d7f640 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/108.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 6 + +Javítások: + +- Értesítési fülön az olvasási pozíció gyakoribb mentése diff --git a/fastlane/metadata/android/hu/changelogs/109.txt b/fastlane/metadata/android/hu/changelogs/109.txt new file mode 100644 index 000000000..277fbd6bb --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/109.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 7 + +Javítások: + + +### Jelentős hibák + +- Minden fontos Mastodon értesítés lekérése, amikor Android értesítéseket hozunk létre +- A "Üzenetírás" kattintása rossz fiókra vitt, megoldva +- Annak biztosítása, hogy az "utolsó olvasott értesítés azonosítója" a helyes fiókba mentődjön diff --git a/fastlane/metadata/android/hu/changelogs/110.txt b/fastlane/metadata/android/hu/changelogs/110.txt new file mode 100644 index 000000000..60db53acb --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/110.txt @@ -0,0 +1,20 @@ +Tusky 22.0 + +Új funkciók: + +- Felkapott hashtag-ek mutatása +- Új hashtag-ek bekövetése +- Jobb sorrendezés nyelvek választásakor +- Bejegyzésverziók közötti különbségek mutatása +- Mastodon v4 szűrők támogatása +- Lehetőség bejegyzés-statisztikák mutatására az idővonalon +- És sok más... + +Javítások: + +- Kiválasztott fül és pozíció megjegyzése +- Értesítések megtartása az elolvasásig +- Kevert balról jobbra és jobbról balra szöveg helyes mutatása a profilon +- Bejegyzéshossz kiszámításának javítása +- Képfeliratok publikálása minden esetben +- És sok más.. diff --git a/fastlane/metadata/android/hu/changelogs/111.txt b/fastlane/metadata/android/hu/changelogs/111.txt new file mode 100644 index 000000000..47aa24dc8 --- /dev/null +++ b/fastlane/metadata/android/hu/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +Új funkciók: + +- Új beállítás UI-szöveg nagyítására + +Javítások: + +- Fiókinfók helyes mentése +- "leküldési" értesítések Android 11-nél régebbi eszközön +- Androidos hibajavítás, ahol a szövegmezők "elfelejtik", hogy tudnak szöveget másolni +- A "különbségek" mutatása a szerkesztési történetben nem túl nagy +- Összeomlás elkerülése, ha a kiszolgáló nem ismeri a bejegyzés történetét +- "Törlés" gomb hozzáadása, amikor szűrőt szerkesztünk +- Nem kocka emojik helyes megjelenítése From 24b23251d82280028683d75093e69c40ee754c2f Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Tue, 4 Jul 2023 18:35:55 +0000 Subject: [PATCH 004/177] Translated using Weblate (Persian) Currently translated at 100.0% (29 of 29 strings) Translation: Tusky/Tusky description Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/fa/ --- fastlane/metadata/android/fa/changelogs/111.txt | 15 +++++++++++++++ fastlane/metadata/android/fa/changelogs/94.txt | 14 +++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 fastlane/metadata/android/fa/changelogs/111.txt diff --git a/fastlane/metadata/android/fa/changelogs/111.txt b/fastlane/metadata/android/fa/changelogs/111.txt new file mode 100644 index 000000000..4b708c31a --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/111.txt @@ -0,0 +1,15 @@ +تاسکی ۲۳٫۰ بتا + +ویژگی‌های جدید: + +- ترجیح جدید برای مقیاس متن میانای کاربری + +رفع اشکال‌ها: + +- ذخیرهٔ درست اطّلاعات حساب +- «گرفتن» آگاهی‌ها روی افزاره‌های با نگارش اندروید کم‌تر از ۱۱ +- دور زدن مشکل امکان فراموشی توانایی رونوشت و جای‌گذاری در زمینه‌های متنی اندروید +- تجاوز نکردن از لیهٔ صفحه هنگام دیدن «تفاوت‌ها» در تاریخچهٔ ویرایش +- فرونپاشیدن در صورت نبودن تاریخچهٔ ویرایش فرسته روی کارساز +- افزودن دکمهٔ «حذف» هنگام ویرایش یک پالایه +- نمایش درست اموجی‌های نامربّعی diff --git a/fastlane/metadata/android/fa/changelogs/94.txt b/fastlane/metadata/android/fa/changelogs/94.txt index 5a9697372..75c4e6e47 100644 --- a/fastlane/metadata/android/fa/changelogs/94.txt +++ b/fastlane/metadata/android/fa/changelogs/94.txt @@ -1,9 +1,9 @@ تاسکی ۱۹٫۰ -- Support for Unified Push. To activate the support you will have to relogin into your accounts. -- The number of responses to a post is now indicated in timelines. -- Images can now by cropped while composing a post. -- Profiles now show the date when they were created. -- When viewing a list the title is now displayed in the toolbar. -- A lot of bugfixes -- Translation improvements +- پشتیبانی از Unified Push. برای فعّال کردن این پشتیبانی باید دوباره به حساب‌هایتان وارد شوید. +- اکنون تعداد پاسخ‌ها به فرسته‌ها در خط‌های زمانی نشان داده می‌شود. +- اکنون تصویرها می‌توانند هنگام ایجاد فرسته بریده شوند. +- اکنون نمایه‌ها تاریخ ایجادشان را نشان می‌دهند. +- اکنون عنوان سیاهه هنگام دیدنش در نوار ابزار نمایش داده می‌شود. +- یک عالمه رفع اشکال +- بهبودهای ترجمه From 6160ddee4438b296bbab3c6950ce63d36738fcc3 Mon Sep 17 00:00:00 2001 From: Luna Jernberg Date: Tue, 4 Jul 2023 18:35:55 +0000 Subject: [PATCH 005/177] Translated using Weblate (Swedish) Currently translated at 100.0% (29 of 29 strings) Translation: Tusky/Tusky description Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/sv/ --- fastlane/metadata/android/sv/changelogs/111.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 fastlane/metadata/android/sv/changelogs/111.txt diff --git a/fastlane/metadata/android/sv/changelogs/111.txt b/fastlane/metadata/android/sv/changelogs/111.txt new file mode 100644 index 000000000..f1ed7d0ce --- /dev/null +++ b/fastlane/metadata/android/sv/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +Nya funktioner: + +- Ny inställning för att skala UI-text + +Fixar: + +- Sparar kontoinformation korrekt +- "pull"-meddelanden på enheter som kör Android-versioner <= 11 +- Undvik Android-bugg där textfält kan "glömma" att de kan kopiera/klistra in +- Att visa "diffs" i redigeringshistoriken kommer inte att sträcka sig utanför skärmkanten +- Krascha inte om din server inte har någon inläggsredigeringshistorik +- Lägg till en "Radera"-knapp när du redigerar ett filter +- Visa icke-fyrkantiga emoji korrekt From 2b1d7078100dad1f7c9e009f23d18439407c549c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 08:27:00 +0000 Subject: [PATCH 006/177] Update dependency app.cash.turbine:turbine to v1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 15ffb881c..bf08ddaca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,7 @@ rxkotlin3 = "3.0.1" photoview = "2.3.0" sparkbutton = "4.1.0" truth = "1.1.5" -turbine = "0.13.0" +turbine = "1.0.0" unified-push = "2.1.1" xmlwriter = "1.0.4" From 481bd513a39566a75d881eab8f9dea3bcb88b68d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:15:38 +0000 Subject: [PATCH 007/177] Update coroutines to v1.7.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bf08ddaca..c0d4549c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ androidx-room = "2.5.1" autodispose = "2.1.1" bouncycastle = "1.70" conscrypt = "2.5.2" -coroutines = "1.7.1" +coroutines = "1.7.2" dagger = "2.46.1" diffx = "1.1.1" emoji2 = "1.3.0" From a71ed0a8135c9c305f8f562e012b0141923b95d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 22:22:32 +0000 Subject: [PATCH 008/177] Update plugin ktlint to v11.5.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0d4549c1..768f8bf5f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,7 +60,7 @@ google-ksp = "com.google.devtools.ksp:1.8.22-1.0.11" kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } -ktlint = "org.jlleitschuh.gradle.ktlint:11.4.2" +ktlint = "org.jlleitschuh.gradle.ktlint:11.5.0" [libraries] android-material = { module = "com.google.android.material:material", version.ref = "material" } From 121db1713d7d0099115a5fc2264ee748f04a7d30 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Thu, 6 Jul 2023 19:37:51 +0200 Subject: [PATCH 009/177] Fix lint issues in AppDatabase.java (#3809) --- app/lint-baseline.xml | 278 +++++------------- .../keylesspalace/tusky/db/AppDatabase.java | 13 +- 2 files changed, 80 insertions(+), 211 deletions(-) diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index bf4a0466a..056acea99 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -839,7 +839,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -850,7 +850,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -861,7 +861,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2031,17 +2031,6 @@ column="5"/> - - - - - - - - - - - - - - - - - - - - - - - - @@ -2500,7 +2434,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2511,7 +2445,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2522,7 +2456,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -2533,7 +2467,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2544,7 +2478,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2555,7 +2489,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -2566,7 +2500,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -2577,7 +2511,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2588,7 +2522,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2599,7 +2533,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2610,7 +2544,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2621,7 +2555,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> @@ -2632,7 +2566,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2643,7 +2577,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2654,7 +2588,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> @@ -2665,7 +2599,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2676,7 +2610,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2687,7 +2621,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2698,7 +2632,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -2709,7 +2643,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2720,7 +2654,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2731,7 +2665,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2742,7 +2676,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -2962,7 +2896,7 @@ errorLine2=" ~~~~~~~~~"> @@ -2973,7 +2907,7 @@ errorLine2=" ~~~~~~~~~"> @@ -2984,7 +2918,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -2995,7 +2929,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3006,7 +2940,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3017,7 +2951,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3028,7 +2962,7 @@ errorLine2=" ~~~~~~"> @@ -3039,7 +2973,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~"> @@ -3050,7 +2984,7 @@ errorLine2=" ~~~~~~~~~~~"> @@ -3061,7 +2995,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3072,7 +3006,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3083,7 +3017,7 @@ errorLine2=" ~~~~~~~~~~~~~"> @@ -3094,7 +3028,7 @@ errorLine2=" ~~~~~~"> @@ -3204,7 +3138,7 @@ errorLine2=" ~~~~~~~"> @@ -3215,7 +3149,7 @@ errorLine2=" ~~~~~~~"> @@ -3270,7 +3204,7 @@ errorLine2=" ~~~~~~~"> @@ -3281,7 +3215,7 @@ errorLine2=" ~~~~~~~"> @@ -3292,7 +3226,7 @@ errorLine2=" ~~~~~~~"> @@ -3303,7 +3237,7 @@ errorLine2=" ~~~~~~~"> @@ -3314,7 +3248,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -3325,7 +3259,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -3589,7 +3523,7 @@ errorLine2=" ~~~~~~~~~"> @@ -3600,7 +3534,7 @@ errorLine2=" ~~~~~~~~~"> @@ -4491,7 +4425,7 @@ errorLine2=" ~~~~~~~~~"> @@ -4502,7 +4436,7 @@ errorLine2=" ~~~~~~~~"> @@ -4513,7 +4447,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -4524,7 +4458,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> @@ -4535,7 +4469,7 @@ errorLine2=" ~~~~~~~"> @@ -4546,7 +4480,7 @@ errorLine2=" ~~~~~~~"> @@ -4557,7 +4491,7 @@ errorLine2=" ~~~~~~~"> @@ -4568,7 +4502,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5096,7 +5030,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -5620,7 +5554,7 @@ errorLine2=" ~~~~~~~~"> @@ -5631,7 +5565,7 @@ errorLine2=" ~~~~~~~~"> @@ -6137,7 +6071,7 @@ errorLine2=" ~~~~~~~~"> @@ -6445,7 +6379,7 @@ errorLine2=" ~~~~~~~~~"> @@ -6500,7 +6434,7 @@ errorLine2=" ~~~~~~~~~"> @@ -6746,72 +6680,6 @@ column="63"/> - - - - - - - - - - - - - - - - - - - - - - - - @@ -6830,7 +6698,7 @@ errorLine2=" ~~~~~~"> @@ -6841,7 +6709,7 @@ errorLine2=" ~~~~~~~~"> @@ -6852,7 +6720,7 @@ errorLine2=" ~~~~"> @@ -6863,7 +6731,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> @@ -6874,7 +6742,7 @@ errorLine2=" ~~~~~~~~~~~~"> @@ -6885,7 +6753,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -6896,7 +6764,7 @@ errorLine2=" ~~~~~~~~"> @@ -6907,7 +6775,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~"> diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 86ea7664b..a51273545 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -16,6 +16,7 @@ package com.keylesspalace.tusky.db; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.room.AutoMigration; import androidx.room.Database; import androidx.room.DeleteColumn; @@ -50,11 +51,11 @@ import java.io.File; ) public abstract class AppDatabase extends RoomDatabase { - public abstract AccountDao accountDao(); - public abstract InstanceDao instanceDao(); - public abstract ConversationsDao conversationDao(); - public abstract TimelineDao timelineDao(); - public abstract DraftDao draftDao(); + @NonNull public abstract AccountDao accountDao(); + @NonNull public abstract InstanceDao instanceDao(); + @NonNull public abstract ConversationsDao conversationDao(); + @NonNull public abstract TimelineDao timelineDao(); + @NonNull public abstract DraftDao draftDao(); public static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override @@ -386,7 +387,7 @@ public abstract class AppDatabase extends RoomDatabase { private final File oldDraftDirectory; - public Migration25_26(File oldDraftDirectory) { + public Migration25_26(@Nullable File oldDraftDirectory) { super(25, 26); this.oldDraftDirectory = oldDraftDirectory; } From 3c90f22b84ebbce0a43a6d124c691bd1a4cb15cf Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Thu, 6 Jul 2023 19:57:35 +0200 Subject: [PATCH 010/177] Fix ArrayIndexOutOfBoundsException (#3808) Fixes https://github.com/tuskyapp/Tusky/issues/3807 --- .../keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt index 96774ac0b..a57c0aba9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt @@ -82,11 +82,11 @@ class AccountFieldEditAdapter : RecyclerView.Adapter - fieldData[holder.bindingAdapterPosition].first = newText.toString() + fieldData.getOrNull(holder.bindingAdapterPosition)?.first = newText.toString() } holder.binding.accountFieldValueText.doAfterTextChanged { newText -> - fieldData[holder.bindingAdapterPosition].second = newText.toString() + fieldData.getOrNull(holder.bindingAdapterPosition)?.second = newText.toString() } // Ensure the textview contents are selectable From 47bf86593eacc2d251460bfbc00de06d6f881795 Mon Sep 17 00:00:00 2001 From: Sotolf Flasskjegg Date: Fri, 7 Jul 2023 11:35:54 +0000 Subject: [PATCH 011/177] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (29 of 29 strings) Translation: Tusky/Tusky description Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/nb_NO/ --- .../metadata/android/nb-NO/changelogs/100.txt | 7 +++++++ .../metadata/android/nb-NO/changelogs/103.txt | 18 +++++++++++++++++ .../metadata/android/nb-NO/changelogs/104.txt | 10 ++++++++++ .../metadata/android/nb-NO/changelogs/105.txt | 11 ++++++++++ .../metadata/android/nb-NO/changelogs/106.txt | 5 +++++ .../metadata/android/nb-NO/changelogs/107.txt | 6 ++++++ .../metadata/android/nb-NO/changelogs/108.txt | 5 +++++ .../metadata/android/nb-NO/changelogs/109.txt | 9 +++++++++ .../metadata/android/nb-NO/changelogs/110.txt | 20 +++++++++++++++++++ .../metadata/android/nb-NO/changelogs/111.txt | 15 ++++++++++++++ 10 files changed, 106 insertions(+) create mode 100644 fastlane/metadata/android/nb-NO/changelogs/100.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/103.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/104.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/105.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/106.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/107.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/108.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/109.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/110.txt create mode 100644 fastlane/metadata/android/nb-NO/changelogs/111.txt diff --git a/fastlane/metadata/android/nb-NO/changelogs/100.txt b/fastlane/metadata/android/nb-NO/changelogs/100.txt new file mode 100644 index 000000000..c3ed5c875 --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/100.txt @@ -0,0 +1,7 @@ +Tusky 21.0 + +- Støtte for postendringer +- Ny innstilling for å styre ønsket leseretning +- Større mediaforhåndsvisninger og et nytt overlegg for å indikere media med beskrivelse +- Det er nå mulig å legge til kontoer til lister ifra dere profil +og mye mer diff --git a/fastlane/metadata/android/nb-NO/changelogs/103.txt b/fastlane/metadata/android/nb-NO/changelogs/103.txt new file mode 100644 index 000000000..73b64a8e8 --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/103.txt @@ -0,0 +1,18 @@ +Tusky 22.0 beta 1 + +Funksjoner som + +- Se trendy emneknagger +- Endre bildebeskrivelser og fokuspunkt +- "oppdateringmeny" for tilgjengelighet +- Support Mastodon v4 filtre +- Vis detaljerte endringer når en post har blitt endret +- Mulighet til å vise poststatistikk i tidslinjen + +Fikser som: + +- Vis avspillingskontroll mens lyd blir avspillt +- Riktig postlengdekalkulasjon +- Publisér alltid bildetekster + +og mye mer diff --git a/fastlane/metadata/android/nb-NO/changelogs/104.txt b/fastlane/metadata/android/nb-NO/changelogs/104.txt new file mode 100644 index 000000000..51baf233f --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/104.txt @@ -0,0 +1,10 @@ +Tusky 22.0 beta 2 + +Nye fikser: + +- Forbedret fart for lasting av varler +- 0/1/1+ vises igjen for svar +- Vis filtertitler ikke filterord på filtrede poster +- Fikset en bug som ved åpning av status kunne åpne en urelatert lenke +- Vis "legg til"-knappen på riktig plass når det ikke er noen filtre +- Fikse diverse krasj diff --git a/fastlane/metadata/android/nb-NO/changelogs/105.txt b/fastlane/metadata/android/nb-NO/changelogs/105.txt new file mode 100644 index 000000000..b09a0e3de --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/105.txt @@ -0,0 +1,11 @@ +Tusky 22.0 beta 3 + +Fikser som: + +- Fikset krasj ved visning av en tråd +- Fikset krasj ved behandilg av Mastodonfiltre +- Lenker i beskrivelser av følgere/følgerforespørsler er klikkbare +- Android varselsoppdateringer + - Androidvarsler for mastodonvarsler skal bare bli vist én gang + - Androidvarsler blir gruppert etter mastodonvarselstype (følger, nevnt, delt osv) + - Mulighet for savnede varsler har blitt fjernet. diff --git a/fastlane/metadata/android/nb-NO/changelogs/106.txt b/fastlane/metadata/android/nb-NO/changelogs/106.txt new file mode 100644 index 000000000..60fc5e8d6 --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/106.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 4 + +Fikser: + +- Fikset gjentagene henting av varsler dersom flere kontoer er konfigurért diff --git a/fastlane/metadata/android/nb-NO/changelogs/107.txt b/fastlane/metadata/android/nb-NO/changelogs/107.txt new file mode 100644 index 000000000..3bf8e1365 --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/107.txt @@ -0,0 +1,6 @@ +Tusky 22.0 beta 5 + +Fikser: + +- Tilbakestillt APNG-bibliotek for å fikse ødelagte animerte emoji +- Lagre lokal kopi av varslingsmerker hvis serveren ikke støtter APIet diff --git a/fastlane/metadata/android/nb-NO/changelogs/108.txt b/fastlane/metadata/android/nb-NO/changelogs/108.txt new file mode 100644 index 000000000..ff5e3b53b --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/108.txt @@ -0,0 +1,5 @@ +Tusky 22.0 beta 6 + +Fikser: + +- Lagre leseposisjon i varslingsfanen oftere diff --git a/fastlane/metadata/android/nb-NO/changelogs/109.txt b/fastlane/metadata/android/nb-NO/changelogs/109.txt new file mode 100644 index 000000000..2f7dcc69a --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/109.txt @@ -0,0 +1,9 @@ +Tusky 22.0 beta 7 + +Fikser: + +### Betydningsfulle bugfikser + +- Hent alle forblivende Mastodonvarsler når androidvarsler lages +- Hvis du klikker på "Skriv" fra et varsel ble feil konto valgt +- Sikkerstille at "Sist lest ID" er lagret till rett konto diff --git a/fastlane/metadata/android/nb-NO/changelogs/110.txt b/fastlane/metadata/android/nb-NO/changelogs/110.txt new file mode 100644 index 000000000..cae2bfeb5 --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/110.txt @@ -0,0 +1,20 @@ +Tusky 22.0 + +Nye funksjoner: + +- Vis trendy emneknagger +- Følge nye emneknagger +- Bedre rekkefølge ved valg av språk +- Vis forskjellen mellom versoner av en tut +- Støtter nå mastodon v4 filtre +- Mulighet til å vise poststatistikk i tidslinjen +- Og mere... + +Fikser: + +- Husk valgt fane og posisjon +- Behold varsler til de er lest +- Riktig visning av mikset HTV og VTH tekst i profiler +- Riktig postlengdeberegning +- Publisér alltid bildebeskrivelser +- Og mer. diff --git a/fastlane/metadata/android/nb-NO/changelogs/111.txt b/fastlane/metadata/android/nb-NO/changelogs/111.txt new file mode 100644 index 000000000..dfa4322df --- /dev/null +++ b/fastlane/metadata/android/nb-NO/changelogs/111.txt @@ -0,0 +1,15 @@ +Tusky 23.0 beta 1 + +Nye funksjoner: + +- Ny instilling for å skalére tekst i brukergrenesnittet. + +Fikser: + +- Lagre kontoinformasjon korrekt +- "Pull" varsler på enheter som har androidversjoner <= 11 +- Unnvik androidbug der hvor tekstfelt kunne "glemme" at de kann klippe/lime +- Vise "differ" i endringshistorien blir ikke forlenget over skjermkanten +- Ikke krasj dersom tjeneren din ikke har postendringshistorie +- Legg til en "Slette"-knapp ved endring av et filter +- Vis ikke-firkantede emojier riktig From c0cf5b2f0d33c7f6aa30d4cfedd720cbf8be6ecf Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Fri, 7 Jul 2023 15:10:15 +0200 Subject: [PATCH 012/177] Fix caption dialog context menu background (#3787) Fixes #3770, #3491 --- app/src/main/res/values/styles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bcf041b04..2c1ab1c46 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -98,7 +98,7 @@ - - diff --git a/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestBase.kt b/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestBase.kt index 666d591cd..b99f64f64 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestBase.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestBase.kt @@ -99,7 +99,6 @@ abstract class NotificationsViewModelTestBase { PrefKeys.CONFIRM_REBLOGS to true, PrefKeys.CONFIRM_FAVOURITES to false, PrefKeys.WELLBEING_HIDE_STATS_POSTS to false, - PrefKeys.SHOW_NOTIFICATIONS_FILTER to true, PrefKeys.FAB_HIDE to false ) diff --git a/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestUiState.kt b/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestUiState.kt index e295dda05..942fe717b 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestUiState.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/notifications/NotificationsViewModelTestUiState.kt @@ -35,7 +35,6 @@ class NotificationsViewModelTestUiState : NotificationsViewModelTestBase() { private val initialUiState = UiState( activeFilter = setOf(Notification.Type.FOLLOW), - showFilterOptions = true, showFabWhileScrolling = true ) @@ -64,23 +63,4 @@ class NotificationsViewModelTestUiState : NotificationsViewModelTestBase() { assertThat(expectMostRecentItem().showFabWhileScrolling).isFalse() } } - - @Test - fun `showFilterOptions depends on SHOW_NOTIFICATIONS_FILTER preference`() = runTest { - // Prior - viewModel.uiState.test { - assertThat(expectMostRecentItem().showFilterOptions).isTrue() - } - - // Given - sharedPreferencesMap[PrefKeys.SHOW_NOTIFICATIONS_FILTER] = false - - // When - eventHub.dispatch(PreferenceChangedEvent(PrefKeys.SHOW_NOTIFICATIONS_FILTER)) - - // Then - viewModel.uiState.test { - assertThat(expectMostRecentItem().showFilterOptions).isFalse() - } - } } From 461ec8d7226764a58a7882f286efb687c11ff7bc Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Sat, 19 Aug 2023 17:36:00 +0200 Subject: [PATCH 158/177] Prompt user before leaving edit profile when any field has been modified. --- .../tusky/EditProfileActivity.kt | 48 ++++-- .../tusky/viewmodel/EditProfileViewModel.kt | 138 +++++++++++------- app/src/main/res/values/strings.xml | 2 + 3 files changed, 124 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 190421e69..b734d92a8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -25,7 +25,9 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.ImageView +import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels +import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import androidx.lifecycle.LiveData import androidx.lifecycle.lifecycleScope @@ -46,9 +48,11 @@ import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Success +import com.keylesspalace.tusky.util.await import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewmodel.EditProfileViewModel +import com.keylesspalace.tusky.viewmodel.ProfileData import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt @@ -200,20 +204,37 @@ class EditProfileActivity : BaseActivity(), Injectable { } } } + + val onBackCallback = object : OnBackPressedCallback(enabled = true) { + override fun handleOnBackPressed() { + if (!viewModel.hasUnsavedChanges(gatherProfileData())) finish() + + lifecycleScope.launch { + when(showConfirmationDialog()) { + AlertDialog.BUTTON_POSITIVE -> save() + else -> finish() + } + } + } + } + + onBackPressedDispatcher.addCallback(this, onBackCallback) } override fun onStop() { super.onStop() if (!isFinishing) { - viewModel.updateProfile( - binding.displayNameEditText.text.toString(), - binding.noteEditText.text.toString(), - binding.lockedCheckBox.isChecked, - accountFieldEditAdapter.getFieldData() - ) + viewModel.updateProfile(gatherProfileData()) } } + private fun gatherProfileData() = ProfileData( + displayName = binding.displayNameEditText.text.toString(), + note = binding.noteEditText.text.toString(), + locked = binding.lockedCheckBox.isChecked, + fields = accountFieldEditAdapter.getFieldData(), + ) + private fun observeImage( liveData: LiveData, imageView: ImageView, @@ -287,14 +308,7 @@ class EditProfileActivity : BaseActivity(), Injectable { return super.onOptionsItemSelected(item) } - private fun save() { - viewModel.save( - binding.displayNameEditText.text.toString(), - binding.noteEditText.text.toString(), - binding.lockedCheckBox.isChecked, - accountFieldEditAdapter.getFieldData() - ) - } + private fun save() = viewModel.save(gatherProfileData()) private fun onSaveFailure(msg: String?) { val errorMsg = msg ?: getString(R.string.error_media_upload_sending) @@ -306,4 +320,10 @@ class EditProfileActivity : BaseActivity(), Injectable { Log.w("EditProfileActivity", "failed to pick media", throwable) Snackbar.make(binding.avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show() } + + private suspend fun showConfirmationDialog() = AlertDialog.Builder(this) + .setTitle(getString(R.string.title_edit_profile_save_changes_prompt)) + .setMessage(getString(R.string.message_edit_profile_save_changes_prompt)) + .create() + .await(R.string.action_save, R.string.action_discard) } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index 21a8a01d7..46b16d5ed 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -51,6 +51,17 @@ import javax.inject.Inject private const val HEADER_FILE_NAME = "header.png" private const val AVATAR_FILE_NAME = "avatar.png" + +/** + * Conveniently groups Profile Data users can modify in the UI. + */ +internal data class ProfileData( + val displayName: String, + val note: String, + val locked: Boolean, + val fields: List, +) + class EditProfileViewModel @Inject constructor( private val mastodonApi: MastodonApi, private val eventHub: EventHub, @@ -96,29 +107,73 @@ class EditProfileViewModel @Inject constructor( headerData.value = getHeaderUri() } - fun save(newDisplayName: String, newNote: String, newLocked: Boolean, newFields: List) { + internal fun save(newProfileData: ProfileData) { if (saveData.value is Loading || profileData.value !is Success) { return } saveData.value = Loading() - val displayName = if (oldProfileData?.displayName == newDisplayName) { - null - } else { - newDisplayName.toRequestBody(MultipartBody.FORM) + val encoded = encodeChangedProfileFields(newProfileData) + if (encoded.allFieldsAreNull()) { + // if nothing has changed, there is no need to make a network request + saveData.postValue(Success()) + return } - val note = if (oldProfileData?.source?.note == newNote) { + viewModelScope.launch { + mastodonApi.accountUpdateCredentials( + encoded.displayName, encoded.note, encoded.locked, encoded.avatar, encoded.header, + encoded.field1?.first, encoded.field1?.second, encoded.field2?.first, encoded.field2?.second, encoded.field3?.first, encoded.field3?.second, encoded.field4?.first, encoded.field4?.second + ).fold( + { newProfileData -> + saveData.postValue(Success()) + eventHub.dispatch(ProfileEditedEvent(newProfileData)) + }, + { throwable -> + saveData.postValue(Error(errorMessage = throwable.getServerErrorMessage())) + } + ) + } + } + + // cache activity state for rotation change + internal fun updateProfile(newProfileData: ProfileData) { + if (profileData.value is Success) { + val newProfileSource = profileData.value?.data?.source?.copy(note = newProfileData.note, fields = newProfileData.fields) + val newProfile = profileData.value?.data?.copy( + displayName = newProfileData.displayName, + locked = newProfileData.locked, + source = newProfileSource + ) + + profileData.postValue(Success(newProfile)) + } + } + + internal fun hasUnsavedChanges(newProfileData: ProfileData) : Boolean { + val encoded = encodeChangedProfileFields(newProfileData) + // If all fields are null, there are no changes. + return !encoded.allFieldsAreNull() + } + + private fun encodeChangedProfileFields(newProfileData: ProfileData): EncodedProfileData { + val displayName = if (oldProfileData?.displayName == newProfileData.displayName) { null } else { - newNote.toRequestBody(MultipartBody.FORM) + newProfileData.displayName.toRequestBody(MultipartBody.FORM) } - val locked = if (oldProfileData?.locked == newLocked) { + val note = if (oldProfileData?.source?.note == newProfileData.note) { null } else { - newLocked.toString().toRequestBody(MultipartBody.FORM) + newProfileData.note.toRequestBody(MultipartBody.FORM) + } + + val locked = if (oldProfileData?.locked == newProfileData.locked) { + null + } else { + newProfileData.locked.toString().toRequestBody(MultipartBody.FORM) } val avatar = if (avatarData.value != null) { @@ -136,48 +191,15 @@ class EditProfileViewModel @Inject constructor( } // when one field changed, all have to be sent or they unchanged ones would get overridden - val fieldsUnchanged = oldProfileData?.source?.fields == newFields - val field1 = calculateFieldToUpdate(newFields.getOrNull(0), fieldsUnchanged) - val field2 = calculateFieldToUpdate(newFields.getOrNull(1), fieldsUnchanged) - val field3 = calculateFieldToUpdate(newFields.getOrNull(2), fieldsUnchanged) - val field4 = calculateFieldToUpdate(newFields.getOrNull(3), fieldsUnchanged) + val fieldsUnchanged = oldProfileData?.source?.fields == newProfileData.fields + val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), fieldsUnchanged) + val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), fieldsUnchanged) + val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), fieldsUnchanged) + val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), fieldsUnchanged) - if (displayName == null && note == null && locked == null && avatar == null && header == null && - field1 == null && field2 == null && field3 == null && field4 == null - ) { - /** if nothing has changed, there is no need to make a network request */ - saveData.postValue(Success()) - return - } - - viewModelScope.launch { - mastodonApi.accountUpdateCredentials( - displayName, note, locked, avatar, header, - field1?.first, field1?.second, field2?.first, field2?.second, field3?.first, field3?.second, field4?.first, field4?.second - ).fold( - { newProfileData -> - saveData.postValue(Success()) - eventHub.dispatch(ProfileEditedEvent(newProfileData)) - }, - { throwable -> - saveData.postValue(Error(errorMessage = throwable.getServerErrorMessage())) - } - ) - } - } - - // cache activity state for rotation change - fun updateProfile(newDisplayName: String, newNote: String, newLocked: Boolean, newFields: List) { - if (profileData.value is Success) { - val newProfileSource = profileData.value?.data?.source?.copy(note = newNote, fields = newFields) - val newProfile = profileData.value?.data?.copy( - displayName = newDisplayName, - locked = newLocked, - source = newProfileSource - ) - - profileData.postValue(Success(newProfile)) - } + return EncodedProfileData( + displayName, note, locked, field1, field2, field3, field4, header, avatar + ) } private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair? { @@ -193,4 +215,20 @@ class EditProfileViewModel @Inject constructor( private fun getCacheFileForName(filename: String): File { return File(application.cacheDir, filename) } + + private data class EncodedProfileData( + val displayName: RequestBody?, + val note: RequestBody?, + val locked: RequestBody?, + val field1: Pair?, + val field2: Pair?, + val field3: Pair?, + val field4: Pair?, + val header: MultipartBody.Part?, + val avatar: MultipartBody.Part?, + ) { + fun allFieldsAreNull() = displayName == null && note == null && locked == null + && avatar == null && header == null && field1 == null && field2 == null + && field3 == null && field4 == null + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fdf776200..b43965fbc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -820,4 +820,6 @@ Playback failed: %s Delete filter \'%1$s\'?" Delete + Unsaved Changes + Do you want to save your profile changes? From 634f020ffa9adae79f413c792ec9dcc3fb128a81 Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Sat, 19 Aug 2023 17:57:25 +0200 Subject: [PATCH 159/177] Apply klint recommendations. --- .../com/keylesspalace/tusky/EditProfileActivity.kt | 4 ++-- .../tusky/viewmodel/EditProfileViewModel.kt | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index b734d92a8..2fa1d3ae7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -210,7 +210,7 @@ class EditProfileActivity : BaseActivity(), Injectable { if (!viewModel.hasUnsavedChanges(gatherProfileData())) finish() lifecycleScope.launch { - when(showConfirmationDialog()) { + when (showConfirmationDialog()) { AlertDialog.BUTTON_POSITIVE -> save() else -> finish() } @@ -232,7 +232,7 @@ class EditProfileActivity : BaseActivity(), Injectable { displayName = binding.displayNameEditText.text.toString(), note = binding.noteEditText.text.toString(), locked = binding.lockedCheckBox.isChecked, - fields = accountFieldEditAdapter.getFieldData(), + fields = accountFieldEditAdapter.getFieldData() ) private fun observeImage( diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index 46b16d5ed..aa5ff44c8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -51,7 +51,6 @@ import javax.inject.Inject private const val HEADER_FILE_NAME = "header.png" private const val AVATAR_FILE_NAME = "avatar.png" - /** * Conveniently groups Profile Data users can modify in the UI. */ @@ -59,7 +58,7 @@ internal data class ProfileData( val displayName: String, val note: String, val locked: Boolean, - val fields: List, + val fields: List ) class EditProfileViewModel @Inject constructor( @@ -151,7 +150,7 @@ class EditProfileViewModel @Inject constructor( } } - internal fun hasUnsavedChanges(newProfileData: ProfileData) : Boolean { + internal fun hasUnsavedChanges(newProfileData: ProfileData): Boolean { val encoded = encodeChangedProfileFields(newProfileData) // If all fields are null, there are no changes. return !encoded.allFieldsAreNull() @@ -225,10 +224,10 @@ class EditProfileViewModel @Inject constructor( val field3: Pair?, val field4: Pair?, val header: MultipartBody.Part?, - val avatar: MultipartBody.Part?, + val avatar: MultipartBody.Part? ) { - fun allFieldsAreNull() = displayName == null && note == null && locked == null - && avatar == null && header == null && field1 == null && field2 == null - && field3 == null && field4 == null + fun allFieldsAreNull() = displayName == null && note == null && locked == null && + avatar == null && header == null && field1 == null && field2 == null && + field3 == null && field4 == null } } From 06239bb8a1755f848d872d6a0aa721b87f34899e Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Tue, 22 Aug 2023 12:05:11 +0200 Subject: [PATCH 160/177] Fix synthetic accessor lint error. --- .../tusky/EditProfileActivity.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 2fa1d3ae7..2e7695aba 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -207,7 +207,7 @@ class EditProfileActivity : BaseActivity(), Injectable { val onBackCallback = object : OnBackPressedCallback(enabled = true) { override fun handleOnBackPressed() { - if (!viewModel.hasUnsavedChanges(gatherProfileData())) finish() + if (!viewModel.hasUnsavedChanges(profileData)) finish() lifecycleScope.launch { when (showConfirmationDialog()) { @@ -224,16 +224,17 @@ class EditProfileActivity : BaseActivity(), Injectable { override fun onStop() { super.onStop() if (!isFinishing) { - viewModel.updateProfile(gatherProfileData()) + viewModel.updateProfile(profileData) } } - private fun gatherProfileData() = ProfileData( - displayName = binding.displayNameEditText.text.toString(), - note = binding.noteEditText.text.toString(), - locked = binding.lockedCheckBox.isChecked, - fields = accountFieldEditAdapter.getFieldData() - ) + private val profileData + get() = ProfileData( + displayName = binding.displayNameEditText.text.toString(), + note = binding.noteEditText.text.toString(), + locked = binding.lockedCheckBox.isChecked, + fields = accountFieldEditAdapter.getFieldData()) + private fun observeImage( liveData: LiveData, @@ -308,7 +309,7 @@ class EditProfileActivity : BaseActivity(), Injectable { return super.onOptionsItemSelected(item) } - private fun save() = viewModel.save(gatherProfileData()) + private fun save() = viewModel.save(profileData) private fun onSaveFailure(msg: String?) { val errorMsg = msg ?: getString(R.string.error_media_upload_sending) From c446d510e4c3c26ab315f7bbaa1947b3bb63e0b2 Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Tue, 22 Aug 2023 12:08:13 +0200 Subject: [PATCH 161/177] Fix lint double space. --- .../main/java/com/keylesspalace/tusky/EditProfileActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 2e7695aba..d15d0d1ab 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -233,8 +233,8 @@ class EditProfileActivity : BaseActivity(), Injectable { displayName = binding.displayNameEditText.text.toString(), note = binding.noteEditText.text.toString(), locked = binding.lockedCheckBox.isChecked, - fields = accountFieldEditAdapter.getFieldData()) - + fields = accountFieldEditAdapter.getFieldData() + ) private fun observeImage( liveData: LiveData, From 8edc8d6422cba5d1da3d85b346fd7750766d1438 Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Tue, 22 Aug 2023 12:19:38 +0200 Subject: [PATCH 162/177] Make profileData internal so there's no synthetic accessor required. --- .../main/java/com/keylesspalace/tusky/EditProfileActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index d15d0d1ab..3d02f940d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -228,7 +228,7 @@ class EditProfileActivity : BaseActivity(), Injectable { } } - private val profileData + internal val profileData get() = ProfileData( displayName = binding.displayNameEditText.text.toString(), note = binding.noteEditText.text.toString(), From e56c0cb5a327256427eb7bd544ca196c286ef707 Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Tue, 22 Aug 2023 12:49:33 +0200 Subject: [PATCH 163/177] Avoid synthetic accessors. --- .../tusky/EditProfileActivity.kt | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 3d02f940d..87df58fb7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -206,21 +206,20 @@ class EditProfileActivity : BaseActivity(), Injectable { } val onBackCallback = object : OnBackPressedCallback(enabled = true) { - override fun handleOnBackPressed() { - if (!viewModel.hasUnsavedChanges(profileData)) finish() - - lifecycleScope.launch { - when (showConfirmationDialog()) { - AlertDialog.BUTTON_POSITIVE -> save() - else -> finish() - } - } - } + override fun handleOnBackPressed() = checkForPotentialUnsavedChanges() } onBackPressedDispatcher.addCallback(this, onBackCallback) } + fun checkForPotentialUnsavedChanges() { + if (hasUnsavedChanges()) { + showUnsavedChangesDialog() + } else { + finish() + } + } + override fun onStop() { super.onStop() if (!isFinishing) { @@ -228,7 +227,7 @@ class EditProfileActivity : BaseActivity(), Injectable { } } - internal val profileData + private val profileData get() = ProfileData( displayName = binding.displayNameEditText.text.toString(), note = binding.noteEditText.text.toString(), @@ -322,7 +321,16 @@ class EditProfileActivity : BaseActivity(), Injectable { Snackbar.make(binding.avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show() } - private suspend fun showConfirmationDialog() = AlertDialog.Builder(this) + private fun showUnsavedChangesDialog() = lifecycleScope.launch { + when (launchAlertDialog()) { + AlertDialog.BUTTON_POSITIVE -> save() + else -> finish() + } + } + + private fun hasUnsavedChanges() = viewModel.hasUnsavedChanges(profileData) + + private suspend fun launchAlertDialog() = AlertDialog.Builder(this) .setTitle(getString(R.string.title_edit_profile_save_changes_prompt)) .setMessage(getString(R.string.message_edit_profile_save_changes_prompt)) .create() From f09f464667f74ba37d14907e7d3bc27b57f6ec96 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Tue, 22 Aug 2023 15:52:09 +0200 Subject: [PATCH 164/177] 3486: Rename stuff --- .../tusky/EditProfileActivity.kt | 38 +++++++++---------- .../tusky/viewmodel/EditProfileViewModel.kt | 38 +++++++++---------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 87df58fb7..ea55bd810 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -100,6 +100,14 @@ class EditProfileActivity : BaseActivity(), Injectable { } } + private val currentProfileData + get() = ProfileData( + displayName = binding.displayNameEditText.text.toString(), + note = binding.noteEditText.text.toString(), + locked = binding.lockedCheckBox.isChecked, + fields = accountFieldEditAdapter.getFieldData() + ) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -206,35 +214,25 @@ class EditProfileActivity : BaseActivity(), Injectable { } val onBackCallback = object : OnBackPressedCallback(enabled = true) { - override fun handleOnBackPressed() = checkForPotentialUnsavedChanges() + override fun handleOnBackPressed() { + if (viewModel.hasUnsavedChanges(currentProfileData)) { + showUnsavedChangesDialog() + } else { + finish() + } + } } onBackPressedDispatcher.addCallback(this, onBackCallback) } - fun checkForPotentialUnsavedChanges() { - if (hasUnsavedChanges()) { - showUnsavedChangesDialog() - } else { - finish() - } - } - override fun onStop() { super.onStop() if (!isFinishing) { - viewModel.updateProfile(profileData) + viewModel.updateProfile(currentProfileData) } } - private val profileData - get() = ProfileData( - displayName = binding.displayNameEditText.text.toString(), - note = binding.noteEditText.text.toString(), - locked = binding.lockedCheckBox.isChecked, - fields = accountFieldEditAdapter.getFieldData() - ) - private fun observeImage( liveData: LiveData, imageView: ImageView, @@ -308,7 +306,7 @@ class EditProfileActivity : BaseActivity(), Injectable { return super.onOptionsItemSelected(item) } - private fun save() = viewModel.save(profileData) + private fun save() = viewModel.save(currentProfileData) private fun onSaveFailure(msg: String?) { val errorMsg = msg ?: getString(R.string.error_media_upload_sending) @@ -328,8 +326,6 @@ class EditProfileActivity : BaseActivity(), Injectable { } } - private fun hasUnsavedChanges() = viewModel.hasUnsavedChanges(profileData) - private suspend fun launchAlertDialog() = AlertDialog.Builder(this) .setTitle(getString(R.string.title_edit_profile_save_changes_prompt)) .setMessage(getString(R.string.message_edit_profile_save_changes_prompt)) diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index aa5ff44c8..4a8782d58 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -76,7 +76,7 @@ class EditProfileViewModel @Inject constructor( val instanceData: Flow = instanceInfoRepo::getInstanceInfo.asFlow() .shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1) - private var oldProfileData: Account? = null + private var apiProfileAccount: Account? = null fun obtainProfile() = viewModelScope.launch { if (profileData.value == null || profileData.value is Error) { @@ -84,7 +84,7 @@ class EditProfileViewModel @Inject constructor( mastodonApi.accountVerifyCredentials().fold( { profile -> - oldProfileData = profile + apiProfileAccount = profile profileData.postValue(Success(profile)) }, { @@ -113,21 +113,21 @@ class EditProfileViewModel @Inject constructor( saveData.value = Loading() - val encoded = encodeChangedProfileFields(newProfileData) - if (encoded.allFieldsAreNull()) { - // if nothing has changed, there is no need to make a network request + val diff = getProfileDiff(apiProfileAccount, newProfileData) + if (diff.hasNoChanges()) { + // if nothing has changed, there is no need to make an api call saveData.postValue(Success()) return } viewModelScope.launch { mastodonApi.accountUpdateCredentials( - encoded.displayName, encoded.note, encoded.locked, encoded.avatar, encoded.header, - encoded.field1?.first, encoded.field1?.second, encoded.field2?.first, encoded.field2?.second, encoded.field3?.first, encoded.field3?.second, encoded.field4?.first, encoded.field4?.second + diff.displayName, diff.note, diff.locked, diff.avatar, diff.header, + diff.field1?.first, diff.field1?.second, diff.field2?.first, diff.field2?.second, diff.field3?.first, diff.field3?.second, diff.field4?.first, diff.field4?.second ).fold( - { newProfileData -> + { newAccountData -> saveData.postValue(Success()) - eventHub.dispatch(ProfileEditedEvent(newProfileData)) + eventHub.dispatch(ProfileEditedEvent(newAccountData)) }, { throwable -> saveData.postValue(Error(errorMessage = throwable.getServerErrorMessage())) @@ -151,25 +151,25 @@ class EditProfileViewModel @Inject constructor( } internal fun hasUnsavedChanges(newProfileData: ProfileData): Boolean { - val encoded = encodeChangedProfileFields(newProfileData) + val diff = getProfileDiff(apiProfileAccount, newProfileData) // If all fields are null, there are no changes. - return !encoded.allFieldsAreNull() + return !diff.hasNoChanges() } - private fun encodeChangedProfileFields(newProfileData: ProfileData): EncodedProfileData { - val displayName = if (oldProfileData?.displayName == newProfileData.displayName) { + private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileData): DiffProfileData { + val displayName = if (oldProfileAccount?.displayName == newProfileData.displayName) { null } else { newProfileData.displayName.toRequestBody(MultipartBody.FORM) } - val note = if (oldProfileData?.source?.note == newProfileData.note) { + val note = if (oldProfileAccount?.source?.note == newProfileData.note) { null } else { newProfileData.note.toRequestBody(MultipartBody.FORM) } - val locked = if (oldProfileData?.locked == newProfileData.locked) { + val locked = if (oldProfileAccount?.locked == newProfileData.locked) { null } else { newProfileData.locked.toString().toRequestBody(MultipartBody.FORM) @@ -190,13 +190,13 @@ class EditProfileViewModel @Inject constructor( } // when one field changed, all have to be sent or they unchanged ones would get overridden - val fieldsUnchanged = oldProfileData?.source?.fields == newProfileData.fields + val fieldsUnchanged = oldProfileAccount?.source?.fields == newProfileData.fields val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), fieldsUnchanged) val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), fieldsUnchanged) val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), fieldsUnchanged) val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), fieldsUnchanged) - return EncodedProfileData( + return DiffProfileData( displayName, note, locked, field1, field2, field3, field4, header, avatar ) } @@ -215,7 +215,7 @@ class EditProfileViewModel @Inject constructor( return File(application.cacheDir, filename) } - private data class EncodedProfileData( + private data class DiffProfileData( val displayName: RequestBody?, val note: RequestBody?, val locked: RequestBody?, @@ -226,7 +226,7 @@ class EditProfileViewModel @Inject constructor( val header: MultipartBody.Part?, val avatar: MultipartBody.Part? ) { - fun allFieldsAreNull() = displayName == null && note == null && locked == null && + fun hasNoChanges() = displayName == null && note == null && locked == null && avatar == null && header == null && field1 == null && field2 == null && field3 == null && field4 == null } From ba50ff5686901fab541ae5be74d9025bc372d769 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Tue, 22 Aug 2023 16:12:03 +0200 Subject: [PATCH 165/177] 3486: Separate diff and encoding --- .../tusky/viewmodel/EditProfileViewModel.kt | 89 +++++++++++-------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index 4a8782d58..f3564ad33 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -114,16 +114,37 @@ class EditProfileViewModel @Inject constructor( saveData.value = Loading() val diff = getProfileDiff(apiProfileAccount, newProfileData) - if (diff.hasNoChanges()) { + if (!diff.hasChanges()) { // if nothing has changed, there is no need to make an api call saveData.postValue(Success()) return } viewModelScope.launch { + var avatarFileBody: MultipartBody.Part? = null + diff.avatarFile?.let { + avatarFileBody = MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + } + + var headerFileBody: MultipartBody.Part? = null + diff.headerFile?.let { + headerFileBody = MultipartBody.Part.createFormData("header", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + } + mastodonApi.accountUpdateCredentials( - diff.displayName, diff.note, diff.locked, diff.avatar, diff.header, - diff.field1?.first, diff.field1?.second, diff.field2?.first, diff.field2?.second, diff.field3?.first, diff.field3?.second, diff.field4?.first, diff.field4?.second + diff.displayName?.toRequestBody(MultipartBody.FORM), + diff.note?.toRequestBody(MultipartBody.FORM), + diff.locked?.toString()?.toRequestBody(MultipartBody.FORM), + avatarFileBody, + headerFileBody, + diff.field1?.first?.toRequestBody(MultipartBody.FORM), + diff.field1?.second?.toRequestBody(MultipartBody.FORM), + diff.field2?.first?.toRequestBody(MultipartBody.FORM), + diff.field2?.second?.toRequestBody(MultipartBody.FORM), + diff.field3?.first?.toRequestBody(MultipartBody.FORM), + diff.field3?.second?.toRequestBody(MultipartBody.FORM), + diff.field4?.first?.toRequestBody(MultipartBody.FORM), + diff.field4?.second?.toRequestBody(MultipartBody.FORM), ).fold( { newAccountData -> saveData.postValue(Success()) @@ -152,62 +173,60 @@ class EditProfileViewModel @Inject constructor( internal fun hasUnsavedChanges(newProfileData: ProfileData): Boolean { val diff = getProfileDiff(apiProfileAccount, newProfileData) - // If all fields are null, there are no changes. - return !diff.hasNoChanges() + + return diff.hasChanges() } private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileData): DiffProfileData { val displayName = if (oldProfileAccount?.displayName == newProfileData.displayName) { null } else { - newProfileData.displayName.toRequestBody(MultipartBody.FORM) + newProfileData.displayName } val note = if (oldProfileAccount?.source?.note == newProfileData.note) { null } else { - newProfileData.note.toRequestBody(MultipartBody.FORM) + newProfileData.note } val locked = if (oldProfileAccount?.locked == newProfileData.locked) { null } else { - newProfileData.locked.toString().toRequestBody(MultipartBody.FORM) + newProfileData.locked } - val avatar = if (avatarData.value != null) { - val avatarBody = getCacheFileForName(AVATAR_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull()) - MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), avatarBody) + val avatarFile = if (avatarData.value != null) { + getCacheFileForName(AVATAR_FILE_NAME) } else { null } - val header = if (headerData.value != null) { - val headerBody = getCacheFileForName(HEADER_FILE_NAME).asRequestBody("image/png".toMediaTypeOrNull()) - MultipartBody.Part.createFormData("header", randomAlphanumericString(12), headerBody) + val headerFile = if (headerData.value != null) { + getCacheFileForName(HEADER_FILE_NAME) } else { null } // when one field changed, all have to be sent or they unchanged ones would get overridden - val fieldsUnchanged = oldProfileAccount?.source?.fields == newProfileData.fields - val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), fieldsUnchanged) - val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), fieldsUnchanged) - val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), fieldsUnchanged) - val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), fieldsUnchanged) + val allFieldsUnchanged = oldProfileAccount?.source?.fields == newProfileData.fields + val field1 = calculateFieldToUpdate(newProfileData.fields.getOrNull(0), allFieldsUnchanged) + val field2 = calculateFieldToUpdate(newProfileData.fields.getOrNull(1), allFieldsUnchanged) + val field3 = calculateFieldToUpdate(newProfileData.fields.getOrNull(2), allFieldsUnchanged) + val field4 = calculateFieldToUpdate(newProfileData.fields.getOrNull(3), allFieldsUnchanged) return DiffProfileData( - displayName, note, locked, field1, field2, field3, field4, header, avatar + displayName, note, locked, field1, field2, field3, field4, headerFile, avatarFile ) } - private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair? { + private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair? { if (fieldsUnchanged || newField == null) { return null } return Pair( - newField.name.toRequestBody(MultipartBody.FORM), - newField.value.toRequestBody(MultipartBody.FORM) + newField.name, + newField.value, ) } @@ -216,18 +235,18 @@ class EditProfileViewModel @Inject constructor( } private data class DiffProfileData( - val displayName: RequestBody?, - val note: RequestBody?, - val locked: RequestBody?, - val field1: Pair?, - val field2: Pair?, - val field3: Pair?, - val field4: Pair?, - val header: MultipartBody.Part?, - val avatar: MultipartBody.Part? + val displayName: String?, + val note: String?, + val locked: Boolean?, + val field1: Pair?, + val field2: Pair?, + val field3: Pair?, + val field4: Pair?, + val headerFile: File?, + val avatarFile: File? ) { - fun hasNoChanges() = displayName == null && note == null && locked == null && - avatar == null && header == null && field1 == null && field2 == null && - field3 == null && field4 == null + fun hasChanges() = displayName != null || note != null || locked != null || + avatarFile != null || headerFile != null || field1 != null || field2 != null || + field3 != null || field4 != null } } From f49b1cc7446b867c506bbf6449820ae8623f0441 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Tue, 22 Aug 2023 18:22:45 +0200 Subject: [PATCH 166/177] Fix exception when updating summary notifications (#3976) https://github.com/tuskyapp/Tusky/commit/dc9e9f2aebfd3feb057fee4c3ac72b05cbc75a12 modifed the code that fetched the value of EXTRA_NOTIFICATION_TYPE in an intent, to use getSerializable(). However, the value was being placed in to the intent using putString(). This caused an exception when trying to update the summary notification, so it would never update. ``` java.lang.ClassCastException: java.lang.String cannot be cast to com.keylesspalace.tusky.entity.Notification$Type at com.keylesspalace.tusky.components.notifications.NotificationHelper.updateSummaryNotifications(NotificationHelper.java:321) at com.keylesspalace.tusky.components.notifications.NotificationFetcher.fetchAndShow(NotificationFetcher.kt:87) at com.keylesspalace.tusky.components.notifications.NotificationFetcher$fetchAndShow$1.invokeSuspend(Unknown Source:14) ``` Fix this by placing the value in to the intent using putSerializable(), to match how it will be fetched. --- .../tusky/components/notifications/NotificationHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java index a0c5a8ed5..f15d44d2a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationHelper.java @@ -238,7 +238,7 @@ public class NotificationHelper { Bundle extras = new Bundle(); // Add the sending account's name, so it can be used when summarising this notification extras.putString(EXTRA_ACCOUNT_NAME, body.getAccount().getName()); - extras.putString(EXTRA_NOTIFICATION_TYPE, body.getType().toString()); + extras.putSerializable(EXTRA_NOTIFICATION_TYPE, body.getType()); builder.addExtras(extras); // Only alert for the first notification of a batch to avoid multiple alerts at once From 3a402740031fbde683cc5dccb1fcc6d21c70fec5 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Tue, 22 Aug 2023 21:17:22 +0200 Subject: [PATCH 167/177] 3486: Re-introduce separate check method to not need a synthetic accessor (lint error) --- .../keylesspalace/tusky/EditProfileActivity.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index ea55bd810..a24ccc10a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -214,18 +214,21 @@ class EditProfileActivity : BaseActivity(), Injectable { } val onBackCallback = object : OnBackPressedCallback(enabled = true) { - override fun handleOnBackPressed() { - if (viewModel.hasUnsavedChanges(currentProfileData)) { - showUnsavedChangesDialog() - } else { - finish() - } - } + override fun handleOnBackPressed() = checkForUnsavedChanges() } onBackPressedDispatcher.addCallback(this, onBackCallback) } + fun checkForUnsavedChanges() { + if (viewModel.hasUnsavedChanges(currentProfileData)) { + showUnsavedChangesDialog() + } else { + finish() + } + } + + override fun onStop() { super.onStop() if (!isFinishing) { From dd0cf9c366f08f98e0f0ad46b5b00cc043d22fed Mon Sep 17 00:00:00 2001 From: Weblate <42475313+nailyk-weblate@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:23:07 +0200 Subject: [PATCH 168/177] Translations update from Weblate (#3971) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translations update from [Weblate](https://weblate.tusky.app) for [Tusky/Tusky](https://weblate.tusky.app/projects/tusky/tusky/). Current translation status: ![Weblate translation status](https://weblate.tusky.app/widgets/tusky/-/tusky/horizontal-auto.svg) --------- Co-authored-by: ButterflyOfFire Co-authored-by: puf Co-authored-by: Danial Behzadi Co-authored-by: Oliebol Co-authored-by: Eric Co-authored-by: Hồ Nhất Duy Co-authored-by: XoseM Co-authored-by: Nik Clayton --- app/src/main/res/values-cy/strings.xml | 2 ++ app/src/main/res/values-fa/strings.xml | 2 ++ app/src/main/res/values-fr/strings.xml | 4 ++-- app/src/main/res/values-gl/strings.xml | 25 ++++++++++++++++------ app/src/main/res/values-nl/strings.xml | 8 +++---- app/src/main/res/values-vi/strings.xml | 2 ++ app/src/main/res/values-zh-rCN/strings.xml | 2 ++ 7 files changed, 32 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index eb6c33ba7..647644737 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -731,4 +731,6 @@ Copïwyd fersiwn a gwybodaeth dyfais Cuddio o\'r ffrwd cartref Methodd chwarae: %s + Dileu + Dileu\'r hidlydd \'%1$s\'\? diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 31570ad4f..7d65376bd 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -683,4 +683,6 @@ اطّلاعات افزاره و نگارش رونوشت شد نهفتن از خط زمانی خانگی پخش شکست خورد: %s + حذف + حذف پالایهٔ «%1$s»؟ diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9fa13bffa..22eb4313c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -439,7 +439,7 @@ Ajouter un choix Choix multiples Choix %d - Éditer + Modifier Pouets planifiés Modifier Messages programmés @@ -682,4 +682,4 @@ Ajouter un mot-clé Modifier mot-clé %s : %s - + \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index f74f3a961..2dc51827b 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -262,10 +262,10 @@ Engadir conta á listaxe Atopar persoas ás que segues Eliminar a listaxe - Renomear a listaxe + Actualizar a listaxe Crear unha listaxe Non se puido eliminar a listaxe - Non se puido renomear a listaxe + Non se actualizou a listaxe Non se puido crear a listaxe Listaxes Listaxes @@ -304,10 +304,9 @@ Compartir ligazón ao toot Compartir contido do toot Perfil de Tusky - Informar de fallos e solicitar funcións: -\n https://github.com/tuskyapp/Tusky/issues - Web do proxecto: -\n https://tusky.app + Informar de fallos e solicitar funcións: +\nhttps://github.com/tuskyapp/Tusky/issues + Web do proxecto: https://tusky.app Tusky é software libre e de código aberto. Está baixo a licenza GNU General Public License Version 3. Podes ver a licenza aquí: https://www.gnu.org/licenses/gpl-3.0.en.html Desenvolta por Tusky Tusky %s @@ -666,4 +665,16 @@ Obtendo as notificacións… Mantemento da caché… Fallou a subida: %s - + O teu dispositivo + %s %s +\nVersión de Android: %s +\nVersión SDK: %d + A túa conta + \@%s@%s +\nVersión: %s + Copiouse a información sobre o dispositivo e versión + Copiar a información da versión e o dispositivo + Eliminar + Agochar na cronoloxía de inicio + Eliminar o filtro \'%1$s\'\? + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d883dc95e..bad61b2e9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -259,10 +259,8 @@ Lijsten Berichten plaatsen als %1$s - Omschrijf dit voor iemand met een visuele beperking -\n(tekenlimiet is %d) - Omschrijf dit voor mensen met een visuele beperking -\n(tekenlimiet is %d) + Omschrijf inhoud voor iemand met een visuele beperking (tekenlimiet is %d) + Omschrijf inhoud voor mensen met een visuele beperking (tekenlimiet is %d) Beschrijving toevoegen Verwijderen @@ -663,4 +661,6 @@ Versie en apparaatinformatie gekopieerd De upload is mislukt: %s Contact zoeken met je server duurde te lang + Verwijder + Verwijder filter \'%1$s\'\? diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index ffc70bee0..aeeb84f93 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -665,4 +665,6 @@ Thiết bị của bạn Ẩn khỏi bảng tin Không thể phát: %s + Xóa bộ lọc \'%1$s\'\? + Xóa diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 61aec7198..3729e425f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -679,4 +679,6 @@ 复制版本及设备信息 不出现在首页时间线中 播放失败了:%s + 删除筛选器\'%1$s\'吗? + 删除 From 85888ac23820ae56a925e222514b7c023191e373 Mon Sep 17 00:00:00 2001 From: SpaceFox Date: Wed, 23 Aug 2023 15:04:24 +0200 Subject: [PATCH 169/177] Uses the system theme as default theme (#3813) Set the "System Design" as the default theme. This ensures that the app's initial behaviour respect's the user's system-wide theme choice, while still allowing the user to adjust it later. This is only done for new installs of Tusky. If the user is upgrading from a previous release and they did not have an explicit theme set then the dark theme is used, and the UX does not change. --- .../com/keylesspalace/tusky/BaseActivity.java | 4 +++- .../com/keylesspalace/tusky/TuskyApplication.kt | 15 +++++++++++++-- .../tusky/components/compose/ComposeActivity.kt | 3 ++- .../components/preference/PreferencesActivity.kt | 5 +++-- .../tusky/settings/SettingsConstants.kt | 5 ++++- .../com/keylesspalace/tusky/util/ThemeUtils.kt | 12 ++++++------ 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 6cdc471a0..4c8e35f5e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -56,6 +56,8 @@ import java.util.List; import javax.inject.Inject; +import static com.keylesspalace.tusky.settings.PrefKeys.APP_THEME; + public abstract class BaseActivity extends AppCompatActivity implements Injectable { private static final String TAG = "BaseActivity"; @@ -74,7 +76,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab /* There isn't presently a way to globally change the theme of a whole application at * runtime, just individual activities. So, each activity has to set its theme before any * views are created. */ - String theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT); + String theme = preferences.getString(APP_THEME, ThemeUtils.APP_THEME_DEFAULT); Log.d("activeTheme", theme); if (theme.equals("black")) { setTheme(R.style.TuskyBlackTheme); diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt index 3c943863d..84fbabbaf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt @@ -25,10 +25,13 @@ import androidx.work.WorkManager import autodispose2.AutoDisposePlugins import com.keylesspalace.tusky.components.notifications.NotificationHelper import com.keylesspalace.tusky.di.AppInjector +import com.keylesspalace.tusky.settings.NEW_INSTALL_SCHEMA_VERSION import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.PrefKeys.APP_THEME import com.keylesspalace.tusky.settings.SCHEMA_VERSION import com.keylesspalace.tusky.util.APP_THEME_DEFAULT import com.keylesspalace.tusky.util.LocaleManager +import com.keylesspalace.tusky.util.THEME_NIGHT import com.keylesspalace.tusky.util.setAppNightMode import com.keylesspalace.tusky.worker.PruneCacheWorker import com.keylesspalace.tusky.worker.WorkerFactory @@ -76,7 +79,7 @@ class TuskyApplication : Application(), HasAndroidInjector { AppInjector.init(this) // Migrate shared preference keys and defaults from version to version. - val oldVersion = sharedPreferences.getInt(PrefKeys.SCHEMA_VERSION, 0) + val oldVersion = sharedPreferences.getInt(PrefKeys.SCHEMA_VERSION, NEW_INSTALL_SCHEMA_VERSION) if (oldVersion != SCHEMA_VERSION) { upgradeSharedPreferences(oldVersion, SCHEMA_VERSION) } @@ -87,7 +90,7 @@ class TuskyApplication : Application(), HasAndroidInjector { EmojiPackHelper.init(this, DefaultEmojiPackList.get(this), allowPackImports = false) // init night mode - val theme = sharedPreferences.getString("appTheme", APP_THEME_DEFAULT) + val theme = sharedPreferences.getString(APP_THEME, APP_THEME_DEFAULT) setAppNightMode(theme) localeManager.setLocale() @@ -136,6 +139,14 @@ class TuskyApplication : Application(), HasAndroidInjector { editor.remove(PrefKeys.Deprecated.SHOW_NOTIFICATIONS_FILTER) } + if (oldVersion != NEW_INSTALL_SCHEMA_VERSION && oldVersion < 2023082301) { + // Default value for appTheme is now THEME_SYSTEM. If the user is upgrading and + // didn't have an explicit preference set use the previous default, so the + // theme does not unexpectedly change. + if (!sharedPreferences.contains(APP_THEME)) { + editor.putString(APP_THEME, THEME_NIGHT) + } + } editor.putInt(PrefKeys.SCHEMA_VERSION, newVersion) editor.apply() } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index a2828f3a3..14f9b947e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -94,6 +94,7 @@ import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.PrefKeys.APP_THEME import com.keylesspalace.tusky.util.APP_THEME_DEFAULT import com.keylesspalace.tusky.util.MentionSpan import com.keylesspalace.tusky.util.PickMediaFiles @@ -208,7 +209,7 @@ class ComposeActivity : activeAccount = accountManager.activeAccount ?: return - val theme = preferences.getString("appTheme", APP_THEME_DEFAULT) + val theme = preferences.getString(APP_THEME, APP_THEME_DEFAULT) if (theme == "black") { setTheme(R.style.TuskyDialogActivityBlackTheme) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt index b47df1596..f8464ea02 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt @@ -34,6 +34,7 @@ import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.databinding.ActivityPreferencesBinding import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.PrefKeys.APP_THEME import com.keylesspalace.tusky.util.APP_THEME_DEFAULT import com.keylesspalace.tusky.util.getNonNullString import com.keylesspalace.tusky.util.setAppNightMode @@ -145,8 +146,8 @@ class PreferencesActivity : override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { when (key) { - "appTheme" -> { - val theme = sharedPreferences.getNonNullString("appTheme", APP_THEME_DEFAULT) + APP_THEME -> { + val theme = sharedPreferences.getNonNullString(APP_THEME, APP_THEME_DEFAULT) Log.d("activeTheme", theme) setAppNightMode(theme) diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt index 636c1fc69..49041c1aa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -41,7 +41,10 @@ enum class AppTheme(val value: String) { * * - Adding a new preference that does not change the interpretation of an existing preference */ -const val SCHEMA_VERSION = 2023072401 +const val SCHEMA_VERSION = 2023082301 + +/** The schema version for fresh installs */ +const val NEW_INSTALL_SCHEMA_VERSION = 0 object PrefKeys { // Note: not all of these keys are actually used as SharedPreferences keys but we must give diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.kt index a03a50260..10f8df1a4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.kt @@ -30,12 +30,12 @@ import com.google.android.material.color.MaterialColors * the ability to do so is not supported in resource files. */ -private const val THEME_NIGHT = "night" -private const val THEME_DAY = "day" -private const val THEME_BLACK = "black" -private const val THEME_AUTO = "auto" -private const val THEME_SYSTEM = "auto_system" -const val APP_THEME_DEFAULT = THEME_NIGHT +const val THEME_NIGHT = "night" +const val THEME_DAY = "day" +const val THEME_BLACK = "black" +const val THEME_AUTO = "auto" +const val THEME_SYSTEM = "auto_system" +const val APP_THEME_DEFAULT = THEME_SYSTEM fun getDimension(context: Context, @AttrRes attribute: Int): Int { return context.obtainStyledAttributes(intArrayOf(attribute)).use { array -> From 45d2fa1570e677e15119e129a5cf54c9c03e7c63 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Wed, 23 Aug 2023 15:06:08 +0200 Subject: [PATCH 170/177] 3486: (Appease linter) --- .../java/com/keylesspalace/tusky/EditProfileActivity.kt | 1 - .../tusky/viewmodel/EditProfileViewModel.kt | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index a24ccc10a..f373521ac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -228,7 +228,6 @@ class EditProfileActivity : BaseActivity(), Injectable { } } - override fun onStop() { super.onStop() if (!isFinishing) { diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index f3564ad33..2e1a8d43c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -42,7 +42,6 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody -import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody import java.io.File @@ -123,12 +122,12 @@ class EditProfileViewModel @Inject constructor( viewModelScope.launch { var avatarFileBody: MultipartBody.Part? = null diff.avatarFile?.let { - avatarFileBody = MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + avatarFileBody = MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) } var headerFileBody: MultipartBody.Part? = null diff.headerFile?.let { - headerFileBody = MultipartBody.Part.createFormData("header", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + headerFileBody = MultipartBody.Part.createFormData("header", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) } mastodonApi.accountUpdateCredentials( @@ -144,7 +143,7 @@ class EditProfileViewModel @Inject constructor( diff.field3?.first?.toRequestBody(MultipartBody.FORM), diff.field3?.second?.toRequestBody(MultipartBody.FORM), diff.field4?.first?.toRequestBody(MultipartBody.FORM), - diff.field4?.second?.toRequestBody(MultipartBody.FORM), + diff.field4?.second?.toRequestBody(MultipartBody.FORM) ).fold( { newAccountData -> saveData.postValue(Success()) @@ -226,7 +225,7 @@ class EditProfileViewModel @Inject constructor( } return Pair( newField.name, - newField.value, + newField.value ) } From 0c892cf56f5c762ff56e5a97b3fd28959f043953 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Wed, 23 Aug 2023 22:49:44 +0200 Subject: [PATCH 171/177] Leave comments on PRs with fixes for lint errors (#3224) Runs `ktlintFormat`, and adds comments to the PR if that generates any diffs. The comments include the fix, which can be accepted immediately through the GitHub UI. --- .github/workflows/ktlint.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/ktlint.yml diff --git a/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml new file mode 100644 index 000000000..c7c0e4241 --- /dev/null +++ b/.github/workflows/ktlint.yml @@ -0,0 +1,35 @@ +name: reviewdog-suggester +on: pull_request +jobs: + ktlint: + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' + cache: 'gradle' + + - run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties + + - uses: gradle/wrapper-validation-action@v1 + + - uses: gradle/gradle-build-action@v2 + with: + cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} + + - run: chmod +x ./gradlew + + - run: ./gradlew ktlintFormat + + - uses: reviewdog/action-suggester@v1 + with: + tool_name: ktlintFormat + +permissions: + contents: read + issues: write + pull-requests: write From 387c3989d7339a88086311d35577b3d4634c4bd5 Mon Sep 17 00:00:00 2001 From: Martin Marconcini Date: Thu, 24 Aug 2023 10:06:25 +0200 Subject: [PATCH 172/177] Apply PR suggestions: * Remove dialog title and string * Rename Data Class * Use liveData.value directly when possible --- .../keylesspalace/tusky/EditProfileActivity.kt | 11 +++++------ .../tusky/viewmodel/EditProfileViewModel.kt | 17 +++++++---------- app/src/main/res/values/strings.xml | 3 +-- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index f373521ac..94c160f74 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -52,7 +52,7 @@ import com.keylesspalace.tusky.util.await import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewmodel.EditProfileViewModel -import com.keylesspalace.tusky.viewmodel.ProfileData +import com.keylesspalace.tusky.viewmodel.ProfileDataInUi import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt @@ -101,7 +101,7 @@ class EditProfileActivity : BaseActivity(), Injectable { } private val currentProfileData - get() = ProfileData( + get() = ProfileDataInUi( displayName = binding.displayNameEditText.text.toString(), note = binding.noteEditText.text.toString(), locked = binding.lockedCheckBox.isChecked, @@ -322,15 +322,14 @@ class EditProfileActivity : BaseActivity(), Injectable { } private fun showUnsavedChangesDialog() = lifecycleScope.launch { - when (launchAlertDialog()) { + when (launchSaveDialog()) { AlertDialog.BUTTON_POSITIVE -> save() else -> finish() } } - private suspend fun launchAlertDialog() = AlertDialog.Builder(this) - .setTitle(getString(R.string.title_edit_profile_save_changes_prompt)) - .setMessage(getString(R.string.message_edit_profile_save_changes_prompt)) + private suspend fun launchSaveDialog() = AlertDialog.Builder(this) + .setMessage(getString(R.string.dialog_save_profile_changes_message)) .create() .await(R.string.action_save, R.string.action_discard) } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index 2e1a8d43c..55de04be4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -50,10 +50,7 @@ import javax.inject.Inject private const val HEADER_FILE_NAME = "header.png" private const val AVATAR_FILE_NAME = "avatar.png" -/** - * Conveniently groups Profile Data users can modify in the UI. - */ -internal data class ProfileData( +internal data class ProfileDataInUi( val displayName: String, val note: String, val locked: Boolean, @@ -105,7 +102,7 @@ class EditProfileViewModel @Inject constructor( headerData.value = getHeaderUri() } - internal fun save(newProfileData: ProfileData) { + internal fun save(newProfileData: ProfileDataInUi) { if (saveData.value is Loading || profileData.value !is Success) { return } @@ -115,7 +112,7 @@ class EditProfileViewModel @Inject constructor( val diff = getProfileDiff(apiProfileAccount, newProfileData) if (!diff.hasChanges()) { // if nothing has changed, there is no need to make an api call - saveData.postValue(Success()) + saveData.value = Success() return } @@ -157,7 +154,7 @@ class EditProfileViewModel @Inject constructor( } // cache activity state for rotation change - internal fun updateProfile(newProfileData: ProfileData) { + internal fun updateProfile(newProfileData: ProfileDataInUi) { if (profileData.value is Success) { val newProfileSource = profileData.value?.data?.source?.copy(note = newProfileData.note, fields = newProfileData.fields) val newProfile = profileData.value?.data?.copy( @@ -166,17 +163,17 @@ class EditProfileViewModel @Inject constructor( source = newProfileSource ) - profileData.postValue(Success(newProfile)) + profileData.value = Success(newProfile) } } - internal fun hasUnsavedChanges(newProfileData: ProfileData): Boolean { + internal fun hasUnsavedChanges(newProfileData: ProfileDataInUi): Boolean { val diff = getProfileDiff(apiProfileAccount, newProfileData) return diff.hasChanges() } - private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileData): DiffProfileData { + private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileDataInUi): DiffProfileData { val displayName = if (oldProfileAccount?.displayName == newProfileData.displayName) { null } else { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2233b2dea..a04f6ca5b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -819,6 +819,5 @@ Playback failed: %s Delete filter \'%1$s\'?" Delete - Unsaved Changes - Do you want to save your profile changes? + Do you want to save your profile changes? From f11808816125e77124c9c6e5c4c0c61454cc6f45 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Thu, 24 Aug 2023 13:01:03 +0200 Subject: [PATCH 173/177] Revert "Enable gradle build cache for Bitrise builds" (#3920) Reverts tuskyapp/Tusky#3840. Turns out this needs to be enabled at Bitrise, and it's not on our plan. So every build is running this extra workflow, but it's not providing any benefit, just slowing things down. --- bitrise.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bitrise.yml b/bitrise.yml index 714b3dd89..a7627ac6f 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -18,9 +18,6 @@ workflows: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - git-clone@8.0: {} - cache-pull@2.7: {} - - activate-build-cache-for-gradle: - inputs: - - push: 'true' - install-missing-android-tools: inputs: - gradlew_path: $PROJECT_LOCATION/gradlew @@ -60,7 +57,6 @@ workflows: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - git-clone: {} - cache-pull@2.7: {} - - activate-build-cache-for-gradle: {} - install-missing-android-tools: inputs: - gradlew_path: $PROJECT_LOCATION/gradlew @@ -100,9 +96,6 @@ workflows: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - git-clone: {} - cache-pull@2.7: {} - - activate-build-cache-for-gradle: - inputs: - - push: 'true' - install-missing-android-tools@3.1: inputs: - gradlew_path: $PROJECT_LOCATION/gradlew From 70d86345a87994f2f219594b0ecfe236a27d1f4e Mon Sep 17 00:00:00 2001 From: Lakoja Date: Thu, 24 Aug 2023 15:00:52 +0200 Subject: [PATCH 174/177] 3891: Match (only) title for muting state --- .../main/java/com/keylesspalace/tusky/StatusListActivity.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 9e4e93052..39cd0ad09 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -186,9 +186,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { filters -> mutedFilter = filters.firstOrNull { filter -> // TODO shouldn't this be an exact match (only one keyword; exactly the hashtag)? - filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any { - it.keyword == hashedTag - } + filter.context.contains(Filter.Kind.HOME.kind) && filter.title == hashedTag } updateTagMuteState(mutedFilter != null) }, From 5764efa5d4ad6191bce3eb34b6809720926f05e0 Mon Sep 17 00:00:00 2001 From: Mike Haynes Date: Thu, 24 Aug 2023 07:21:43 -0700 Subject: [PATCH 175/177] Allow the user to add a "bookmarks" tab (#3983) Fixes #2368 --- app/src/main/java/com/keylesspalace/tusky/TabData.kt | 7 +++++++ .../java/com/keylesspalace/tusky/TabPreferenceActivity.kt | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/app/src/main/java/com/keylesspalace/tusky/TabData.kt b/app/src/main/java/com/keylesspalace/tusky/TabData.kt index 58969292f..4760bd5d5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabData.kt @@ -36,6 +36,7 @@ const val DIRECT = "Direct" const val TRENDING_TAGS = "TrendingTags" const val HASHTAG = "Hashtag" const val LIST = "List" +const val BOOKMARKS = "Bookmarks" data class TabData( val id: String, @@ -114,6 +115,12 @@ fun createTabDataFromId(id: String, arguments: List = emptyList()): TabD arguments = arguments, title = { arguments.getOrNull(1).orEmpty() } ) + BOOKMARKS -> TabData( + id = BOOKMARKS, + text = R.string.title_bookmarks, + icon = R.drawable.ic_bookmark_active_24dp, + fragment = { TimelineFragment.newInstance(TimelineViewModel.Kind.BOOKMARKS) } + ) else -> throw IllegalArgumentException("unknown tab type") } } diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt index 5f85f7375..29611074e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt @@ -382,6 +382,10 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene if (!currentTabs.contains(trendingTagsTab)) { addableTabs.add(trendingTagsTab) } + val bookmarksTab = createTabDataFromId(BOOKMARKS) + if (!currentTabs.contains(trendingTagsTab)) { + addableTabs.add(bookmarksTab) + } addableTabs.add(createTabDataFromId(HASHTAG)) addableTabs.add(createTabDataFromId(LIST)) From ee4f6f4b7f2ddf227ccd31dc6886ec253ab81901 Mon Sep 17 00:00:00 2001 From: Conny Duck Date: Wed, 30 Aug 2023 18:49:59 +0200 Subject: [PATCH 176/177] fix links sometimes being underlined --- app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt index 04176a11e..d60c260e4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt @@ -68,7 +68,7 @@ fun setClickableText(view: TextView, content: CharSequence, mentions: List Date: Sun, 3 Sep 2023 11:02:08 -0400 Subject: [PATCH 177/177] Fix link to Matrix in README (#3995) Updated Matrix chat link to correct Matrix Space link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bc9b63bc..62c5e1ded 100644 --- a/README.md +++ b/README.md @@ -32,4 +32,4 @@ If you have any bug reports, feature requests or questions please open an issue We always welcome new contributors! Please read our [contribution guide](https://github.com/tuskyapp/Tusky/blob/develop/CONTRIBUTING.md) to get started. ### Development chatroom -https://riot.im/app/#/room/#Tusky:matrix.org +https://matrix.to/#/#Tusky:matrix.org