diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index e0da3ee56..bb270b80a 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -65,6 +65,10 @@
+
+
+
+
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt
index 61f7e1a31..400c1bb40 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt
@@ -331,7 +331,7 @@ fun EditPostView(
it,
accountViewModel.account.settings.defaultFileServer,
onAdd = { alt, server, sensitiveContent, mediaQuality ->
- postViewModel.upload(alt, sensitiveContent, mediaQuality, false, server, accountViewModel::toast, context)
+ postViewModel.upload(alt, sensitiveContent, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
if (server.type != ServerType.NIP95) {
accountViewModel.account.settings.changeDefaultFileServer(server)
}
@@ -365,7 +365,7 @@ fun EditPostView(
postViewModel.wantsInvoice = false
},
onClose = { postViewModel.wantsInvoice = false },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt
index 833417c6e..4bee43e17 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt
@@ -140,7 +140,7 @@ fun NewMediaView(
PostButton(
onPost = {
- postViewModel.upload(context, relayList, onClose, accountViewModel::toast)
+ postViewModel.upload(context, relayList, onClose, accountViewModel.toastManager::toast)
postViewModel.selectedServer?.let {
if (it.type != ServerType.NIP95) {
account.settings.changeDefaultFileServer(it)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
index 26ee67343..2b3fc3378 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
@@ -198,7 +198,7 @@ fun NewUserMetadataScreen(
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp),
) {
- postViewModel.uploadForPicture(it, context, onError = accountViewModel::toast)
+ postViewModel.uploadForPicture(it, context, onError = accountViewModel.toastManager::toast)
}
},
singleLine = true,
@@ -223,7 +223,7 @@ fun NewUserMetadataScreen(
tint = MaterialTheme.colorScheme.placeholderText,
modifier = Modifier.padding(start = 5.dp),
) {
- postViewModel.uploadForBanner(it, context, onError = accountViewModel::toast)
+ postViewModel.uploadForBanner(it, context, onError = accountViewModel.toastManager::toast)
}
},
singleLine = true,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
index 21330a85a..ed2b4b4ef 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
@@ -218,7 +218,7 @@ fun RelaySelectionDialog(
)
}
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.unable_to_download_relay_document),
msg,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt
index b6255abf8..4cc08ed1c 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt
@@ -103,7 +103,7 @@ fun BasicRelaySetupInfoDialog(
)
}
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.unable_to_download_relay_document),
msg,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt
index 0f850e99f..5a3a1d357 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt
@@ -280,7 +280,7 @@ fun LoadRelayInfo(
)
}
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.unable_to_download_relay_document),
msg,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt
index 4f31f931e..b767d58aa 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt
@@ -105,7 +105,7 @@ fun Kind3RelaySetupInfoProposalDialog(
)
}
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.unable_to_download_relay_document),
msg,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt
index cc962f34e..d5707a366 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt
@@ -64,7 +64,7 @@ fun RelayStatusRow(
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_from_relay,
R.string.read_from_relay_description,
)
@@ -91,7 +91,7 @@ fun RelayStatusRow(
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.write_to_relay,
R.string.write_to_relay_description,
)
@@ -118,7 +118,7 @@ fun RelayStatusRow(
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.errors,
R.string.errors_description,
)
@@ -150,7 +150,7 @@ fun RelayStatusRow(
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.spam,
R.string.spam_description,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
index d50594d3c..3a8303793 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
@@ -116,7 +116,7 @@ fun CashuPreview(
CashuPreviewNew(
it,
accountViewModel::meltCashu,
- accountViewModel::toast,
+ accountViewModel.toastManager::toast,
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt
index e9cacf55c..3f127dba5 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/VideoView.kt
@@ -1235,10 +1235,10 @@ private fun saveMediaToGallery(
mimeType = mimeType,
localContext = localContext,
onSuccess = {
- accountViewModel.toast(R.string.video_saved_to_the_gallery, R.string.video_saved_to_the_gallery)
+ accountViewModel.toastManager.toast(R.string.video_saved_to_the_gallery, R.string.video_saved_to_the_gallery)
},
onError = {
- accountViewModel.toast(R.string.failed_to_save_the_video, null, it)
+ accountViewModel.toastManager.toast(R.string.failed_to_save_the_video, null, it)
},
)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentDialog.kt
index c99dc3cb6..fa770082a 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentDialog.kt
@@ -312,10 +312,10 @@ private fun saveMediaToGallery(
forceProxy = useTor,
localContext,
onSuccess = {
- accountViewModel.toast(success, success)
+ accountViewModel.toastManager.toast(success, success)
},
onError = {
- accountViewModel.toast(failure, null, it)
+ accountViewModel.toastManager.toast(failure, null, it)
},
)
} else if (content is MediaPreloadedContent) {
@@ -325,10 +325,10 @@ private fun saveMediaToGallery(
content.mimeType,
localContext,
onSuccess = {
- accountViewModel.toast(success, success)
+ accountViewModel.toastManager.toast(success, success)
},
onError = { innerIt ->
- accountViewModel.toast(failure, null, innerIt)
+ accountViewModel.toastManager.toast(failure, null, innerIt)
},
)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt
index 0db17f94b..3520fe829 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt
@@ -726,7 +726,7 @@ fun ShareImageAction(
val n19 = Nip19Parser.uriToRoute(postNostrUri)?.entity as? NEvent
if (n19 != null) {
accountViewModel.addMediaToGallery(n19.hex, videoUri, n19.relay.getOrNull(0), blurhash, dim, hash, mimeType) // TODO Whole list or first?
- accountViewModel.toast(R.string.media_added, R.string.media_added_to_profile_gallery)
+ accountViewModel.toastManager.toast(R.string.media_added, R.string.media_added_to_profile_gallery)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/DisplayErrorMessages.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt
similarity index 75%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/DisplayErrorMessages.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt
index 14bb6471f..ac29855cd 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/DisplayErrorMessages.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/DisplayErrorMessages.kt
@@ -18,20 +18,24 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.components
+package com.vitorpamplona.amethyst.ui.components.toasts
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
+import com.vitorpamplona.amethyst.ui.components.toasts.multiline.MultiErrorToastMsg
+import com.vitorpamplona.amethyst.ui.components.toasts.multiline.MultiUserErrorMessageDialog
+import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.ResourceToastMsg
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThrowableToastMsg
import com.vitorpamplona.amethyst.ui.stringRes
@Composable
-fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
- val openDialogMsg = accountViewModel.toasts.collectAsStateWithLifecycle(null)
+fun DisplayErrorMessages(
+ toastManager: ToastManager,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val openDialogMsg = toastManager.toasts.collectAsStateWithLifecycle(null)
openDialogMsg.value?.let { obj ->
when (obj) {
@@ -41,14 +45,14 @@ fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
stringRes(obj.titleResId),
stringRes(obj.resourceId, *obj.params),
) {
- accountViewModel.clearToasts()
+ toastManager.clearToasts()
}
} else {
InformationDialog(
stringRes(obj.titleResId),
stringRes(obj.resourceId),
) {
- accountViewModel.clearToasts()
+ toastManager.clearToasts()
}
}
@@ -57,7 +61,7 @@ fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
obj.title,
obj.msg,
) {
- accountViewModel.clearToasts()
+ toastManager.clearToasts()
}
is ThrowableToastMsg ->
@@ -66,8 +70,10 @@ fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
obj.msg,
obj.throwable,
) {
- accountViewModel.clearToasts()
+ toastManager.clearToasts()
}
+
+ is MultiErrorToastMsg -> MultiUserErrorMessageDialog(obj, accountViewModel, nav)
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ResourceToastMsg.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ResourceToastMsg.kt
new file mode 100644
index 000000000..1ca182da0
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ResourceToastMsg.kt
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts
+
+import androidx.compose.runtime.Immutable
+
+@Immutable
+class ResourceToastMsg(
+ val titleResId: Int,
+ val resourceId: Int,
+ val params: Array? = null,
+) : ToastMsg()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/StringToastMsg.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/StringToastMsg.kt
new file mode 100644
index 000000000..1cd090262
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/StringToastMsg.kt
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts
+
+import androidx.compose.runtime.Immutable
+
+@Immutable
+class StringToastMsg(
+ val title: String,
+ val msg: String,
+) : ToastMsg()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ThrowableToastMsg.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ThrowableToastMsg.kt
new file mode 100644
index 000000000..1ee9f555c
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ThrowableToastMsg.kt
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts
+
+import androidx.compose.runtime.Immutable
+
+@Immutable
+class ThrowableToastMsg(
+ val titleResId: Int,
+ val msg: String? = null,
+ val throwable: Throwable,
+) : ToastMsg()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastManager.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastManager.kt
new file mode 100644
index 000000000..95e1830e1
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastManager.kt
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts
+
+import com.vitorpamplona.amethyst.model.User
+import com.vitorpamplona.amethyst.ui.components.toasts.multiline.MultiErrorToastMsg
+import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class ToastManager {
+ val toasts = MutableStateFlow(null)
+
+ fun clearToasts() {
+ toasts.tryEmit(null)
+ }
+
+ fun toast(
+ title: String,
+ message: String,
+ ) {
+ toasts.tryEmit(StringToastMsg(title, message))
+ }
+
+ fun toast(
+ titleResId: Int,
+ resourceId: Int,
+ ) {
+ toasts.tryEmit(ResourceToastMsg(titleResId, resourceId))
+ }
+
+ fun toast(
+ titleResId: Int,
+ message: String?,
+ throwable: Throwable,
+ ) {
+ toasts.tryEmit(ThrowableToastMsg(titleResId, message, throwable))
+ }
+
+ fun toast(
+ titleResId: Int,
+ resourceId: Int,
+ vararg params: String,
+ ) {
+ toasts.tryEmit(ResourceToastMsg(titleResId, resourceId, params))
+ }
+
+ fun toast(
+ titleResId: Int,
+ message: String,
+ user: User?,
+ ) {
+ val current = toasts.value
+ if (current is MultiErrorToastMsg && current.titleResId == titleResId) {
+ current.add(message, user)
+ } else {
+ toasts.tryEmit(MultiErrorToastMsg(titleResId).also { it.add(message, user) })
+ }
+ }
+
+ fun toast(
+ titleResId: Int,
+ data: UserBasedErrorMessage,
+ ) {
+ val current = toasts.value
+ if (current is MultiErrorToastMsg && current.titleResId == titleResId) {
+ current.add(data)
+ } else {
+ toasts.tryEmit(MultiErrorToastMsg(titleResId).also { it.add(data) })
+ }
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastMsg.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastMsg.kt
new file mode 100644
index 000000000..285ae8af6
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/ToastMsg.kt
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts
+
+import androidx.compose.runtime.Immutable
+
+@Immutable
+open class ToastMsg
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiUserMessageDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/ErrorList.kt
similarity index 65%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiUserMessageDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/ErrorList.kt
index 789f052bc..a822bde13 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiUserMessageDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/ErrorList.kt
@@ -18,11 +18,10 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.note
+package com.vitorpamplona.amethyst.ui.components.toasts.multiline
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -31,56 +30,44 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.text.selection.SelectionContainer
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Done
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
-import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
+import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
-import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
import com.vitorpamplona.amethyst.ui.theme.Size30dp
import com.vitorpamplona.amethyst.ui.theme.Size40dp
import com.vitorpamplona.amethyst.ui.theme.Size5dp
-import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@Composable
@Preview
-fun MultiUserErrorMessageContentPreview() {
+fun ErrorListPreview() {
val accountViewModel = mockAccountViewModel()
- val nav = EmptyNav
var user1: User? = null
var user2: User? = null
@@ -88,107 +75,43 @@ fun MultiUserErrorMessageContentPreview() {
runBlocking {
withContext(Dispatchers.IO) {
- user1 = LocalCache.getOrCreateUser("aaabccaabbccaabbcc")
- user2 = LocalCache.getOrCreateUser("bbbccabbbccabbbcca")
- user3 = LocalCache.getOrCreateUser("ccaadaccaadaccaada")
+ user1 = LocalCache.getOrCreateUser("aaabccaabbccaabbccabdd")
+ user2 = LocalCache.getOrCreateUser("bbbccabbbccabbbccaabdd")
+ user3 = LocalCache.getOrCreateUser("ccaadaccaadaccaadaabdd")
}
}
- val model: UserBasedErrorMessageViewModel = viewModel()
+ val model = MultiErrorToastMsg(R.string.error_dialog_zap_error)
model.add("Could not fetch invoice from https://minibits.cash/.well-known/lnurlp/victorieeman: There are too many unpaid invoices for this name.", user1)
model.add("No Wallets found to pay a lightning invoice. Please install a Lightning wallet to use zaps.", user2)
model.add("Could not fetch invoice", user3)
ThemeComparisonColumn {
- MultiUserErrorMessageDialogInner(
- title = "Couldn't not zap",
+ ErrorList(
model = model,
accountViewModel = accountViewModel,
- nav = nav,
+ nav = EmptyNav,
)
}
}
-@Stable
-class UserBasedErrorMessageViewModel : ViewModel() {
- val errors = MutableStateFlow>(emptyList())
- val hasErrors = errors.map { it.isNotEmpty() }
-
- fun add(
- message: String,
- user: User?,
- ) {
- add(UserBasedErrorMessage(message, user))
- }
-
- fun add(newError: UserBasedErrorMessage) {
- errors.update {
- it + newError
- }
- }
-
- fun clearErrors() {
- errors.update {
- emptyList()
- }
- }
-}
-
-class UserBasedErrorMessage(
- val error: String,
- val user: User?,
-)
-
@Composable
-fun MultiUserErrorMessageDialog(
- title: String,
- model: UserBasedErrorMessageViewModel,
+fun ErrorList(
+ model: MultiErrorToastMsg,
accountViewModel: AccountViewModel,
nav: INav,
) {
- val hasErrors by model.hasErrors.collectAsStateWithLifecycle(false)
- if (hasErrors) {
- MultiUserErrorMessageDialogInner(title, model, accountViewModel, nav)
+ val errorState by model.errors.collectAsStateWithLifecycle()
+ LazyColumn {
+ itemsIndexed(errorState) { index, it ->
+ ErrorRow(it, accountViewModel, nav)
+ if (index < errorState.size - 1) {
+ HorizontalDivider(thickness = DividerThickness)
+ }
+ }
}
}
-@Composable
-fun MultiUserErrorMessageDialogInner(
- title: String,
- model: UserBasedErrorMessageViewModel,
- accountViewModel: AccountViewModel,
- nav: INav,
-) {
- AlertDialog(
- onDismissRequest = model::clearErrors,
- title = { Text(title) },
- text = {
- val errorState by model.errors.collectAsStateWithLifecycle(emptyList())
- LazyColumn {
- itemsIndexed(errorState) { index, it ->
- ErrorRow(it, accountViewModel, nav)
- if (index < errorState.size - 1) {
- HorizontalDivider(thickness = DividerThickness)
- }
- }
- }
- },
- confirmButton = {
- Button(
- onClick = model::clearErrors,
- contentPadding = PaddingValues(horizontal = Size16dp),
- ) {
- Icon(
- imageVector = Icons.Outlined.Done,
- contentDescription = null,
- )
- Spacer(StdHorzSpacer)
- Text(stringRes(R.string.error_dialog_button_ok))
- }
- },
- )
-}
-
@Composable
fun ErrorRow(
errorState: UserBasedErrorMessage,
@@ -201,7 +124,7 @@ fun ErrorRow(
) {
errorState.user?.let {
Column(Modifier.width(Size40dp), horizontalAlignment = Alignment.Start) {
- UserPicture(errorState.user, Size30dp, Modifier, accountViewModel, nav)
+ UserPicture(it, Size30dp, Modifier, accountViewModel, nav)
Spacer(StdVertSpacer)
IconButton(
modifier = Size30Modifier,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiErrorToastMsg.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiErrorToastMsg.kt
new file mode 100644
index 000000000..ff42feb67
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiErrorToastMsg.kt
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts.multiline
+
+import androidx.compose.runtime.Immutable
+import com.vitorpamplona.amethyst.model.User
+import com.vitorpamplona.amethyst.ui.components.toasts.ToastMsg
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.update
+
+@Immutable
+class MultiErrorToastMsg(
+ val titleResId: Int,
+) : ToastMsg() {
+ val errors = MutableStateFlow>(emptyList())
+
+ fun add(
+ message: String,
+ user: User?,
+ ) {
+ add(UserBasedErrorMessage(message, user))
+ }
+
+ fun add(newError: UserBasedErrorMessage) {
+ errors.update {
+ it + newError
+ }
+ }
+}
+
+class UserBasedErrorMessage(
+ val error: String,
+ val user: User?,
+)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiUserErrorMessageDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiUserErrorMessageDialog.kt
new file mode 100644
index 000000000..edf0dea2b
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/toasts/multiline/MultiUserErrorMessageDialog.kt
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.components.toasts.multiline
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Done
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.LocalCache
+import com.vitorpamplona.amethyst.model.User
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
+import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
+import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.Size16dp
+import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+
+@Composable
+@Preview
+fun MultiUserErrorMessageContentPreview() {
+ val accountViewModel = mockAccountViewModel()
+ val nav = EmptyNav
+
+ var user1: User? = null
+ var user2: User? = null
+ var user3: User? = null
+
+ runBlocking {
+ withContext(Dispatchers.IO) {
+ user1 = LocalCache.getOrCreateUser("aaabccaabbccaabbccabdd")
+ user2 = LocalCache.getOrCreateUser("bbbccabbbccabbbccaabdd")
+ user3 = LocalCache.getOrCreateUser("ccaadaccaadaccaadaabdd")
+ }
+ }
+
+ val model = MultiErrorToastMsg(R.string.error_dialog_zap_error)
+ model.add("Could not fetch invoice from https://minibits.cash/.well-known/lnurlp/victorieeman: There are too many unpaid invoices for this name.", user1)
+ model.add("No Wallets found to pay a lightning invoice. Please install a Lightning wallet to use zaps.", user2)
+ model.add("Could not fetch invoice", user3)
+
+ ThemeComparisonColumn {
+ MultiUserErrorMessageDialog(
+ model = model,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ }
+}
+
+@Composable
+fun MultiUserErrorMessageDialog(
+ model: MultiErrorToastMsg,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ AlertDialog(
+ onDismissRequest = accountViewModel.toastManager::clearToasts,
+ title = { Text(stringRes(model.titleResId)) },
+ text = {
+ ErrorList(model, accountViewModel, nav)
+ },
+ confirmButton = {
+ Button(
+ onClick = accountViewModel.toastManager::clearToasts,
+ contentPadding = PaddingValues(horizontal = Size16dp),
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Done,
+ contentDescription = null,
+ )
+ Spacer(StdHorzSpacer)
+ Text(stringRes(R.string.error_dialog_button_ok))
+ }
+ },
+ )
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
index b3af85f60..c298d7b04 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
@@ -51,8 +51,8 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.MainActivity
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataScreen
import com.vitorpamplona.amethyst.ui.actions.relays.AllRelayListView
-import com.vitorpamplona.amethyst.ui.components.DisplayErrorMessages
import com.vitorpamplona.amethyst.ui.components.DisplayNotifyMessages
+import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
@@ -354,7 +354,7 @@ fun AppNavigation(
NavigateIfIntentRequested(nav, accountViewModel, accountStateViewModel)
- DisplayErrorMessages(accountViewModel)
+ DisplayErrorMessages(accountViewModel.toastManager, accountViewModel, nav)
DisplayNotifyMessages(accountViewModel, nav)
}
@@ -437,7 +437,7 @@ private fun NavigateIfIntentRequested(
actionableNextPage = null
} else {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.invalid_nip19_uri,
R.string.invalid_nip19_uri_description,
intentNextPage,
@@ -492,7 +492,7 @@ private fun NavigateIfIntentRequested(
} else {
scope.launch {
delay(1000)
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.invalid_nip19_uri,
R.string.invalid_nip19_uri_description,
uri,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt
index 0cfb0aefd..b3f573c2d 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt
@@ -551,7 +551,7 @@ fun ListContent(
accountViewModel.setTorSettings(torSettings)
},
onError = {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.could_not_connect_to_tor),
it,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt
index 6d8bb6f52..725f74977 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt
@@ -76,11 +76,11 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
+import com.vitorpamplona.amethyst.ui.components.toasts.StringToastMsg
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockVitorAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
@@ -530,23 +530,23 @@ fun ZapVote(
indication = ripple24dp,
onClick = {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
)
} else if (pollViewModel.isPollClosed()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.poll_unable_to_vote,
R.string.poll_is_closed_explainer,
)
} else if (isLoggedUser) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.poll_unable_to_vote,
R.string.poll_author_no_vote,
)
} else if (pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn.value) {
// only allow one vote per option when min==max, i.e. atomic vote amount specified
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.poll_unable_to_vote,
R.string.one_vote_per_user_on_atomic_votes,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt
index 9a9e06a76..3ee5b3c7d 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt
@@ -99,12 +99,12 @@ import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
-import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
+import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource.user
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.AnimatedBorderTextCornerRadius
@@ -662,7 +662,7 @@ fun ReplyReaction(
modifier = iconSizeModifier,
onClick = {
if (baseNote.isDraft()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_reply_to_a_draft_note,
)
@@ -670,7 +670,7 @@ fun ReplyReaction(
if (accountViewModel.isWriteable()) {
onPress()
} else {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_reply,
)
@@ -986,7 +986,7 @@ private fun likeClick(
onWantsToSignReaction: () -> Unit,
) {
if (baseNote.isDraft()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_react_to_a_draft_note,
)
@@ -995,12 +995,12 @@ private fun likeClick(
val choices = accountViewModel.reactionChoices()
if (choices.isEmpty()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.no_reactions_setup,
R.string.no_reaction_type_setup_long_press_to_change,
)
} else if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_like_posts,
)
@@ -1025,7 +1025,6 @@ fun ZapReaction(
var wantsToZap by remember { mutableStateOf(false) }
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
var wantsToSetCustomZap by remember { mutableStateOf(false) }
- val errorViewModel: UserBasedErrorMessageViewModel = viewModel()
var wantsToPay by
remember(baseNote) {
mutableStateOf>(
@@ -1060,7 +1059,7 @@ fun ZapReaction(
onError = { _, message, user ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, user)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
}
},
onPayViaIntent = { wantsToPay = it },
@@ -1089,7 +1088,7 @@ fun ZapReaction(
onError = { _, message, user ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, user)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
}
},
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
@@ -1097,13 +1096,6 @@ fun ZapReaction(
)
}
- MultiUserErrorMessageDialog(
- title = stringRes(id = R.string.error_dialog_zap_error),
- model = errorViewModel,
- accountViewModel = accountViewModel,
- nav = nav,
- )
-
if (wantsToChangeZapAmount) {
UpdateZapAmountDialog(
onClose = { wantsToChangeZapAmount = false },
@@ -1120,12 +1112,12 @@ fun ZapReaction(
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
justShowError = {
scope.launch {
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
)
@@ -1137,7 +1129,7 @@ fun ZapReaction(
onError = { _, message, user ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, user)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
}
},
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
@@ -1191,7 +1183,7 @@ fun zapClick(
onPayViaIntent: (ImmutableList) -> Unit,
) {
if (baseNote.isDraft()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_zap_to_a_draft_note,
)
@@ -1201,12 +1193,12 @@ fun zapClick(
val choices = accountViewModel.zapAmountChoices()
if (choices.isEmpty()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.error_dialog_zap_error,
R.string.no_zap_amount_setup_long_press_to_change,
)
} else if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.error_dialog_zap_error,
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
index 57209e901..d96d742a1 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
@@ -169,7 +169,7 @@ fun RenderRelay(
openRelayDialog = true
},
onError = { url, errorCode, exceptionMessage ->
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.unable_to_download_relay_document,
when (errorCode) {
Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt
index bc1d43280..807bf1579 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt
@@ -337,12 +337,12 @@ fun UpdateZapAmountContent(
postViewModel.updateNIP47(nip47uri)
} catch (e: IllegalArgumentException) {
if (e.message != null) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_parsing_nip47_title),
stringRes(context, R.string.error_parsing_nip47, nip47uri, e.message!!),
)
} else {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_parsing_nip47_title),
stringRes(context, R.string.error_parsing_nip47_no_error, nip47uri),
)
@@ -510,12 +510,12 @@ fun UpdateZapAmountContent(
postViewModel.updateNIP47(it)
} catch (e: IllegalArgumentException) {
if (e.message != null) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_parsing_nip47_title),
stringRes(context, R.string.error_parsing_nip47, it, e.message!!),
)
} else {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_parsing_nip47_title),
stringRes(context, R.string.error_parsing_nip47_no_error, it),
)
@@ -611,7 +611,7 @@ fun UpdateZapAmountContent(
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = { showPassword = true },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
} else {
showPassword = false
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt
index b9c0da284..364865fb5 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt
@@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
+import com.vitorpamplona.amethyst.ui.components.toasts.multiline.UserBasedErrorMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
index 73efdf414..1a9f38ec0 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
@@ -201,7 +201,7 @@ fun ShowFollowingOrUnfollowingButton(
if (isFollowing) {
UnfollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_unfollow,
)
@@ -212,7 +212,7 @@ fun ShowFollowingOrUnfollowingButton(
} else {
FollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt
index 018725713..573fe81ca 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DisplayOts.kt
@@ -75,7 +75,7 @@ fun DisplayOts(
val fullDateTime =
SimpleDateFormat.getDateTimeInstance().format(Date(unixtimestamp * 1000))
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.ots_info_title,
R.string.ots_info_description,
fullDateTime,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt
index c92fd13aa..473915ffc 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ZapTheDevsCard.kt
@@ -58,7 +58,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
@@ -72,10 +71,8 @@ import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.CloseIcon
-import com.vitorpamplona.amethyst.ui.note.MultiUserErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon
import com.vitorpamplona.amethyst.ui.note.PayViaIntentDialog
-import com.vitorpamplona.amethyst.ui.note.UserBasedErrorMessageViewModel
import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup
import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
@@ -293,7 +290,6 @@ fun ZapDonationButton(
nav: INav,
) {
var wantsToZap by remember { mutableStateOf?>(null) }
- val errorViewModel: UserBasedErrorMessageViewModel = viewModel()
var wantsToPay by
remember(baseNote) {
mutableStateOf>(
@@ -320,7 +316,7 @@ fun ZapDonationButton(
onError = { _, message, toUser ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, toUser)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, toUser)
}
},
onPayViaIntent = { wantsToPay = it },
@@ -344,7 +340,7 @@ fun ZapDonationButton(
onError = { _, message, user ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, user)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
}
},
onProgress = {
@@ -354,13 +350,6 @@ fun ZapDonationButton(
)
}
- MultiUserErrorMessageDialog(
- title = stringRes(id = R.string.error_dialog_zap_error),
- model = errorViewModel,
- accountViewModel = accountViewModel,
- nav = nav,
- )
-
if (wantsToPay.isNotEmpty()) {
PayViaIntentDialog(
payingInvoices = wantsToPay,
@@ -370,12 +359,12 @@ fun ZapDonationButton(
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
justShowError = {
scope.launch {
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
)
@@ -441,7 +430,7 @@ fun customZapClick(
onPayViaIntent: (ImmutableList) -> Unit,
) {
if (baseNote.isDraft()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_zap_to_a_draft_note,
)
@@ -451,12 +440,12 @@ fun customZapClick(
val choices = accountViewModel.zapAmountChoices()
if (choices.isEmpty()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_dialog_zap_error),
stringRes(context, R.string.no_zap_amount_setup_long_press_to_change),
)
} else if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
stringRes(context, R.string.error_dialog_zap_error),
stringRes(context, R.string.login_with_a_private_key_to_be_able_to_send_zaps),
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
index 2fbcefca7..3e5675030 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
@@ -221,7 +221,7 @@ fun DisplayFileList(
ContextCompat.startActivity(context, intent, null)
} catch (e: Exception) {
if (e is CancellationException) throw e
- accountViewModel.toast(R.string.torrent_failure, R.string.torrent_no_apps)
+ accountViewModel.toastManager.toast(R.string.torrent_failure, R.string.torrent_no_apps)
}
}, Modifier.size(Size30dp)) {
DownloadForOfflineIcon(Size20dp, MaterialTheme.colorScheme.onBackground)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
index d19575501..473796764 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
@@ -156,7 +156,7 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
contract = ActivityResultContracts.StartActivityForResult(),
onResult = { result ->
if (result.resultCode != Activity.RESULT_OK) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.sign_request_rejected,
R.string.sign_request_rejected_description,
)
@@ -183,7 +183,7 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
} catch (e: Exception) {
if (e is CancellationException) throw e
Log.e("Signer", "Error opening Signer app", e)
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.error_opening_external_signer,
R.string.error_opening_external_signer_description,
)
@@ -203,7 +203,7 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
} catch (e: Exception) {
if (e is CancellationException) throw e
Log.e("Signer", "Error opening Signer app", e)
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.error_opening_external_signer,
R.string.error_opening_external_signer_description,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt
index 185e75900..e92b95028 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt
@@ -321,7 +321,7 @@ private fun NSecCopyButton(accountViewModel: AccountViewModel) {
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = { copyNSec(context, scope, accountViewModel.account, clipboardManager) },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
},
shape = ButtonBorder,
@@ -371,7 +371,7 @@ private fun EncryptNSecCopyButton(
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = { encryptCopyNSec(password, context, scope, accountViewModel, clipboardManager) },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
},
shape = ButtonBorder,
@@ -496,7 +496,7 @@ private fun QrCodeButtonBase(
context = context,
keyguardLauncher = keyguardLauncher,
onApproved = { dialogOpen = true },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
},
) {
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt
index 14d7e73ab..349caffc0 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt
@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
import com.vitorpamplona.amethyst.ui.actions.Dao
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
+import com.vitorpamplona.amethyst.ui.components.toasts.ToastManager
import com.vitorpamplona.amethyst.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
@@ -123,8 +124,6 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -137,25 +136,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-@Immutable open class ToastMsg
-
-@Immutable class StringToastMsg(
- val title: String,
- val msg: String,
-) : ToastMsg()
-
-@Immutable class ResourceToastMsg(
- val titleResId: Int,
- val resourceId: Int,
- val params: Array? = null,
-) : ToastMsg()
-
-@Immutable class ThrowableToastMsg(
- val titleResId: Int,
- val msg: String? = null,
- val throwable: Throwable,
-) : ToastMsg()
-
@Stable
class AccountViewModel(
accountSettings: AccountSettings,
@@ -189,7 +169,7 @@ class AccountViewModel(
val dmRelays: StateFlow = observeByAuthor(ChatMessageRelayListEvent.KIND, account.signer.pubKey)
val searchRelays: StateFlow = observeByAuthor(SearchRelayListEvent.KIND, account.signer.pubKey)
- val toasts = MutableSharedFlow(0, 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ val toastManager = ToastManager()
val feedStates = AccountFeedContentStates(this)
@@ -269,40 +249,6 @@ class AccountViewModel(
Route.Notification to notificationHasNewItemsFlow,
)
- fun clearToasts() {
- viewModelScope.launch { toasts.emit(null) }
- }
-
- fun toast(
- title: String,
- message: String,
- ) {
- viewModelScope.launch { toasts.emit(StringToastMsg(title, message)) }
- }
-
- fun toast(
- titleResId: Int,
- resourceId: Int,
- ) {
- viewModelScope.launch { toasts.emit(ResourceToastMsg(titleResId, resourceId)) }
- }
-
- fun toast(
- titleResId: Int,
- message: String?,
- throwable: Throwable,
- ) {
- viewModelScope.launch { toasts.emit(ThrowableToastMsg(titleResId, message, throwable)) }
- }
-
- fun toast(
- titleResId: Int,
- resourceId: Int,
- vararg params: String,
- ) {
- viewModelScope.launch { toasts.emit(ResourceToastMsg(titleResId, resourceId, params)) }
- }
-
fun isWriteable(): Boolean = account.isWriteable()
fun userProfile(): User = account.userProfile()
@@ -1406,7 +1352,7 @@ class AccountViewModel(
onMore: () -> Unit,
) {
if (baseNote.isDraft()) {
- toast(
+ toastManager.toast(
R.string.draft_note,
R.string.it_s_not_possible_to_quote_to_a_draft_note,
)
@@ -1420,7 +1366,7 @@ class AccountViewModel(
onMore()
}
} else {
- toast(
+ toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_boost_posts,
)
@@ -1766,6 +1712,7 @@ fun mockAccountViewModel(): AccountViewModel {
KeyPair(
privKey = Hex.decode("0f761f8a5a481e26f06605a1d9b3e9eba7a107d351f43c43a57469b788274499"),
pubKey = Hex.decode("989c3734c46abac7ce3ce229971581a5a6ee39cdd6aa7261a55823fa7f8c4799"),
+ forceReplacePubkey = false,
),
),
sharedPreferencesViewModel.sharedPrefs,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
index 07f698f4c..8a27ae796 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
@@ -527,7 +527,7 @@ fun NewPostScreen(
it,
accountViewModel.account.settings.defaultFileServer,
onAdd = { alt, server, sensitiveContent, mediaQuality ->
- postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, false, server, accountViewModel::toast, context)
+ postViewModel.upload(alt, if (sensitiveContent) "" else null, mediaQuality, false, server, accountViewModel.toastManager::toast, context)
if (server.type != ServerType.NIP95) {
accountViewModel.account.settings.changeDefaultFileServer(server)
}
@@ -558,7 +558,7 @@ fun NewPostScreen(
postViewModel.wantsInvoice = false
},
onClose = { postViewModel.wantsInvoice = false },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt
index 65ab604ff..c13e6c6a2 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/upload/ChatFileUploadDialog.kt
@@ -65,7 +65,7 @@ fun RoomChatFileUploadDialog(
},
upload = {
channelScreenModel.upload(
- onError = accountViewModel::toast,
+ onError = accountViewModel.toastManager::toast,
context = context,
onceUploaded = onUpload,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt
index 6b7def77b..b90f6346f 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelFileUploadDialog.kt
@@ -126,7 +126,7 @@ fun ChannelFileUploadDialog(
},
upload = {
channelScreenModel.upload(
- onError = accountViewModel::toast,
+ onError = accountViewModel.toastManager::toast,
context = context,
onceUploaded = onUpload,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt
index d7dd79387..b6e7321b3 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt
@@ -70,11 +70,9 @@ import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.DVMCard
-import com.vitorpamplona.amethyst.ui.note.MultiUserErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon
import com.vitorpamplona.amethyst.ui.note.PayViaIntentDialog
-import com.vitorpamplona.amethyst.ui.note.UserBasedErrorMessageViewModel
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup
import com.vitorpamplona.amethyst.ui.note.ZapIcon
@@ -442,7 +440,6 @@ fun ZapDVMButton(
val noteAuthor = baseNote.author ?: return
var wantsToZap by remember { mutableStateOf?>(null) }
- val errorViewModel: UserBasedErrorMessageViewModel = viewModel()
var wantsToPay by
remember(baseNote) {
mutableStateOf>(
@@ -472,7 +469,7 @@ fun ZapDVMButton(
onError = { _, message, toUser ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, toUser)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, toUser)
}
},
onPayViaIntent = { wantsToPay = it },
@@ -496,7 +493,7 @@ fun ZapDVMButton(
onError = { _, message, user ->
scope.launch {
zappingProgress = 0f
- errorViewModel.add(message, user)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, message, user)
}
},
onProgress = {
@@ -506,13 +503,6 @@ fun ZapDVMButton(
)
}
- MultiUserErrorMessageDialog(
- title = stringRes(id = R.string.error_dialog_zap_error),
- model = errorViewModel,
- accountViewModel,
- nav,
- )
-
if (wantsToPay.isNotEmpty()) {
PayViaIntentDialog(
payingInvoices = wantsToPay,
@@ -522,12 +512,12 @@ fun ZapDVMButton(
wantsToPay = persistentListOf()
scope.launch {
zappingProgress = 0f
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
justShowError = {
scope.launch {
- errorViewModel.add(it)
+ accountViewModel.toastManager.toast(R.string.error_dialog_zap_error, it)
}
},
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashScreen.kt
index c180e0468..0bde5c607 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/GeoHashScreen.kt
@@ -208,7 +208,7 @@ fun GeoHashActionOptions(
if (isFollowingTag) {
UnfollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_unfollow,
)
@@ -219,7 +219,7 @@ fun GeoHashActionOptions(
} else {
FollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagScreen.kt
index 9779f81a8..7d60fb079 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/HashtagScreen.kt
@@ -187,7 +187,7 @@ fun HashtagActionOptions(
if (isFollowingTag) {
UnfollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_unfollow,
)
@@ -198,7 +198,7 @@ fun HashtagActionOptions(
} else {
FollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayFollowUnfollowButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayFollowUnfollowButton.kt
index 9fe7176d2..6a474f8f3 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayFollowUnfollowButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayFollowUnfollowButton.kt
@@ -56,7 +56,7 @@ fun DisplayFollowUnfollowButton(
if (isLoggedInFollowingUser) {
UnfollowButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_unfollow,
)
@@ -68,7 +68,7 @@ fun DisplayFollowUnfollowButton(
if (isUserFollowingLoggedIn) {
FollowButton(R.string.follow_back) {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow,
)
@@ -79,7 +79,7 @@ fun DisplayFollowUnfollowButton(
} else {
FollowButton(R.string.follow) {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_follow,
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt
index f80a8a02c..e46498b1f 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/DisplayLNAddress.kt
@@ -130,7 +130,7 @@ fun DisplayLNAddress(
}
},
onClose = { zapExpanded = false },
- onError = { title, message -> accountViewModel.toast(title, message) },
+ onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt
index 334672c81..1d1d17749 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt
@@ -331,7 +331,7 @@ fun MutedWordActionOptions(
if (isMutedWord == true) {
ShowWordButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_show_word,
)
@@ -342,7 +342,7 @@ fun MutedWordActionOptions(
} else {
HideWordButton {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_hide_word,
)
@@ -393,7 +393,7 @@ private fun hideIfWritable(
currentWordToAdd: MutableState,
) {
if (!accountViewModel.isWriteable()) {
- accountViewModel.toast(
+ accountViewModel.toastManager.toast(
R.string.read_only_user,
R.string.login_with_a_private_key_to_be_able_to_hide_word,
)
diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt
index 048309e56..0fd06d11d 100644
--- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt
+++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey
class KeyPair(
privKey: ByteArray? = null,
pubKey: ByteArray? = null,
+ forceReplacePubkey: Boolean = true,
) {
val privKey: ByteArray?
val pubKey: ByteArray
@@ -44,7 +45,11 @@ class KeyPair(
} else {
// as private key is provided, ignore the public key and set keys according to private key
this.privKey = privKey
- this.pubKey = Nip01.pubKeyCreate(privKey)
+ if (pubKey == null || forceReplacePubkey) {
+ this.pubKey = Nip01.pubKeyCreate(privKey)
+ } else {
+ this.pubKey = pubKey
+ }
}
}