From 2bed164e149c634b547713366d7f5c28247ed890 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 9 Jan 2024 15:47:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6=E3=81=AE?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E3=82=A8?= =?UTF-8?q?=E3=83=A9=E3=83=BC=E3=82=92=E3=82=A2=E3=83=97=E3=83=AA=E5=85=A8?= =?UTF-8?q?=E4=BD=93=E3=81=A7=E5=87=A6=E7=90=86=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../misskeyandroidclient/MainActivity.kt | 8 ++ .../note/option/NoteOptionViewModel.kt | 20 ++++ .../milktea/note/viewmodel/NotesViewModel.kt | 112 ++++++++++++++++-- .../milktea/userlist/ListListActivity.kt | 8 ++ .../userlist/viewmodel/ListListViewModel.kt | 34 ++++++ 5 files changed, 175 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/MainActivity.kt b/app/src/main/java/jp/panta/misskeyandroidclient/MainActivity.kt index 71b2f17aef..be138c3686 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/MainActivity.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/MainActivity.kt @@ -41,6 +41,7 @@ import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common.ui.ToolbarSetter import net.pantasystem.milktea.common_android_ui.account.AccountSwitchingDialog import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel +import net.pantasystem.milktea.common_android_ui.error.UserActionAppGlobalErrorListener import net.pantasystem.milktea.common_android_ui.report.ReportViewModel import net.pantasystem.milktea.common_navigation.MainNavigation import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel @@ -78,6 +79,9 @@ class MainActivity : AppCompatActivity(), ToolbarSetter { @Inject internal lateinit var channelAPIMainEventDispatcherAdapter: ChannelAPIMainEventDispatcherAdapter + @Inject + internal lateinit var userActionAppGlobalErrorListener: UserActionAppGlobalErrorListener + private val notesViewModel: NotesViewModel by viewModels() private val accountViewModel: AccountViewModel by viewModels() @@ -167,6 +171,10 @@ class MainActivity : AppCompatActivity(), ToolbarSetter { } GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(this) + userActionAppGlobalErrorListener( + lifecycle = lifecycle, + fragmentManager = supportFragmentManager + ) } override fun setToolbar(toolbar: Toolbar, visibleTitle: Boolean) { diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/option/NoteOptionViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/option/NoteOptionViewModel.kt index 3b968f3e1c..c0b9d18895 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/option/NoteOptionViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/option/NoteOptionViewModel.kt @@ -8,7 +8,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +import net.pantasystem.milktea.app_store.handler.AppGlobalError +import net.pantasystem.milktea.app_store.handler.UserActionAppGlobalErrorStore import net.pantasystem.milktea.common.Logger +import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.page.Pageable @@ -24,6 +27,7 @@ class NoteOptionViewModel @Inject constructor( private val noteRelationGetter: NoteRelationGetter, private val loggerFactory: Logger.Factory, private val featureEnables: FeatureEnables, + private val userActionAppGlobalErrorStore: UserActionAppGlobalErrorStore, private val savedStateHandle: SavedStateHandle ) : ViewModel() { companion object { @@ -98,6 +102,14 @@ class NoteOptionViewModel @Inject constructor( viewModelScope.launch { noteRepository.createThreadMute(noteId).onFailure { logger.error("create thread mute failed", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + "NoteOptionViewModel.createThreadMute", + AppGlobalError.ErrorLevel.Error, + StringSource("Create thread mute failed"), + it + ) + ) } savedStateHandle[NOTE_ID] = noteId } @@ -107,6 +119,14 @@ class NoteOptionViewModel @Inject constructor( viewModelScope.launch { noteRepository.deleteThreadMute(noteId).onFailure { logger.error("delete thread mute failed", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + "NoteOptionViewModel.deleteThreadMute", + AppGlobalError.ErrorLevel.Error, + StringSource("Delete thread mute failed"), + it + ) + ) } savedStateHandle[NOTE_ID] = noteId } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt index cc77a41d80..a10a660798 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt @@ -7,6 +7,8 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch +import net.pantasystem.milktea.app_store.handler.AppGlobalError +import net.pantasystem.milktea.app_store.handler.UserActionAppGlobalErrorStore import net.pantasystem.milktea.app_store.notes.NoteTranslationStore import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.mapCancellableCatching @@ -48,6 +50,7 @@ class NotesViewModel @Inject constructor( private val deleteReactionUseCase: DeleteReactionsUseCase, private val voteUseCase: VoteUseCase, private val configRepository: LocalConfigRepository, + private val userActionAppGlobalErrorStore: UserActionAppGlobalErrorStore, loggerFactory: Logger.Factory ) : ViewModel() { private val logger by lazy { @@ -76,6 +79,15 @@ class NotesViewModel @Inject constructor( quoteRenoteTarget.tryEmit( QuoteRenoteData.ofTimeline(it.id) ) + }.onFailure { + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.showQuoteNoteEditor", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Load note failed"), + throwable = it + ) + ) } } } @@ -86,6 +98,15 @@ class NotesViewModel @Inject constructor( quoteRenoteTarget.tryEmit( QuoteRenoteData.ofChannel(it.id, requireNotNull(it.channelId)) ) + }.onFailure { + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.showQuoteToChannelNoteEditor", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Load note failed"), + throwable = it + ) + ) } } } @@ -108,6 +129,14 @@ class NotesViewModel @Inject constructor( viewModelScope.launch { toggleReactionUseCase(noteId, reaction).onFailure { logger.error("リアクション失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.toggleReaction", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Reaction failed"), + throwable = it + ) + ) } } } @@ -116,6 +145,14 @@ class NotesViewModel @Inject constructor( viewModelScope.launch { deleteReactionUseCase(noteId).onFailure { logger.error("リアクションの解除に失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.deleteReactions", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Delete reactions failed"), + throwable = it + ) + ) } } } @@ -132,12 +169,18 @@ class NotesViewModel @Inject constructor( fun deleteFavorite(noteId: Note.Id) { viewModelScope.launch { - val result = deleteFavoriteUseCase(noteId).getOrNull() != null - _statusMessage.tryEmit(if (result) { - StringSource(R.string.removed_from_favorites) - } else { - StringSource(R.string.failed_to_delete_favorites) - }) + deleteFavoriteUseCase(noteId).onFailure { + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.deleteFavorite", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource(R.string.failed_to_delete_favorites), + throwable = it + ) + ) + }.onSuccess { + _statusMessage.tryEmit(StringSource(R.string.removed_from_favorites)) + } } } @@ -145,7 +188,14 @@ class NotesViewModel @Inject constructor( fun addBookmark(noteId: Note.Id) { viewModelScope.launch { createBookmarkUseCase(noteId).onFailure { - logger.error("add book mark error", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.addBookmark", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Add bookmark failed"), + throwable = it + ) + ) } } } @@ -154,6 +204,14 @@ class NotesViewModel @Inject constructor( viewModelScope.launch { deleteBookmarkUseCase(noteId).onFailure { logger.error("remove book mark error", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.removeBookmark", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Remove bookmark failed"), + throwable = it + ) + ) } } } @@ -164,6 +222,14 @@ class NotesViewModel @Inject constructor( _statusMessage.tryEmit(StringSource(R.string.successfully_deleted)) }.onFailure { logger.error("ノート削除に失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.removeNote", + level = AppGlobalError.ErrorLevel.Error, + message = StringSource("Delete note failed"), + throwable = it + ) + ) } } @@ -175,6 +241,14 @@ class NotesViewModel @Inject constructor( _openNoteEditorEvent.tryEmit(it) }.onFailure { logger.error("削除に失敗しました", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.removeAndEditNote", + level = AppGlobalError.ErrorLevel.Error, + message = StringSource("Delete note failed"), + throwable = it + ) + ) } } @@ -187,6 +261,14 @@ class NotesViewModel @Inject constructor( viewModelScope.launch { voteUseCase(noteId, choice).onFailure { logger.error("投票に失敗しました", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.vote", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Vote failed"), + throwable = it + ) + ) }.onSuccess { logger.debug("投票に成功しました") } @@ -197,6 +279,14 @@ class NotesViewModel @Inject constructor( viewModelScope.launch { toggleFavoriteUseCase(note.id).onFailure { logger.error("favoriteに失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.onToggleFavoriteUseCase", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Toggle favorite failed"), + throwable = it + ) + ) } } } @@ -215,6 +305,14 @@ class NotesViewModel @Inject constructor( configRepository.save(it) }.onFailure { logger.error("警告表示の抑制に失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + tag = "NotesViewModel.neverShowSensitiveMediaDialog", + level = AppGlobalError.ErrorLevel.Warning, + message = StringSource("Never show sensitive media dialog failed"), + throwable = it + ) + ) } } } diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt index 582d57e24b..4a6d2a06f7 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt @@ -13,6 +13,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.ExperimentalCoroutinesApi import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.ui.ApplyTheme +import net.pantasystem.milktea.common_android_ui.error.UserActionAppGlobalErrorListener import net.pantasystem.milktea.common_compose.MilkteaStyleConfigApplyAndTheme import net.pantasystem.milktea.common_navigation.UserListArgs import net.pantasystem.milktea.common_navigation.UserListNavigation @@ -63,6 +64,9 @@ class ListListActivity : AppCompatActivity() { @Inject internal lateinit var configRepository: LocalConfigRepository + @Inject + internal lateinit var userActionAppGlobalErrorListener: UserActionAppGlobalErrorListener + private val addUserId: User.Id? by lazy { val addUserIdSt = intent.getStringExtra(EXTRA_ADD_USER_ID) @@ -79,6 +83,10 @@ class ListListActivity : AppCompatActivity() { super.onCreate(savedInstanceState) applyTheme() + userActionAppGlobalErrorListener( + lifecycle = lifecycle, + fragmentManager = supportFragmentManager + ) mListListViewModel.setAddTargetUserId(addUserId) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt index 3924c5b068..205ea635ae 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt @@ -8,7 +8,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.app_store.handler.AppGlobalError +import net.pantasystem.milktea.app_store.handler.UserActionAppGlobalErrorStore import net.pantasystem.milktea.common.* +import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.list.UserList import net.pantasystem.milktea.model.list.UserListRepository @@ -27,6 +30,7 @@ class ListListViewModel @Inject constructor( private val userRepository: UserRepository, private val toggleAddToTabUseCase: UserListTabToggleAddToTabUseCase, private val savedStateHandle: SavedStateHandle, + private val userActionAppGlobalErrorStore: UserActionAppGlobalErrorStore, ) : ViewModel() { companion object { @@ -122,6 +126,20 @@ class ListListViewModel @Inject constructor( userListRepository.syncOne(userList.id) }.onFailure { logger.error("toggle user failed", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + "ListListViewModel.toggle", + AppGlobalError.ErrorLevel.Error, + StringSource( + if (userList.userIds.contains(userId)) { + "Remove user from list failed" + } else { + "Add user to list failed" + } + ), + it + ), + ) } } } @@ -131,6 +149,14 @@ class ListListViewModel @Inject constructor( viewModelScope.launch { toggleAddToTabUseCase(ul.id, savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID]).onFailure { logger.error("タブtoggle処理失敗", e = it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + "ListListViewModel.toggleTab", + AppGlobalError.ErrorLevel.Error, + StringSource("add/remove tab failed"), + it + ) + ) } } } @@ -146,6 +172,14 @@ class ListListViewModel @Inject constructor( logger.debug("作成成功") }.onFailure { logger.error("作成失敗", it) + userActionAppGlobalErrorStore.dispatch( + AppGlobalError( + "ListListViewModel.createUserList", + AppGlobalError.ErrorLevel.Error, + StringSource("create user list failed"), + it + ) + ) } }