mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-03-17 13:21:50 +01:00
Improves multiple error dialogs appearing when zapping from the reactions bar in the feed.
This commit is contained in:
parent
d76de0123a
commit
fd538ebde1
4
.idea/inspectionProfiles/Project_Default.xml
generated
4
.idea/inspectionProfiles/Project_Default.xml
generated
@ -65,6 +65,10 @@
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
|
@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -218,7 +218,7 @@ fun RelaySelectionDialog(
|
||||
)
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
|
@ -103,7 +103,7 @@ fun BasicRelaySetupInfoDialog(
|
||||
)
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
|
@ -280,7 +280,7 @@ fun LoadRelayInfo(
|
||||
)
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
|
@ -105,7 +105,7 @@ fun Kind3RelaySetupInfoProposalDialog(
|
||||
)
|
||||
}
|
||||
|
||||
accountViewModel.toast(
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.unable_to_download_relay_document),
|
||||
msg,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -116,7 +116,7 @@ fun CashuPreview(
|
||||
CashuPreviewNew(
|
||||
it,
|
||||
accountViewModel::meltCashu,
|
||||
accountViewModel::toast,
|
||||
accountViewModel.toastManager::toast,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<out String>? = null,
|
||||
) : ToastMsg()
|
@ -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()
|
@ -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()
|
@ -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<ToastMsg?>(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) })
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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,82 +75,33 @@ 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<List<UserBasedErrorMessage>>(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)
|
||||
}
|
||||
}
|
||||
|
||||
@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())
|
||||
val errorState by model.errors.collectAsStateWithLifecycle()
|
||||
LazyColumn {
|
||||
itemsIndexed(errorState) { index, it ->
|
||||
ErrorRow(it, accountViewModel, nav)
|
||||
@ -172,21 +110,6 @@ fun MultiUserErrorMessageDialogInner(
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
@ -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,
|
@ -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<List<UserBasedErrorMessage>>(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?,
|
||||
)
|
@ -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))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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<ImmutableList<ZapPaymentHandler.Payable>>(
|
||||
@ -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<ZapPaymentHandler.Payable>) -> 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,
|
||||
)
|
||||
|
@ -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 ->
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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<ImmutableList<Long>?>(null) }
|
||||
val errorViewModel: UserBasedErrorMessageViewModel = viewModel()
|
||||
var wantsToPay by
|
||||
remember(baseNote) {
|
||||
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
|
||||
@ -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<ZapPaymentHandler.Payable>) -> 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),
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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) },
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
@ -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<out String>? = 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<ChatMessageRelayListEvent?> = observeByAuthor(ChatMessageRelayListEvent.KIND, account.signer.pubKey)
|
||||
val searchRelays: StateFlow<SearchRelayListEvent?> = observeByAuthor(SearchRelayListEvent.KIND, account.signer.pubKey)
|
||||
|
||||
val toasts = MutableSharedFlow<ToastMsg?>(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,
|
||||
|
@ -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) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ fun RoomChatFileUploadDialog(
|
||||
},
|
||||
upload = {
|
||||
channelScreenModel.upload(
|
||||
onError = accountViewModel::toast,
|
||||
onError = accountViewModel.toastManager::toast,
|
||||
context = context,
|
||||
onceUploaded = onUpload,
|
||||
)
|
||||
|
@ -126,7 +126,7 @@ fun ChannelFileUploadDialog(
|
||||
},
|
||||
upload = {
|
||||
channelScreenModel.upload(
|
||||
onError = accountViewModel::toast,
|
||||
onError = accountViewModel.toastManager::toast,
|
||||
context = context,
|
||||
onceUploaded = onUpload,
|
||||
)
|
||||
|
@ -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<List<Long>?>(null) }
|
||||
val errorViewModel: UserBasedErrorMessageViewModel = viewModel()
|
||||
var wantsToPay by
|
||||
remember(baseNote) {
|
||||
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
|
||||
@ -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)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -130,7 +130,7 @@ fun DisplayLNAddress(
|
||||
}
|
||||
},
|
||||
onClose = { zapExpanded = false },
|
||||
onError = { title, message -> accountViewModel.toast(title, message) },
|
||||
onError = { title, message -> accountViewModel.toastManager.toast(title, message) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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<String>,
|
||||
) {
|
||||
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,
|
||||
)
|
||||
|
@ -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
|
||||
if (pubKey == null || forceReplacePubkey) {
|
||||
this.pubKey = Nip01.pubKeyCreate(privKey)
|
||||
} else {
|
||||
this.pubKey = pubKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user