From a915e95c884d9ebfe80cb649d547e8d1a22a20e0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 30 Jul 2021 15:38:00 +0800 Subject: [PATCH 001/615] init kotlin mpp --- {app => android}/.gitignore | 0 {app => android}/build.gradle.kts | 0 {app => android}/proguard-rules.pro | 0 .../1.json | 0 .../2.json | 0 .../3.json | 0 .../10.json | 0 .../11.json | 0 .../12.json | 0 .../13.json | 0 .../14.json | 0 .../15.json | 0 .../16.json | 0 .../17.json | 0 .../18.json | 0 .../twiderex/ExampleInstrumentedTest.kt | 0 .../twiderex/db/AppDatabaseMigrationTest.kt | 0 .../com/twidere/twiderex/db/DbDMEventTest.kt | 0 .../com/twidere/twiderex/db/DbListTest.kt | 0 .../com/twidere/twiderex/db/DbSearchTest.kt | 0 .../com/twidere/twiderex/db/DbTrendTest.kt | 0 .../twiderex/mock/MockDirectMessageService.kt | 0 .../twiderex/mock/MockLookUpService.kt | 0 .../twiderex/paging/dm/DMEventMediatorTest.kt | 0 .../dm/DirectMessageRepositoryTest.kt | 0 {app => android}/src/main/AndroidManifest.xml | 0 .../src/main/ic_launcher-playstore.png | Bin .../kotlin/com/twidere/twiderex/Consts.kt | 0 .../kotlin/com/twidere/twiderex/TwidereApp.kt | 0 .../com/twidere/twiderex/TwidereXActivity.kt | 0 .../twidere/twiderex/action/ComposeAction.kt | 0 .../twiderex/action/DirectMessageAction.kt | 0 .../twidere/twiderex/action/StatusActions.kt | 0 .../twiderex/component/FormattedTime.kt | 0 .../twiderex/component/HumanizedTime.kt | 0 .../twidere/twiderex/component/LoginLogo.kt | 0 .../twiderex/component/TimelineComponent.kt | 0 .../twiderex/component/UserComponent.kt | 0 .../twiderex/component/UserListComponent.kt | 0 .../twiderex/component/foundation/AppBar.kt | 0 .../foundation/AppBarNavigationButton.kt | 0 .../component/foundation/BlurImage.kt | 0 .../component/foundation/CheckboxItem.kt | 0 .../component/foundation/ColoredSwitch.kt | 0 .../component/foundation/ErrorPlaceholder.kt | 0 .../component/foundation/GridLayout.kt | 0 .../component/foundation/HorizontalDivider.kt | 0 .../foundation/InAppNotificationScaffold.kt | 0 .../component/foundation/LoadingProgress.kt | 0 .../foundation/NestedScrollScaffold.kt | 0 .../component/foundation/NetworkImage.kt | 0 .../twiderex/component/foundation/Pager.kt | 0 .../component/foundation/ParallaxLayout.kt | 0 .../component/foundation/ReorderableColumn.kt | 0 .../component/foundation/SignInButton.kt | 0 .../component/foundation/SignInScaffold.kt | 0 .../foundation/SwipeToRefreshLayout.kt | 0 .../component/foundation/TabsComponent.kt | 0 .../component/foundation/TextInput.kt | 0 .../component/foundation/VideoPlayer.kt | 0 .../component/foundation/WebComponent.kt | 0 .../foundation/navigation/BackStackEntry.kt | 0 .../navigation/NavControllerViewModel.kt | 0 .../foundation/navigation/NavHost.kt | 0 .../foundation/navigation/NavOptions.kt | 0 .../foundation/navigation/Navigator.kt | 0 .../foundation/navigation/PopUpTo.kt | 0 .../foundation/navigation/QueryString.kt | 0 .../foundation/navigation/RouteBuilder.kt | 0 .../foundation/navigation/RouteGraph.kt | 0 .../foundation/navigation/RouteMatch.kt | 0 .../foundation/navigation/RouteMatchResult.kt | 0 .../foundation/navigation/RouteParser.kt | 0 .../foundation/navigation/RouteStack.kt | 0 .../navigation/RouteStackManager.kt | 0 .../navigation/route/ComposeRoute.kt | 0 .../navigation/route/DialogRoute.kt | 0 .../foundation/navigation/route/Route.kt | 0 .../foundation/navigation/route/SceneRoute.kt | 0 .../transition/AnimatedDialogRoute.kt | 0 .../navigation/transition/AnimatedRoute.kt | 0 .../navigation/transition/DialogTransition.kt | 0 .../navigation/transition/NavTransition.kt | 0 .../twiderex/component/lazy/LazyGrid.kt | 0 .../component/lazy/LazyListController.kt | 0 .../twiderex/component/lazy/itemDivider.kt | 0 .../twiderex/component/lazy/itemHeader.kt | 0 .../component/lazy/itemsGridIndexed.kt | 0 .../twiderex/component/lazy/itemsPaging.kt | 0 .../lazy/ui/LazyUiDMConversationList.kt | 0 .../component/lazy/ui/LazyUiDMEventList.kt | 0 .../twiderex/component/lazy/ui/LazyUiList.kt | 0 .../component/lazy/ui/LazyUiListsList.kt | 0 .../lazy/ui/LazyUiStatusImageList.kt | 0 .../component/lazy/ui/LazyUiStatusList.kt | 0 .../component/lazy/ui/LazyUiUserList.kt | 0 .../lists/MastodonListsModifyComponent.kt | 0 .../lists/TwitterListsModifyComponent.kt | 0 .../component/navigation/Navigator.kt | 0 .../placeholder/UiImagePlaceholder.kt | 0 .../placeholder/UiStatusPlaceholder.kt | 0 .../placeholder/UiUserPlaceholder.kt | 0 .../component/requireAuthorization.kt | 0 .../component/settings/SettingRadioItem.kt | 0 .../twiderex/component/settings/SwitchItem.kt | 0 .../status/DetailedStatusComponent.kt | 0 .../twiderex/component/status/HtmlText.kt | 0 .../twiderex/component/status/LinkPreview.kt | 0 .../twiderex/component/status/MastodonPoll.kt | 0 .../twiderex/component/status/RoundAvatar.kt | 0 .../component/status/StatusActions.kt | 0 .../component/status/StatusDivider.kt | 0 .../component/status/StatusLineComponent.kt | 0 .../component/status/StatusMediaComponent.kt | 0 .../twiderex/component/status/StatusText.kt | 0 .../twiderex/component/status/StatusThread.kt | 0 .../status/TimelineStatusComponent.kt | 0 .../twiderex/component/status/TweetHeader.kt | 0 .../twiderex/component/status/UserAvatar.kt | 0 .../component/status/UserScreenName.kt | 0 .../component/trend/MastodonTrendItem.kt | 0 .../component/trend/TwitterTrendItem.kt | 0 .../com/twidere/twiderex/db/AppDatabase.kt | 0 .../com/twidere/twiderex/db/CacheDatabase.kt | 0 .../db/dao/DirectMessageConversationDao.kt | 0 .../twiderex/db/dao/DirectMessageEventDao.kt | 0 .../com/twidere/twiderex/db/dao/DraftDao.kt | 0 .../com/twidere/twiderex/db/dao/ListsDao.kt | 0 .../com/twidere/twiderex/db/dao/MediaDao.kt | 0 .../twiderex/db/dao/NotificationCursorDao.kt | 0 .../twiderex/db/dao/PagingTimelineDao.kt | 0 .../twidere/twiderex/db/dao/ReactionDao.kt | 0 .../com/twidere/twiderex/db/dao/SearchDao.kt | 0 .../com/twidere/twiderex/db/dao/StatusDao.kt | 0 .../twiderex/db/dao/StatusReferenceDao.kt | 0 .../com/twidere/twiderex/db/dao/TrendDao.kt | 0 .../twiderex/db/dao/TrendHistoryDao.kt | 0 .../twidere/twiderex/db/dao/UrlEntityDao.kt | 0 .../com/twidere/twiderex/db/dao/UserDao.kt | 0 .../com/twidere/twiderex/db/mapper/IStatus.kt | 0 .../twidere/twiderex/db/mapper/Mastodon.kt | 0 .../com/twidere/twiderex/db/mapper/Twitter.kt | 0 .../twiderex/db/model/DbDMConversation.kt | 0 .../twidere/twiderex/db/model/DbDMEvent.kt | 0 .../com/twidere/twiderex/db/model/DbDraft.kt | 0 .../com/twidere/twiderex/db/model/DbList.kt | 0 .../com/twidere/twiderex/db/model/DbMedia.kt | 0 .../twiderex/db/model/DbNotificationCursor.kt | 0 .../twiderex/db/model/DbPagingTimeline.kt | 0 .../com/twidere/twiderex/db/model/DbSearch.kt | 0 .../com/twidere/twiderex/db/model/DbStatus.kt | 0 .../twiderex/db/model/DbStatusReaction.kt | 0 .../twiderex/db/model/DbStatusReference.kt | 0 .../com/twidere/twiderex/db/model/DbTrend.kt | 0 .../twiderex/db/model/DbTrendHistory.kt | 0 .../twidere/twiderex/db/model/DbUrlEntity.kt | 0 .../com/twidere/twiderex/db/model/DbUser.kt | 0 .../model/converter/ComposeTypeConverter.kt | 0 .../db/model/converter/ExtraConverter.kt | 0 .../db/model/converter/MediaTypeConverter.kt | 0 .../model/converter/MicroBlogKeyConverter.kt | 0 .../NotificationCursorTypeConverter.kt | 0 .../converter/NotificationTypeConverter.kt | 0 .../model/converter/PlatformTypeConverter.kt | 0 .../db/model/converter/StringListConverter.kt | 0 .../converter/UserTimelineTypeConverter.kt | 0 .../com/twidere/twiderex/di/AndroidModule.kt | 0 .../twiderex/di/InitializerEntryPoint.kt | 0 .../com/twidere/twiderex/di/JobModule.kt | 0 .../twidere/twiderex/di/PreferenceModule.kt | 0 .../twidere/twiderex/di/RepositoryModule.kt | 0 .../com/twidere/twiderex/di/TwidereModule.kt | 0 .../twiderex/di/assisted/AssistedViewModel.kt | 0 .../twiderex/extensions/ColorExtensions.kt | 0 .../twiderex/extensions/ComposeExtensions.kt | 0 .../twiderex/extensions/ContextExtensions.kt | 0 .../twiderex/extensions/DataExtensions.kt | 0 .../twiderex/extensions/FlowExtensions.kt | 0 .../extensions/LazyPagingItemsExtensions.kt | 0 .../extensions/LocationManagerExtensions.kt | 0 .../twiderex/extensions/MastodonExtensions.kt | 0 .../twiderex/extensions/NumberExtensions.kt | 0 .../twiderex/extensions/PagingExtensions.kt | 0 .../extensions/TextFieldValueExtensions.kt | 0 .../twiderex/extensions/TimestampExtension.kt | 0 .../twiderex/extensions/ViewExtensions.kt | 0 .../twiderex/extensions/WindowExtensions.kt | 0 .../http/TwidereHttpConfigProvider.kt | 0 .../http/TwidereNetworkImageLoader.kt | 0 .../twiderex/http/TwidereServiceFactory.kt | 0 .../http/TwidereServiceInitializer.kt | 0 .../twiderex/jobs/common/DownloadMediaJob.kt | 0 .../twiderex/jobs/common/NotificationJob.kt | 0 .../twiderex/jobs/common/ShareMediaJob.kt | 0 .../twiderex/jobs/compose/ComposeJob.kt | 0 .../jobs/compose/MastodonComposeJob.kt | 0 .../jobs/compose/TwitterComposeJob.kt | 0 .../jobs/database/DeleteDbStatusJob.kt | 0 .../jobs/dm/DirectMessageDeleteJob.kt | 0 .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 0 .../twiderex/jobs/dm/DirectMessageSendJob.kt | 0 .../jobs/dm/TwitterDirectMessageSendJob.kt | 0 .../twiderex/jobs/draft/RemoveDraftJob.kt | 0 .../twiderex/jobs/draft/SaveDraftJob.kt | 0 .../twiderex/jobs/status/DeleteStatusJob.kt | 0 .../twiderex/jobs/status/LikeStatusJob.kt | 0 .../twiderex/jobs/status/MastodonVoteJob.kt | 0 .../twiderex/jobs/status/RetweetStatusJob.kt | 0 .../twidere/twiderex/jobs/status/StatusJob.kt | 0 .../jobs/status/UnRetweetStatusJob.kt | 0 .../twiderex/jobs/status/UnlikeStatusJob.kt | 0 .../com/twidere/twiderex/kmp/ExifScrambler.kt | 0 .../com/twidere/twiderex/kmp/FileResolver.kt | 0 .../twidere/twiderex/kmp/RemoteNavigator.kt | 0 .../kmp/android/AndroidExifScrambler.kt | 0 .../kmp/android/AndroidFileResolver.kt | 0 .../kmp/android/AndroidNotificationManager.kt | 0 .../kmp/android/AndroidRemoteNavigator.kt | 0 .../twidere/twiderex/model/AccountDetails.kt | 0 .../twiderex/model/AccountPreferences.kt | 0 .../com/twidere/twiderex/model/AmUser.kt | 0 .../com/twidere/twiderex/model/ComposeData.kt | 0 .../twiderex/model/DirectMessageDeleteData.kt | 0 .../twiderex/model/DirectMessageSendData.kt | 0 .../com/twidere/twiderex/model/JsonAccount.kt | 0 .../com/twidere/twiderex/model/ListType.kt | 0 .../twiderex/model/MastodonStatusType.kt | 0 .../com/twidere/twiderex/model/MediaType.kt | 0 .../twidere/twiderex/model/MicroBlogKey.kt | 0 .../twidere/twiderex/model/PlatformType.kt | 0 .../twidere/twiderex/model/StatusResult.kt | 0 .../model/adapter/AndroidAccountAdapter.kt | 0 .../twiderex/model/cred/BasicCredentials.kt | 0 .../twiderex/model/cred/Credentials.kt | 0 .../twiderex/model/cred/CredentialsType.kt | 0 .../twiderex/model/cred/EmptyCredentials.kt | 0 .../twiderex/model/cred/OAuth2Credentials.kt | 0 .../twiderex/model/cred/OAuthCredentials.kt | 0 .../twiderex/model/ui/UiDMConversation.kt | 0 .../twidere/twiderex/model/ui/UiDMEvent.kt | 0 .../com/twidere/twiderex/model/ui/UiEmoji.kt | 0 .../com/twidere/twiderex/model/ui/UiList.kt | 0 .../com/twidere/twiderex/model/ui/UiMedia.kt | 0 .../com/twidere/twiderex/model/ui/UiStatus.kt | 0 .../com/twidere/twiderex/model/ui/UiTrend.kt | 0 .../twidere/twiderex/model/ui/UiUrlEntity.kt | 0 .../com/twidere/twiderex/model/ui/UiUser.kt | 0 .../com/twidere/twiderex/navigation/Root.kt | 0 .../twiderex/navigation/RootDeepLinks.kt | 0 .../com/twidere/twiderex/navigation/Route.kt | 0 .../com/twidere/twiderex/navigation/Router.kt | 0 .../notification/AppNotificationManager.kt | 0 .../notification/InAppNotification.kt | 0 .../NotificationChannelInitializer.kt | 0 .../notification/NotificationChannelSpec.kt | 0 .../notification/NotificationInitializer.kt | 0 .../twidere/twiderex/paging/IPagingList.kt | 0 .../paging/crud/MemoryCachePagingMediator.kt | 0 .../paging/crud/MemoryCachePagingSource.kt | 0 .../twiderex/paging/crud/PagingMemoryCache.kt | 0 .../mediator/dm/BaseDirectMessageMediator.kt | 0 .../mediator/dm/DMConversationMediator.kt | 0 .../paging/mediator/dm/DMEventMediator.kt | 0 .../paging/mediator/list/ListsMediator.kt | 0 .../mediator/list/ListsMembersMediator.kt | 0 .../mediator/list/ListsTimelineMediator.kt | 0 .../mediator/list/ListsUserPagingMediator.kt | 0 .../mediator/paging/CursorPagingMediator.kt | 0 .../CursorWithCustomOrderPagingMediator.kt | 0 .../mediator/paging/MaxIdPagingMediator.kt | 0 .../paging/mediator/paging/PagingMediator.kt | 0 .../paging/PagingTimelineMediatorBase.kt | 0 .../mediator/paging/PagingWithGapMediator.kt | 0 .../mediator/search/SearchMediaMediator.kt | 0 .../mediator/search/SearchStatusMediator.kt | 0 .../status/MastodonStatusContextMediator.kt | 0 .../status/TwitterConversationMediator.kt | 0 .../mediator/timeline/HomeTimelineMediator.kt | 0 .../MastodonHashtagTimelineMediator.kt | 0 .../timeline/MentionTimelineMediator.kt | 0 .../timeline/NotificationTimelineMediator.kt | 0 .../mastodon/FederatedTimelineMediator.kt | 0 .../mastodon/LocalTimelineMediator.kt | 0 .../paging/mediator/trend/TrendMediator.kt | 0 .../mediator/user/UserFavouriteMediator.kt | 0 .../paging/mediator/user/UserMediaMediator.kt | 0 .../mediator/user/UserStatusMediator.kt | 0 .../paging/source/FollowersPagingSource.kt | 0 .../paging/source/FollowingPagingSource.kt | 0 .../source/ListsSubscribersPagingSource.kt | 0 .../MastodonSearchHashtagPagingSource.kt | 0 .../paging/source/SearchUserPagingSource.kt | 0 .../paging/source/UserPagingSource.kt | 0 .../preferences/ProvidePreferences.kt | 0 .../AppearancePreferencesSerializer.kt | 0 .../DisplayPreferencesSerializer.kt | 0 .../serializer/MiscPreferencesSerializer.kt | 0 .../NotificationPreferencesSerializer.kt | 0 .../twiderex/repository/AccountRepository.kt | 0 .../twiderex/repository/CacheRepository.kt | 0 .../repository/DirectMessageRepository.kt | 0 .../twiderex/repository/DraftRepository.kt | 0 .../twiderex/repository/ListsRepository.kt | 0 .../repository/ListsUsersRepository.kt | 0 .../twiderex/repository/MediaRepository.kt | 0 .../repository/NotificationRepository.kt | 0 .../twiderex/repository/ReactionRepository.kt | 0 .../twiderex/repository/SearchRepository.kt | 0 .../twiderex/repository/StatusRepository.kt | 0 .../twiderex/repository/TimelineRepository.kt | 0 .../twiderex/repository/TrendRepository.kt | 0 .../twiderex/repository/UserRepository.kt | 0 .../twidere/twiderex/scenes/DraftListScene.kt | 0 .../com/twidere/twiderex/scenes/HomeScene.kt | 0 .../com/twidere/twiderex/scenes/MediaScene.kt | 0 .../twidere/twiderex/scenes/PureMediaScene.kt | 0 .../twidere/twiderex/scenes/SignInScene.kt | 0 .../twidere/twiderex/scenes/StatusScene.kt | 0 .../twiderex/scenes/compose/ComposeScene.kt | 0 .../compose/ComposeSearchHashtagScene.kt | 0 .../scenes/compose/ComposeSearchUserScene.kt | 0 .../scenes/dm/DMConversationListScene.kt | 0 .../twiderex/scenes/dm/DMConversationScene.kt | 0 .../scenes/dm/DMNewConversationScene.kt | 0 .../scenes/home/AllNotificationItem.kt | 0 .../scenes/home/DMConversationListItem.kt | 0 .../scenes/home/DraftNavigationItem.kt | 0 .../twidere/twiderex/scenes/home/HomeMenus.kt | 0 .../scenes/home/HomeNavigationItem.kt | 0 .../twiderex/scenes/home/HomeTimelineItem.kt | 0 .../scenes/home/ListsNavigationItem.kt | 0 .../twidere/twiderex/scenes/home/MeItem.kt | 0 .../twiderex/scenes/home/MentionItem.kt | 0 .../twiderex/scenes/home/NotificationItem.kt | 0 .../twiderex/scenes/home/SearchItem.kt | 0 .../home/mastodon/FederatedTimelineItem.kt | 0 .../scenes/home/mastodon/LocalTimelineItem.kt | 0 .../home/mastodon/MastodonNotificationItem.kt | 0 .../scenes/lists/ListsAddMembersScene.kt | 0 .../scenes/lists/ListsMembersScene.kt | 0 .../twiderex/scenes/lists/ListsScene.kt | 0 .../scenes/lists/ListsSubscribersScene.kt | 0 .../scenes/lists/ListsTimelineScene.kt | 0 .../platform/MastodonListsCreateDialog.kt | 0 .../lists/platform/MastodonListsEditDialog.kt | 0 .../lists/platform/TwitterListsCreateScene.kt | 0 .../lists/platform/TwitterListsEditScene.kt | 0 .../scenes/mastodon/MastodonHashtagScene.kt | 0 .../scenes/mastodon/MastodonSignInScene.kt | 0 .../scenes/mastodon/MastodonWebSignInScene.kt | 0 .../scenes/search/SearchInputScene.kt | 0 .../twiderex/scenes/search/SearchScene.kt | 0 .../search/tabs/MastodonSearchHashtagItem.kt | 0 .../scenes/search/tabs/SearchSceneItem.kt | 0 .../scenes/search/tabs/SearchTweetsItem.kt | 0 .../scenes/search/tabs/SearchUserItem.kt | 0 .../search/tabs/TwitterSearchMediaItem.kt | 0 .../twiderex/scenes/settings/AboutScene.kt | 0 .../scenes/settings/AccountManagementScene.kt | 0 .../settings/AccountNotificationScene.kt | 0 .../scenes/settings/AppearanceScene.kt | 0 .../twiderex/scenes/settings/DisplayScene.kt | 0 .../twiderex/scenes/settings/LayoutScene.kt | 0 .../twiderex/scenes/settings/MiscScene.kt | 0 .../scenes/settings/NotificationScene.kt | 0 .../twiderex/scenes/settings/SettingsScene.kt | 0 .../twiderex/scenes/settings/StorageScene.kt | 0 .../scenes/twitter/TwitterSigninScene.kt | 0 .../scenes/twitter/TwitterWebSignInScene.kt | 0 .../scenes/twitter/user/TwitterUserScene.kt | 0 .../twiderex/scenes/user/FollowersScene.kt | 0 .../twiderex/scenes/user/FollowingScene.kt | 0 .../twidere/twiderex/scenes/user/UserScene.kt | 0 .../service/AccountAuthenticatorService.kt | 0 .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 0 .../kotlin/com/twidere/twiderex/ui/Color.kt | 0 .../kotlin/com/twidere/twiderex/ui/Shape.kt | 0 .../kotlin/com/twidere/twiderex/ui/Theme.kt | 0 .../twiderex/utils/CustomTabSignInChannel.kt | 0 .../com/twidere/twiderex/utils/Event.kt | 0 .../twiderex/utils/FileProviderHelper.kt | 0 .../twidere/twiderex/utils/HttpErrorCodes.kt | 0 .../kotlin/com/twidere/twiderex/utils/Json.kt | 0 .../twiderex/utils/MastodonEmojiCache.kt | 0 .../utils/OrientationSensorManager.kt | 0 .../twiderex/utils/PlatformResolver.kt | 0 .../twiderex/utils/TwitterErrorHandling.kt | 0 .../utils/TwitterWebJavascriptInterface.kt | 0 .../utils/video/CacheDataSourceFactory.kt | 0 .../twiderex/utils/video/VideoCache.kt | 0 .../twidere/twiderex/utils/video/VideoPool.kt | 0 .../twiderex/view/LollipopFixWebView.kt | 0 .../viewmodel/ActiveAccountViewModel.kt | 0 .../twiderex/viewmodel/DraftViewModel.kt | 0 .../twiderex/viewmodel/MediaViewModel.kt | 0 .../twiderex/viewmodel/PureMediaViewModel.kt | 0 .../twiderex/viewmodel/StatusViewModel.kt | 0 .../compose/ComposeSearchUserViewModel.kt | 0 .../viewmodel/compose/ComposeViewModel.kt | 0 .../MastodonComposeSearchHashtagViewModel.kt | 0 .../viewmodel/dm/DMConversationViewModel.kt | 0 .../twiderex/viewmodel/dm/DMEventViewModel.kt | 0 .../dm/DMNewConversationViewModel.kt | 0 .../lists/ListsSearchUserViewModel.kt | 0 .../viewmodel/lists/ListsTimelineViewModel.kt | 0 .../viewmodel/lists/ListsUserViewModel.kt | 0 .../viewmodel/lists/ListsViewModel.kt | 0 .../mastodon/MastodonHashtagViewModel.kt | 0 .../MastodonSearchHashtagViewModel.kt | 0 .../mastodon/MastodonSignInViewModel.kt | 0 .../viewmodel/search/SearchInputViewModel.kt | 0 .../viewmodel/search/SearchSaveViewModel.kt | 0 .../viewmodel/search/SearchTweetsViewModel.kt | 0 .../viewmodel/search/SearchUserViewModel.kt | 0 .../settings/AccountNotificationViewModel.kt | 0 .../viewmodel/settings/AppearanceViewModel.kt | 0 .../viewmodel/settings/DisplayViewModel.kt | 0 .../viewmodel/settings/LayoutViewModel.kt | 0 .../viewmodel/settings/MiscViewModel.kt | 0 .../settings/NotificationViewModel.kt | 0 .../viewmodel/settings/StorageViewModel.kt | 0 .../timeline/HomeTimelineViewModel.kt | 0 .../timeline/MentionsTimelineViewModel.kt | 0 .../timeline/NotificationTimelineViewModel.kt | 0 .../viewmodel/timeline/TimelineViewModel.kt | 0 .../mastodon/FederatedTimelineViewModel.kt | 0 .../mastodon/LocalTimelineViewModel.kt | 0 .../viewmodel/trend/TrendViewModel.kt | 0 .../twitter/TwitterSignInViewModel.kt | 0 .../search/TwitterSearchMediaViewModel.kt | 0 .../twitter/user/TwitterUserViewModel.kt | 0 .../viewmodel/user/FollowersViewModel.kt | 0 .../viewmodel/user/FollowingViewModel.kt | 0 .../user/UserFavouriteTimelineViewModel.kt | 0 .../viewmodel/user/UserListViewModel.kt | 0 .../user/UserMediaTimelineViewModel.kt | 0 .../viewmodel/user/UserTimelineViewModel.kt | 0 .../twiderex/viewmodel/user/UserViewModel.kt | 0 .../twiderex/worker/DownloadMediaWorker.kt | 0 .../twiderex/worker/NotificationWorker.kt | 0 .../twiderex/worker/ShareMediaWorker.kt | 0 .../twiderex/worker/compose/ComposeWorker.kt | 0 .../worker/compose/MastodonComposeWorker.kt | 0 .../worker/compose/TwitterComposeWorker.kt | 0 .../worker/database/DeleteDbStatusWorker.kt | 0 .../worker/dm/DirectMessageDeleteWorker.kt | 0 .../worker/dm/DirectMessageFetchWorker.kt | 0 .../worker/dm/DirectMessageInitializer.kt | 0 .../worker/dm/DirectMessageSendWorker.kt | 0 .../dm/TwitterDirectMessageSendWorker.kt | 0 .../worker/draft/RemoveDraftWorker.kt | 0 .../twiderex/worker/draft/SaveDraftWorker.kt | 0 .../worker/status/DeleteStatusWorker.kt | 0 .../twiderex/worker/status/LikeWorker.kt | 0 .../worker/status/MastodonVoteWorker.kt | 0 .../twiderex/worker/status/RetweetWorker.kt | 0 .../twiderex/worker/status/StatusWorker.kt | 0 .../twiderex/worker/status/UnLikeWorker.kt | 0 .../twiderex/worker/status/UnRetweetWorker.kt | 0 .../worker/status/UpdateStatusWorker.kt | 0 .../main/proto/AppearancePreferences.proto | 0 .../src/main/proto/DisplayPreferences.proto | 0 .../src/main/proto/MiscPreferences.proto | 0 .../main/proto/NotificationPreferences.proto | 0 .../res-localized/drawable/ic_drafts_more.xml | 0 .../res-localized/drawable/ic_keyboard.xml | 0 .../res-localized/drawable/ic_trash_can.xml | 0 .../res-localized/values-ar-rSA/strings.xml | 0 .../res-localized/values-ca-rES/strings.xml | 0 .../res-localized/values-de-rDE/strings.xml | 0 .../res-localized/values-en-rUS/strings.xml | 0 .../res-localized/values-es-rES/strings.xml | 0 .../res-localized/values-fr-rFR/strings.xml | 0 .../res-localized/values-it-rIT/strings.xml | 0 .../res-localized/values-ja-rJP/strings.xml | 0 .../res-localized/values-ko-rKR/strings.xml | 0 .../res-localized/values-pt-rBR/strings.xml | 0 .../res-localized/values-si-rLK/strings.xml | 0 .../res-localized/values-tr-rTR/strings.xml | 0 .../res-localized/values-vi-rVN/strings.xml | 0 .../res-localized/values-zh-rCN/strings.xml | 0 .../res-localized/values-zh-rTW/strings.xml | 0 .../drawable-anydpi-v24/ic_notification.xml | 0 .../ic_mastodon_logo_blue.png | Bin .../ic_mastodon_logo_white.png | Bin .../res/drawable-hdpi/featured_graphics.png | Bin .../res/drawable-hdpi/ic_notification.png | Bin .../ic_profile_image_twidere.png | Bin .../ic_mastodon_logo_blue.png | Bin .../ic_mastodon_logo_white.png | Bin .../res/drawable-mdpi/featured_graphics.png | Bin .../res/drawable-mdpi/ic_notification.png | Bin .../ic_profile_image_twidere.png | Bin .../main/res/drawable-night/ic_login_logo.xml | 0 .../drawable-v24/ic_mastodon_logo_blue.xml | 0 .../drawable-v24/ic_mastodon_logo_white.xml | 0 .../ic_mastodon_logo_blue.png | Bin .../ic_mastodon_logo_white.png | Bin .../res/drawable-xhdpi/featured_graphics.png | Bin .../res/drawable-xhdpi/ic_notification.png | Bin .../ic_profile_image_twidere.png | Bin .../ic_mastodon_logo_blue.png | Bin .../ic_mastodon_logo_white.png | Bin .../res/drawable-xxhdpi/featured_graphics.png | Bin .../res/drawable-xxhdpi/ic_notification.png | Bin .../ic_profile_image_twidere.png | Bin .../ic_mastodon_logo_blue.png | Bin .../ic_mastodon_logo_white.png | Bin .../drawable-xxxhdpi/featured_graphics.png | Bin .../ic_profile_image_twidere.png | Bin .../main/res/drawable/ic_about_gray_logo.xml | 0 .../drawable/ic_about_gray_logo_shadow.xml | 0 .../src/main/res/drawable/ic_add.xml | 0 .../src/main/res/drawable/ic_add_colored.xml | 0 .../drawable/ic_adjustments_horizontal.xml | 0 .../src/main/res/drawable/ic_alert.xml | 0 .../main/res/drawable/ic_alert_octagon.xml | 0 .../main/res/drawable/ic_alert_triangle.xml | 0 .../src/main/res/drawable/ic_at_sign.xml | 0 .../src/main/res/drawable/ic_bell.xml | 0 .../src/main/res/drawable/ic_bell_ringing.xml | 0 .../src/main/res/drawable/ic_blockquote.xml | 0 .../src/main/res/drawable/ic_browser.xml | 0 .../src/main/res/drawable/ic_camera.xml | 0 .../main/res/drawable/ic_corner_up_left.xml | 0 .../src/main/res/drawable/ic_cw.xml | 0 .../src/main/res/drawable/ic_database.xml | 0 .../main/res/drawable/ic_delete_colored.xml | 0 .../main/res/drawable/ic_device_floppy.xml | 0 .../main/res/drawable/ic_dots_vertical.xml | 0 .../src/main/res/drawable/ic_draft_number.xml | 0 .../src/main/res/drawable/ic_empty_column.xml | 0 .../src/main/res/drawable/ic_empty_list.xml | 0 .../src/main/res/drawable/ic_empty_status.xml | 0 .../src/main/res/drawable/ic_expand_more.xml | 0 .../src/main/res/drawable/ic_eye_off.xml | 0 .../src/main/res/drawable/ic_feather.xml | 0 .../src/main/res/drawable/ic_filter.xml | 0 .../src/main/res/drawable/ic_float_left.xml | 0 .../src/main/res/drawable/ic_gif.xml | 0 .../src/main/res/drawable/ic_github.xml | 0 .../src/main/res/drawable/ic_globe.xml | 0 .../main/res/drawable/ic_gray_logo_shadow.xml | 0 .../src/main/res/drawable/ic_hash.xml | 0 .../src/main/res/drawable/ic_heart.xml | 0 .../src/main/res/drawable/ic_home.xml | 0 .../src/main/res/drawable/ic_info_circle.xml | 0 .../main/res/drawable/ic_layout_sidebar.xml | 0 .../src/main/res/drawable/ic_lists.xml | 0 .../src/main/res/drawable/ic_lock.xml | 0 .../src/main/res/drawable/ic_lock_open.xml | 0 .../src/main/res/drawable/ic_login_logo.xml | 0 .../src/main/res/drawable/ic_mail.xml | 0 .../src/main/res/drawable/ic_map_pin.xml | 0 .../main/res/drawable/ic_mastodon_badge.xml | 0 .../main/res/drawable/ic_message_circle.xml | 0 .../src/main/res/drawable/ic_mood_smile.xml | 0 .../src/main/res/drawable/ic_note.xml | 0 .../src/main/res/drawable/ic_photo.xml | 0 .../src/main/res/drawable/ic_planet.xml | 0 .../src/main/res/drawable/ic_poll.xml | 0 .../src/main/res/drawable/ic_refresh.xml | 0 .../src/main/res/drawable/ic_repeat.xml | 0 .../src/main/res/drawable/ic_search.xml | 0 .../src/main/res/drawable/ic_send.xml | 0 .../src/main/res/drawable/ic_send_thread.xml | 0 .../res/drawable/ic_settings_notification.xml | 0 .../src/main/res/drawable/ic_share.xml | 0 .../src/main/res/drawable/ic_shirt.xml | 0 .../src/main/res/drawable/ic_telegram.xml | 0 .../src/main/res/drawable/ic_template.xml | 0 .../src/main/res/drawable/ic_thread_mode.xml | 0 .../drawable/ic_triangle_square_circle.xml | 0 .../src/main/res/drawable/ic_twitter.xml | 0 .../main/res/drawable/ic_twitter_badge.xml | 0 .../res/drawable/ic_twitter_logo_white.xml | 0 .../src/main/res/drawable/ic_user.xml | 0 .../main/res/drawable/ic_user_exclamation.xml | 0 .../src/main/res/drawable/ic_user_plus.xml | 0 .../src/main/res/drawable/ic_users.xml | 0 .../src/main/res/drawable/ic_x.xml | 0 .../res/layout/exo_player_control_view.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_background.png | Bin .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_background.png | Bin .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_background.png | Bin .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_background.png | Bin .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_background.png | Bin .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../src/main/res/values/strings.xml | 0 .../res/values/strings_untranslatable.xml | 0 .../src/main/res/values/themes.xml | 0 .../src/main/res/xml/authenticator.xml | 0 .../src/main/res/xml/filer_provider_path.xml | 0 .../com/twidere/twiderex/ExampleUnitTest.kt | 0 .../com/twidere/twiderex/mock/MockCenter.kt | 0 .../twiderex/mock/db/MockCacheDatabse.kt | 0 .../db/MockDirectMessageConversationDao.kt | 0 .../mock/db/MockDirectMessageEventDao.kt | 0 .../twidere/twiderex/mock/db/MockListsDao.kt | 0 .../twidere/twiderex/mock/db/MockMediaDao.kt | 0 .../twidere/twiderex/mock/db/MockTrendDao.kt | 0 .../twiderex/mock/db/MockUrlEntityDao.kt | 0 .../twidere/twiderex/mock/db/MockUserDao.kt | 0 .../twiderex/mock/service/MockListsService.kt | 0 .../twiderex/mock/service/MockTrendService.kt | 0 .../paging/crud/MemoryCachePagingMediator.kt | 0 .../crud/MemoryCachePagingSourceTest.kt | 0 .../paging/crud/PagingMemoryCacheTest.kt | 0 .../paging/lists/ListsMediatorTest.kt | 0 .../lists/ListsUserPagingMediatorTest.kt | 0 .../paging/trend/TrendMediatorTest.kt | 0 .../repository/ListsRepositoryTest.kt | 0 .../repository/ListsUsersRepositoryTest.kt | 0 .../twiderex/viewmodel/ViewModelTestBase.kt | 0 .../lists/ListsCreateViewModelTest.kt | 0 .../lists/ListsModifyViewModelTest.kt | 0 .../viewmodel/lists/ListsViewModelTest.kt | 0 .../precompose/navigation/RouteParserTest.kt | 0 .../org.mockito.plugins.MockMaker | 0 buildSrc/src/main/kotlin/Dependencies.kt | 1 + buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 46 ++++++++++++++++++ desktop/build.gradle.kts | 38 +++++++++++++++ .../kotlin/com/twidere/twiderex/main.kt | 28 +++++++++++ settings.gradle.kts | 4 +- 641 files changed, 117 insertions(+), 1 deletion(-) rename {app => android}/.gitignore (100%) rename {app => android}/build.gradle.kts (100%) rename {app => android}/proguard-rules.pro (100%) rename {app => android}/schemas/com.twidere.twiderex.db.AppDatabase/1.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.AppDatabase/2.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.AppDatabase/3.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/10.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/11.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/12.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/13.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/14.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/15.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/16.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/17.json (100%) rename {app => android}/schemas/com.twidere.twiderex.db.CacheDatabase/18.json (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt (100%) rename {app => android}/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt (100%) rename {app => android}/src/main/AndroidManifest.xml (100%) rename {app => android}/src/main/ic_launcher-playstore.png (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/Consts.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ComposeData.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/DirectMessageDeleteData.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/DirectMessageSendData.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/JsonAccount.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ListType.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/MastodonStatusType.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/MediaType.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/PlatformType.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/StatusResult.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/adapter/AndroidAccountAdapter.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmoji.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/ui/Color.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/Event.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/Json.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/OrientationSensorManager.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt (100%) rename {app => android}/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt (100%) rename {app => android}/src/main/proto/AppearancePreferences.proto (100%) rename {app => android}/src/main/proto/DisplayPreferences.proto (100%) rename {app => android}/src/main/proto/MiscPreferences.proto (100%) rename {app => android}/src/main/proto/NotificationPreferences.proto (100%) rename {app => android}/src/main/res-localized/drawable/ic_drafts_more.xml (100%) rename {app => android}/src/main/res-localized/drawable/ic_keyboard.xml (100%) rename {app => android}/src/main/res-localized/drawable/ic_trash_can.xml (100%) rename {app => android}/src/main/res-localized/values-ar-rSA/strings.xml (100%) rename {app => android}/src/main/res-localized/values-ca-rES/strings.xml (100%) rename {app => android}/src/main/res-localized/values-de-rDE/strings.xml (100%) rename {app => android}/src/main/res-localized/values-en-rUS/strings.xml (100%) rename {app => android}/src/main/res-localized/values-es-rES/strings.xml (100%) rename {app => android}/src/main/res-localized/values-fr-rFR/strings.xml (100%) rename {app => android}/src/main/res-localized/values-it-rIT/strings.xml (100%) rename {app => android}/src/main/res-localized/values-ja-rJP/strings.xml (100%) rename {app => android}/src/main/res-localized/values-ko-rKR/strings.xml (100%) rename {app => android}/src/main/res-localized/values-pt-rBR/strings.xml (100%) rename {app => android}/src/main/res-localized/values-si-rLK/strings.xml (100%) rename {app => android}/src/main/res-localized/values-tr-rTR/strings.xml (100%) rename {app => android}/src/main/res-localized/values-vi-rVN/strings.xml (100%) rename {app => android}/src/main/res-localized/values-zh-rCN/strings.xml (100%) rename {app => android}/src/main/res-localized/values-zh-rTW/strings.xml (100%) rename {app => android}/src/main/res/drawable-anydpi-v24/ic_notification.xml (100%) rename {app => android}/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_blue.png (100%) rename {app => android}/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_white.png (100%) rename {app => android}/src/main/res/drawable-hdpi/featured_graphics.png (100%) rename {app => android}/src/main/res/drawable-hdpi/ic_notification.png (100%) rename {app => android}/src/main/res/drawable-hdpi/ic_profile_image_twidere.png (100%) rename {app => android}/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png (100%) rename {app => android}/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_white.png (100%) rename {app => android}/src/main/res/drawable-mdpi/featured_graphics.png (100%) rename {app => android}/src/main/res/drawable-mdpi/ic_notification.png (100%) rename {app => android}/src/main/res/drawable-mdpi/ic_profile_image_twidere.png (100%) rename {app => android}/src/main/res/drawable-night/ic_login_logo.xml (100%) rename {app => android}/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml (100%) rename {app => android}/src/main/res/drawable-v24/ic_mastodon_logo_white.xml (100%) rename {app => android}/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png (100%) rename {app => android}/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_white.png (100%) rename {app => android}/src/main/res/drawable-xhdpi/featured_graphics.png (100%) rename {app => android}/src/main/res/drawable-xhdpi/ic_notification.png (100%) rename {app => android}/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png (100%) rename {app => android}/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png (100%) rename {app => android}/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_white.png (100%) rename {app => android}/src/main/res/drawable-xxhdpi/featured_graphics.png (100%) rename {app => android}/src/main/res/drawable-xxhdpi/ic_notification.png (100%) rename {app => android}/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png (100%) rename {app => android}/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png (100%) rename {app => android}/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png (100%) rename {app => android}/src/main/res/drawable-xxxhdpi/featured_graphics.png (100%) rename {app => android}/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png (100%) rename {app => android}/src/main/res/drawable/ic_about_gray_logo.xml (100%) rename {app => android}/src/main/res/drawable/ic_about_gray_logo_shadow.xml (100%) rename {app => android}/src/main/res/drawable/ic_add.xml (100%) rename {app => android}/src/main/res/drawable/ic_add_colored.xml (100%) rename {app => android}/src/main/res/drawable/ic_adjustments_horizontal.xml (100%) rename {app => android}/src/main/res/drawable/ic_alert.xml (100%) rename {app => android}/src/main/res/drawable/ic_alert_octagon.xml (100%) rename {app => android}/src/main/res/drawable/ic_alert_triangle.xml (100%) rename {app => android}/src/main/res/drawable/ic_at_sign.xml (100%) rename {app => android}/src/main/res/drawable/ic_bell.xml (100%) rename {app => android}/src/main/res/drawable/ic_bell_ringing.xml (100%) rename {app => android}/src/main/res/drawable/ic_blockquote.xml (100%) rename {app => android}/src/main/res/drawable/ic_browser.xml (100%) rename {app => android}/src/main/res/drawable/ic_camera.xml (100%) rename {app => android}/src/main/res/drawable/ic_corner_up_left.xml (100%) rename {app => android}/src/main/res/drawable/ic_cw.xml (100%) rename {app => android}/src/main/res/drawable/ic_database.xml (100%) rename {app => android}/src/main/res/drawable/ic_delete_colored.xml (100%) rename {app => android}/src/main/res/drawable/ic_device_floppy.xml (100%) rename {app => android}/src/main/res/drawable/ic_dots_vertical.xml (100%) rename {app => android}/src/main/res/drawable/ic_draft_number.xml (100%) rename {app => android}/src/main/res/drawable/ic_empty_column.xml (100%) rename {app => android}/src/main/res/drawable/ic_empty_list.xml (100%) rename {app => android}/src/main/res/drawable/ic_empty_status.xml (100%) rename {app => android}/src/main/res/drawable/ic_expand_more.xml (100%) rename {app => android}/src/main/res/drawable/ic_eye_off.xml (100%) rename {app => android}/src/main/res/drawable/ic_feather.xml (100%) rename {app => android}/src/main/res/drawable/ic_filter.xml (100%) rename {app => android}/src/main/res/drawable/ic_float_left.xml (100%) rename {app => android}/src/main/res/drawable/ic_gif.xml (100%) rename {app => android}/src/main/res/drawable/ic_github.xml (100%) rename {app => android}/src/main/res/drawable/ic_globe.xml (100%) rename {app => android}/src/main/res/drawable/ic_gray_logo_shadow.xml (100%) rename {app => android}/src/main/res/drawable/ic_hash.xml (100%) rename {app => android}/src/main/res/drawable/ic_heart.xml (100%) rename {app => android}/src/main/res/drawable/ic_home.xml (100%) rename {app => android}/src/main/res/drawable/ic_info_circle.xml (100%) rename {app => android}/src/main/res/drawable/ic_layout_sidebar.xml (100%) rename {app => android}/src/main/res/drawable/ic_lists.xml (100%) rename {app => android}/src/main/res/drawable/ic_lock.xml (100%) rename {app => android}/src/main/res/drawable/ic_lock_open.xml (100%) rename {app => android}/src/main/res/drawable/ic_login_logo.xml (100%) rename {app => android}/src/main/res/drawable/ic_mail.xml (100%) rename {app => android}/src/main/res/drawable/ic_map_pin.xml (100%) rename {app => android}/src/main/res/drawable/ic_mastodon_badge.xml (100%) rename {app => android}/src/main/res/drawable/ic_message_circle.xml (100%) rename {app => android}/src/main/res/drawable/ic_mood_smile.xml (100%) rename {app => android}/src/main/res/drawable/ic_note.xml (100%) rename {app => android}/src/main/res/drawable/ic_photo.xml (100%) rename {app => android}/src/main/res/drawable/ic_planet.xml (100%) rename {app => android}/src/main/res/drawable/ic_poll.xml (100%) rename {app => android}/src/main/res/drawable/ic_refresh.xml (100%) rename {app => android}/src/main/res/drawable/ic_repeat.xml (100%) rename {app => android}/src/main/res/drawable/ic_search.xml (100%) rename {app => android}/src/main/res/drawable/ic_send.xml (100%) rename {app => android}/src/main/res/drawable/ic_send_thread.xml (100%) rename {app => android}/src/main/res/drawable/ic_settings_notification.xml (100%) rename {app => android}/src/main/res/drawable/ic_share.xml (100%) rename {app => android}/src/main/res/drawable/ic_shirt.xml (100%) rename {app => android}/src/main/res/drawable/ic_telegram.xml (100%) rename {app => android}/src/main/res/drawable/ic_template.xml (100%) rename {app => android}/src/main/res/drawable/ic_thread_mode.xml (100%) rename {app => android}/src/main/res/drawable/ic_triangle_square_circle.xml (100%) rename {app => android}/src/main/res/drawable/ic_twitter.xml (100%) rename {app => android}/src/main/res/drawable/ic_twitter_badge.xml (100%) rename {app => android}/src/main/res/drawable/ic_twitter_logo_white.xml (100%) rename {app => android}/src/main/res/drawable/ic_user.xml (100%) rename {app => android}/src/main/res/drawable/ic_user_exclamation.xml (100%) rename {app => android}/src/main/res/drawable/ic_user_plus.xml (100%) rename {app => android}/src/main/res/drawable/ic_users.xml (100%) rename {app => android}/src/main/res/drawable/ic_x.xml (100%) rename {app => android}/src/main/res/layout/exo_player_control_view.xml (100%) rename {app => android}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {app => android}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {app => android}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {app => android}/src/main/res/mipmap-hdpi/ic_launcher_background.png (100%) rename {app => android}/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {app => android}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {app => android}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {app => android}/src/main/res/mipmap-mdpi/ic_launcher_background.png (100%) rename {app => android}/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {app => android}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {app => android}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {app => android}/src/main/res/mipmap-xhdpi/ic_launcher_background.png (100%) rename {app => android}/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {app => android}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {app => android}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {app => android}/src/main/res/mipmap-xxhdpi/ic_launcher_background.png (100%) rename {app => android}/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {app => android}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {app => android}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {app => android}/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png (100%) rename {app => android}/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {app => android}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {app => android}/src/main/res/values/strings.xml (100%) rename {app => android}/src/main/res/values/strings_untranslatable.xml (100%) rename {app => android}/src/main/res/values/themes.xml (100%) rename {app => android}/src/main/res/xml/authenticator.xml (100%) rename {app => android}/src/main/res/xml/filer_provider_path.xml (100%) rename {app => android}/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/MockCenter.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt (100%) rename {app => android}/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt (100%) rename {app => android}/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt (100%) rename {app => android}/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%) create mode 100644 common/build.gradle.kts create mode 100644 desktop/build.gradle.kts create mode 100644 desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt diff --git a/app/.gitignore b/android/.gitignore similarity index 100% rename from app/.gitignore rename to android/.gitignore diff --git a/app/build.gradle.kts b/android/build.gradle.kts similarity index 100% rename from app/build.gradle.kts rename to android/build.gradle.kts diff --git a/app/proguard-rules.pro b/android/proguard-rules.pro similarity index 100% rename from app/proguard-rules.pro rename to android/proguard-rules.pro diff --git a/app/schemas/com.twidere.twiderex.db.AppDatabase/1.json b/android/schemas/com.twidere.twiderex.db.AppDatabase/1.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.AppDatabase/1.json rename to android/schemas/com.twidere.twiderex.db.AppDatabase/1.json diff --git a/app/schemas/com.twidere.twiderex.db.AppDatabase/2.json b/android/schemas/com.twidere.twiderex.db.AppDatabase/2.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.AppDatabase/2.json rename to android/schemas/com.twidere.twiderex.db.AppDatabase/2.json diff --git a/app/schemas/com.twidere.twiderex.db.AppDatabase/3.json b/android/schemas/com.twidere.twiderex.db.AppDatabase/3.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.AppDatabase/3.json rename to android/schemas/com.twidere.twiderex.db.AppDatabase/3.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/10.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/10.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/10.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/10.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/11.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/11.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/11.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/11.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/12.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/12.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/12.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/12.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/13.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/13.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/13.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/13.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/14.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/14.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/14.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/14.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/15.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/15.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/15.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/15.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/16.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/16.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/16.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/16.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/17.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/17.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/17.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/17.json diff --git a/app/schemas/com.twidere.twiderex.db.CacheDatabase/18.json b/android/schemas/com.twidere.twiderex.db.CacheDatabase/18.json similarity index 100% rename from app/schemas/com.twidere.twiderex.db.CacheDatabase/18.json rename to android/schemas/com.twidere.twiderex.db.CacheDatabase/18.json diff --git a/app/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt b/android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt b/android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt rename to android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt b/android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt rename to android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt b/android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt diff --git a/app/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt b/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt similarity index 100% rename from app/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt diff --git a/app/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml similarity index 100% rename from app/src/main/AndroidManifest.xml rename to android/src/main/AndroidManifest.xml diff --git a/app/src/main/ic_launcher-playstore.png b/android/src/main/ic_launcher-playstore.png similarity index 100% rename from app/src/main/ic_launcher-playstore.png rename to android/src/main/ic_launcher-playstore.png diff --git a/app/src/main/kotlin/com/twidere/twiderex/Consts.kt b/android/src/main/kotlin/com/twidere/twiderex/Consts.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/Consts.kt rename to android/src/main/kotlin/com/twidere/twiderex/Consts.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt rename to android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt rename to android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/android/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt rename to android/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/android/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt rename to android/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt b/android/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt rename to android/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt b/android/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt b/android/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt b/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt b/android/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt b/android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt b/android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt b/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt b/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt rename to android/src/main/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt rename to android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt rename to android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt rename to android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt rename to android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt b/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt rename to android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt b/android/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt b/android/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt b/android/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ComposeData.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ComposeData.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ComposeData.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ComposeData.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/DirectMessageDeleteData.kt b/android/src/main/kotlin/com/twidere/twiderex/model/DirectMessageDeleteData.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/DirectMessageDeleteData.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/DirectMessageDeleteData.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/DirectMessageSendData.kt b/android/src/main/kotlin/com/twidere/twiderex/model/DirectMessageSendData.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/DirectMessageSendData.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/DirectMessageSendData.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/JsonAccount.kt b/android/src/main/kotlin/com/twidere/twiderex/model/JsonAccount.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/JsonAccount.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/JsonAccount.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ListType.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ListType.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ListType.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ListType.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/MastodonStatusType.kt b/android/src/main/kotlin/com/twidere/twiderex/model/MastodonStatusType.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/MastodonStatusType.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/MastodonStatusType.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/MediaType.kt b/android/src/main/kotlin/com/twidere/twiderex/model/MediaType.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/MediaType.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/MediaType.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt b/android/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/PlatformType.kt b/android/src/main/kotlin/com/twidere/twiderex/model/PlatformType.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/PlatformType.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/PlatformType.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/StatusResult.kt b/android/src/main/kotlin/com/twidere/twiderex/model/StatusResult.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/StatusResult.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/StatusResult.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/adapter/AndroidAccountAdapter.kt b/android/src/main/kotlin/com/twidere/twiderex/model/adapter/AndroidAccountAdapter.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/adapter/AndroidAccountAdapter.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/adapter/AndroidAccountAdapter.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt b/android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmoji.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmoji.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmoji.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmoji.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt b/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt rename to android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt rename to android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt rename to android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt rename to android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt rename to android/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt rename to android/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt rename to android/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt rename to android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt b/android/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt rename to android/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/android/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt rename to android/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt b/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt b/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt b/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt b/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt rename to android/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt rename to android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt b/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt rename to android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt rename to android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/ui/Color.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Color.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/ui/Color.kt rename to android/src/main/kotlin/com/twidere/twiderex/ui/Color.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt rename to android/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt rename to android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/Event.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/Event.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/Event.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/Event.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/OrientationSensorManager.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/OrientationSensorManager.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/OrientationSensorManager.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/OrientationSensorManager.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt rename to android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt b/android/src/main/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt rename to android/src/main/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt rename to android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt similarity index 100% rename from app/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt rename to android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt diff --git a/app/src/main/proto/AppearancePreferences.proto b/android/src/main/proto/AppearancePreferences.proto similarity index 100% rename from app/src/main/proto/AppearancePreferences.proto rename to android/src/main/proto/AppearancePreferences.proto diff --git a/app/src/main/proto/DisplayPreferences.proto b/android/src/main/proto/DisplayPreferences.proto similarity index 100% rename from app/src/main/proto/DisplayPreferences.proto rename to android/src/main/proto/DisplayPreferences.proto diff --git a/app/src/main/proto/MiscPreferences.proto b/android/src/main/proto/MiscPreferences.proto similarity index 100% rename from app/src/main/proto/MiscPreferences.proto rename to android/src/main/proto/MiscPreferences.proto diff --git a/app/src/main/proto/NotificationPreferences.proto b/android/src/main/proto/NotificationPreferences.proto similarity index 100% rename from app/src/main/proto/NotificationPreferences.proto rename to android/src/main/proto/NotificationPreferences.proto diff --git a/app/src/main/res-localized/drawable/ic_drafts_more.xml b/android/src/main/res-localized/drawable/ic_drafts_more.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_drafts_more.xml rename to android/src/main/res-localized/drawable/ic_drafts_more.xml diff --git a/app/src/main/res-localized/drawable/ic_keyboard.xml b/android/src/main/res-localized/drawable/ic_keyboard.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_keyboard.xml rename to android/src/main/res-localized/drawable/ic_keyboard.xml diff --git a/app/src/main/res-localized/drawable/ic_trash_can.xml b/android/src/main/res-localized/drawable/ic_trash_can.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_trash_can.xml rename to android/src/main/res-localized/drawable/ic_trash_can.xml diff --git a/app/src/main/res-localized/values-ar-rSA/strings.xml b/android/src/main/res-localized/values-ar-rSA/strings.xml similarity index 100% rename from app/src/main/res-localized/values-ar-rSA/strings.xml rename to android/src/main/res-localized/values-ar-rSA/strings.xml diff --git a/app/src/main/res-localized/values-ca-rES/strings.xml b/android/src/main/res-localized/values-ca-rES/strings.xml similarity index 100% rename from app/src/main/res-localized/values-ca-rES/strings.xml rename to android/src/main/res-localized/values-ca-rES/strings.xml diff --git a/app/src/main/res-localized/values-de-rDE/strings.xml b/android/src/main/res-localized/values-de-rDE/strings.xml similarity index 100% rename from app/src/main/res-localized/values-de-rDE/strings.xml rename to android/src/main/res-localized/values-de-rDE/strings.xml diff --git a/app/src/main/res-localized/values-en-rUS/strings.xml b/android/src/main/res-localized/values-en-rUS/strings.xml similarity index 100% rename from app/src/main/res-localized/values-en-rUS/strings.xml rename to android/src/main/res-localized/values-en-rUS/strings.xml diff --git a/app/src/main/res-localized/values-es-rES/strings.xml b/android/src/main/res-localized/values-es-rES/strings.xml similarity index 100% rename from app/src/main/res-localized/values-es-rES/strings.xml rename to android/src/main/res-localized/values-es-rES/strings.xml diff --git a/app/src/main/res-localized/values-fr-rFR/strings.xml b/android/src/main/res-localized/values-fr-rFR/strings.xml similarity index 100% rename from app/src/main/res-localized/values-fr-rFR/strings.xml rename to android/src/main/res-localized/values-fr-rFR/strings.xml diff --git a/app/src/main/res-localized/values-it-rIT/strings.xml b/android/src/main/res-localized/values-it-rIT/strings.xml similarity index 100% rename from app/src/main/res-localized/values-it-rIT/strings.xml rename to android/src/main/res-localized/values-it-rIT/strings.xml diff --git a/app/src/main/res-localized/values-ja-rJP/strings.xml b/android/src/main/res-localized/values-ja-rJP/strings.xml similarity index 100% rename from app/src/main/res-localized/values-ja-rJP/strings.xml rename to android/src/main/res-localized/values-ja-rJP/strings.xml diff --git a/app/src/main/res-localized/values-ko-rKR/strings.xml b/android/src/main/res-localized/values-ko-rKR/strings.xml similarity index 100% rename from app/src/main/res-localized/values-ko-rKR/strings.xml rename to android/src/main/res-localized/values-ko-rKR/strings.xml diff --git a/app/src/main/res-localized/values-pt-rBR/strings.xml b/android/src/main/res-localized/values-pt-rBR/strings.xml similarity index 100% rename from app/src/main/res-localized/values-pt-rBR/strings.xml rename to android/src/main/res-localized/values-pt-rBR/strings.xml diff --git a/app/src/main/res-localized/values-si-rLK/strings.xml b/android/src/main/res-localized/values-si-rLK/strings.xml similarity index 100% rename from app/src/main/res-localized/values-si-rLK/strings.xml rename to android/src/main/res-localized/values-si-rLK/strings.xml diff --git a/app/src/main/res-localized/values-tr-rTR/strings.xml b/android/src/main/res-localized/values-tr-rTR/strings.xml similarity index 100% rename from app/src/main/res-localized/values-tr-rTR/strings.xml rename to android/src/main/res-localized/values-tr-rTR/strings.xml diff --git a/app/src/main/res-localized/values-vi-rVN/strings.xml b/android/src/main/res-localized/values-vi-rVN/strings.xml similarity index 100% rename from app/src/main/res-localized/values-vi-rVN/strings.xml rename to android/src/main/res-localized/values-vi-rVN/strings.xml diff --git a/app/src/main/res-localized/values-zh-rCN/strings.xml b/android/src/main/res-localized/values-zh-rCN/strings.xml similarity index 100% rename from app/src/main/res-localized/values-zh-rCN/strings.xml rename to android/src/main/res-localized/values-zh-rCN/strings.xml diff --git a/app/src/main/res-localized/values-zh-rTW/strings.xml b/android/src/main/res-localized/values-zh-rTW/strings.xml similarity index 100% rename from app/src/main/res-localized/values-zh-rTW/strings.xml rename to android/src/main/res-localized/values-zh-rTW/strings.xml diff --git a/app/src/main/res/drawable-anydpi-v24/ic_notification.xml b/android/src/main/res/drawable-anydpi-v24/ic_notification.xml similarity index 100% rename from app/src/main/res/drawable-anydpi-v24/ic_notification.xml rename to android/src/main/res/drawable-anydpi-v24/ic_notification.xml diff --git a/app/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_blue.png similarity index 100% rename from app/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_blue.png rename to android/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_blue.png diff --git a/app/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_white.png similarity index 100% rename from app/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_white.png rename to android/src/main/res/drawable-hdpi-v4/ic_mastodon_logo_white.png diff --git a/app/src/main/res/drawable-hdpi/featured_graphics.png b/android/src/main/res/drawable-hdpi/featured_graphics.png similarity index 100% rename from app/src/main/res/drawable-hdpi/featured_graphics.png rename to android/src/main/res/drawable-hdpi/featured_graphics.png diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/android/src/main/res/drawable-hdpi/ic_notification.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_notification.png rename to android/src/main/res/drawable-hdpi/ic_notification.png diff --git a/app/src/main/res/drawable-hdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-hdpi/ic_profile_image_twidere.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_profile_image_twidere.png rename to android/src/main/res/drawable-hdpi/ic_profile_image_twidere.png diff --git a/app/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png similarity index 100% rename from app/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png rename to android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png diff --git a/app/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_white.png similarity index 100% rename from app/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_white.png rename to android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_white.png diff --git a/app/src/main/res/drawable-mdpi/featured_graphics.png b/android/src/main/res/drawable-mdpi/featured_graphics.png similarity index 100% rename from app/src/main/res/drawable-mdpi/featured_graphics.png rename to android/src/main/res/drawable-mdpi/featured_graphics.png diff --git a/app/src/main/res/drawable-mdpi/ic_notification.png b/android/src/main/res/drawable-mdpi/ic_notification.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_notification.png rename to android/src/main/res/drawable-mdpi/ic_notification.png diff --git a/app/src/main/res/drawable-mdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-mdpi/ic_profile_image_twidere.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_profile_image_twidere.png rename to android/src/main/res/drawable-mdpi/ic_profile_image_twidere.png diff --git a/app/src/main/res/drawable-night/ic_login_logo.xml b/android/src/main/res/drawable-night/ic_login_logo.xml similarity index 100% rename from app/src/main/res/drawable-night/ic_login_logo.xml rename to android/src/main/res/drawable-night/ic_login_logo.xml diff --git a/app/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml b/android/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml similarity index 100% rename from app/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml rename to android/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml diff --git a/app/src/main/res/drawable-v24/ic_mastodon_logo_white.xml b/android/src/main/res/drawable-v24/ic_mastodon_logo_white.xml similarity index 100% rename from app/src/main/res/drawable-v24/ic_mastodon_logo_white.xml rename to android/src/main/res/drawable-v24/ic_mastodon_logo_white.xml diff --git a/app/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png similarity index 100% rename from app/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png rename to android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png diff --git a/app/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_white.png similarity index 100% rename from app/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_white.png rename to android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_white.png diff --git a/app/src/main/res/drawable-xhdpi/featured_graphics.png b/android/src/main/res/drawable-xhdpi/featured_graphics.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/featured_graphics.png rename to android/src/main/res/drawable-xhdpi/featured_graphics.png diff --git a/app/src/main/res/drawable-xhdpi/ic_notification.png b/android/src/main/res/drawable-xhdpi/ic_notification.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_notification.png rename to android/src/main/res/drawable-xhdpi/ic_notification.png diff --git a/app/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png rename to android/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png diff --git a/app/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png rename to android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png diff --git a/app/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_white.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_white.png rename to android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_white.png diff --git a/app/src/main/res/drawable-xxhdpi/featured_graphics.png b/android/src/main/res/drawable-xxhdpi/featured_graphics.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/featured_graphics.png rename to android/src/main/res/drawable-xxhdpi/featured_graphics.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/android/src/main/res/drawable-xxhdpi/ic_notification.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_notification.png rename to android/src/main/res/drawable-xxhdpi/ic_notification.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png rename to android/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png diff --git a/app/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png rename to android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png diff --git a/app/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png rename to android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png diff --git a/app/src/main/res/drawable-xxxhdpi/featured_graphics.png b/android/src/main/res/drawable-xxxhdpi/featured_graphics.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/featured_graphics.png rename to android/src/main/res/drawable-xxxhdpi/featured_graphics.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png rename to android/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png diff --git a/app/src/main/res/drawable/ic_about_gray_logo.xml b/android/src/main/res/drawable/ic_about_gray_logo.xml similarity index 100% rename from app/src/main/res/drawable/ic_about_gray_logo.xml rename to android/src/main/res/drawable/ic_about_gray_logo.xml diff --git a/app/src/main/res/drawable/ic_about_gray_logo_shadow.xml b/android/src/main/res/drawable/ic_about_gray_logo_shadow.xml similarity index 100% rename from app/src/main/res/drawable/ic_about_gray_logo_shadow.xml rename to android/src/main/res/drawable/ic_about_gray_logo_shadow.xml diff --git a/app/src/main/res/drawable/ic_add.xml b/android/src/main/res/drawable/ic_add.xml similarity index 100% rename from app/src/main/res/drawable/ic_add.xml rename to android/src/main/res/drawable/ic_add.xml diff --git a/app/src/main/res/drawable/ic_add_colored.xml b/android/src/main/res/drawable/ic_add_colored.xml similarity index 100% rename from app/src/main/res/drawable/ic_add_colored.xml rename to android/src/main/res/drawable/ic_add_colored.xml diff --git a/app/src/main/res/drawable/ic_adjustments_horizontal.xml b/android/src/main/res/drawable/ic_adjustments_horizontal.xml similarity index 100% rename from app/src/main/res/drawable/ic_adjustments_horizontal.xml rename to android/src/main/res/drawable/ic_adjustments_horizontal.xml diff --git a/app/src/main/res/drawable/ic_alert.xml b/android/src/main/res/drawable/ic_alert.xml similarity index 100% rename from app/src/main/res/drawable/ic_alert.xml rename to android/src/main/res/drawable/ic_alert.xml diff --git a/app/src/main/res/drawable/ic_alert_octagon.xml b/android/src/main/res/drawable/ic_alert_octagon.xml similarity index 100% rename from app/src/main/res/drawable/ic_alert_octagon.xml rename to android/src/main/res/drawable/ic_alert_octagon.xml diff --git a/app/src/main/res/drawable/ic_alert_triangle.xml b/android/src/main/res/drawable/ic_alert_triangle.xml similarity index 100% rename from app/src/main/res/drawable/ic_alert_triangle.xml rename to android/src/main/res/drawable/ic_alert_triangle.xml diff --git a/app/src/main/res/drawable/ic_at_sign.xml b/android/src/main/res/drawable/ic_at_sign.xml similarity index 100% rename from app/src/main/res/drawable/ic_at_sign.xml rename to android/src/main/res/drawable/ic_at_sign.xml diff --git a/app/src/main/res/drawable/ic_bell.xml b/android/src/main/res/drawable/ic_bell.xml similarity index 100% rename from app/src/main/res/drawable/ic_bell.xml rename to android/src/main/res/drawable/ic_bell.xml diff --git a/app/src/main/res/drawable/ic_bell_ringing.xml b/android/src/main/res/drawable/ic_bell_ringing.xml similarity index 100% rename from app/src/main/res/drawable/ic_bell_ringing.xml rename to android/src/main/res/drawable/ic_bell_ringing.xml diff --git a/app/src/main/res/drawable/ic_blockquote.xml b/android/src/main/res/drawable/ic_blockquote.xml similarity index 100% rename from app/src/main/res/drawable/ic_blockquote.xml rename to android/src/main/res/drawable/ic_blockquote.xml diff --git a/app/src/main/res/drawable/ic_browser.xml b/android/src/main/res/drawable/ic_browser.xml similarity index 100% rename from app/src/main/res/drawable/ic_browser.xml rename to android/src/main/res/drawable/ic_browser.xml diff --git a/app/src/main/res/drawable/ic_camera.xml b/android/src/main/res/drawable/ic_camera.xml similarity index 100% rename from app/src/main/res/drawable/ic_camera.xml rename to android/src/main/res/drawable/ic_camera.xml diff --git a/app/src/main/res/drawable/ic_corner_up_left.xml b/android/src/main/res/drawable/ic_corner_up_left.xml similarity index 100% rename from app/src/main/res/drawable/ic_corner_up_left.xml rename to android/src/main/res/drawable/ic_corner_up_left.xml diff --git a/app/src/main/res/drawable/ic_cw.xml b/android/src/main/res/drawable/ic_cw.xml similarity index 100% rename from app/src/main/res/drawable/ic_cw.xml rename to android/src/main/res/drawable/ic_cw.xml diff --git a/app/src/main/res/drawable/ic_database.xml b/android/src/main/res/drawable/ic_database.xml similarity index 100% rename from app/src/main/res/drawable/ic_database.xml rename to android/src/main/res/drawable/ic_database.xml diff --git a/app/src/main/res/drawable/ic_delete_colored.xml b/android/src/main/res/drawable/ic_delete_colored.xml similarity index 100% rename from app/src/main/res/drawable/ic_delete_colored.xml rename to android/src/main/res/drawable/ic_delete_colored.xml diff --git a/app/src/main/res/drawable/ic_device_floppy.xml b/android/src/main/res/drawable/ic_device_floppy.xml similarity index 100% rename from app/src/main/res/drawable/ic_device_floppy.xml rename to android/src/main/res/drawable/ic_device_floppy.xml diff --git a/app/src/main/res/drawable/ic_dots_vertical.xml b/android/src/main/res/drawable/ic_dots_vertical.xml similarity index 100% rename from app/src/main/res/drawable/ic_dots_vertical.xml rename to android/src/main/res/drawable/ic_dots_vertical.xml diff --git a/app/src/main/res/drawable/ic_draft_number.xml b/android/src/main/res/drawable/ic_draft_number.xml similarity index 100% rename from app/src/main/res/drawable/ic_draft_number.xml rename to android/src/main/res/drawable/ic_draft_number.xml diff --git a/app/src/main/res/drawable/ic_empty_column.xml b/android/src/main/res/drawable/ic_empty_column.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_column.xml rename to android/src/main/res/drawable/ic_empty_column.xml diff --git a/app/src/main/res/drawable/ic_empty_list.xml b/android/src/main/res/drawable/ic_empty_list.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_list.xml rename to android/src/main/res/drawable/ic_empty_list.xml diff --git a/app/src/main/res/drawable/ic_empty_status.xml b/android/src/main/res/drawable/ic_empty_status.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_status.xml rename to android/src/main/res/drawable/ic_empty_status.xml diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/android/src/main/res/drawable/ic_expand_more.xml similarity index 100% rename from app/src/main/res/drawable/ic_expand_more.xml rename to android/src/main/res/drawable/ic_expand_more.xml diff --git a/app/src/main/res/drawable/ic_eye_off.xml b/android/src/main/res/drawable/ic_eye_off.xml similarity index 100% rename from app/src/main/res/drawable/ic_eye_off.xml rename to android/src/main/res/drawable/ic_eye_off.xml diff --git a/app/src/main/res/drawable/ic_feather.xml b/android/src/main/res/drawable/ic_feather.xml similarity index 100% rename from app/src/main/res/drawable/ic_feather.xml rename to android/src/main/res/drawable/ic_feather.xml diff --git a/app/src/main/res/drawable/ic_filter.xml b/android/src/main/res/drawable/ic_filter.xml similarity index 100% rename from app/src/main/res/drawable/ic_filter.xml rename to android/src/main/res/drawable/ic_filter.xml diff --git a/app/src/main/res/drawable/ic_float_left.xml b/android/src/main/res/drawable/ic_float_left.xml similarity index 100% rename from app/src/main/res/drawable/ic_float_left.xml rename to android/src/main/res/drawable/ic_float_left.xml diff --git a/app/src/main/res/drawable/ic_gif.xml b/android/src/main/res/drawable/ic_gif.xml similarity index 100% rename from app/src/main/res/drawable/ic_gif.xml rename to android/src/main/res/drawable/ic_gif.xml diff --git a/app/src/main/res/drawable/ic_github.xml b/android/src/main/res/drawable/ic_github.xml similarity index 100% rename from app/src/main/res/drawable/ic_github.xml rename to android/src/main/res/drawable/ic_github.xml diff --git a/app/src/main/res/drawable/ic_globe.xml b/android/src/main/res/drawable/ic_globe.xml similarity index 100% rename from app/src/main/res/drawable/ic_globe.xml rename to android/src/main/res/drawable/ic_globe.xml diff --git a/app/src/main/res/drawable/ic_gray_logo_shadow.xml b/android/src/main/res/drawable/ic_gray_logo_shadow.xml similarity index 100% rename from app/src/main/res/drawable/ic_gray_logo_shadow.xml rename to android/src/main/res/drawable/ic_gray_logo_shadow.xml diff --git a/app/src/main/res/drawable/ic_hash.xml b/android/src/main/res/drawable/ic_hash.xml similarity index 100% rename from app/src/main/res/drawable/ic_hash.xml rename to android/src/main/res/drawable/ic_hash.xml diff --git a/app/src/main/res/drawable/ic_heart.xml b/android/src/main/res/drawable/ic_heart.xml similarity index 100% rename from app/src/main/res/drawable/ic_heart.xml rename to android/src/main/res/drawable/ic_heart.xml diff --git a/app/src/main/res/drawable/ic_home.xml b/android/src/main/res/drawable/ic_home.xml similarity index 100% rename from app/src/main/res/drawable/ic_home.xml rename to android/src/main/res/drawable/ic_home.xml diff --git a/app/src/main/res/drawable/ic_info_circle.xml b/android/src/main/res/drawable/ic_info_circle.xml similarity index 100% rename from app/src/main/res/drawable/ic_info_circle.xml rename to android/src/main/res/drawable/ic_info_circle.xml diff --git a/app/src/main/res/drawable/ic_layout_sidebar.xml b/android/src/main/res/drawable/ic_layout_sidebar.xml similarity index 100% rename from app/src/main/res/drawable/ic_layout_sidebar.xml rename to android/src/main/res/drawable/ic_layout_sidebar.xml diff --git a/app/src/main/res/drawable/ic_lists.xml b/android/src/main/res/drawable/ic_lists.xml similarity index 100% rename from app/src/main/res/drawable/ic_lists.xml rename to android/src/main/res/drawable/ic_lists.xml diff --git a/app/src/main/res/drawable/ic_lock.xml b/android/src/main/res/drawable/ic_lock.xml similarity index 100% rename from app/src/main/res/drawable/ic_lock.xml rename to android/src/main/res/drawable/ic_lock.xml diff --git a/app/src/main/res/drawable/ic_lock_open.xml b/android/src/main/res/drawable/ic_lock_open.xml similarity index 100% rename from app/src/main/res/drawable/ic_lock_open.xml rename to android/src/main/res/drawable/ic_lock_open.xml diff --git a/app/src/main/res/drawable/ic_login_logo.xml b/android/src/main/res/drawable/ic_login_logo.xml similarity index 100% rename from app/src/main/res/drawable/ic_login_logo.xml rename to android/src/main/res/drawable/ic_login_logo.xml diff --git a/app/src/main/res/drawable/ic_mail.xml b/android/src/main/res/drawable/ic_mail.xml similarity index 100% rename from app/src/main/res/drawable/ic_mail.xml rename to android/src/main/res/drawable/ic_mail.xml diff --git a/app/src/main/res/drawable/ic_map_pin.xml b/android/src/main/res/drawable/ic_map_pin.xml similarity index 100% rename from app/src/main/res/drawable/ic_map_pin.xml rename to android/src/main/res/drawable/ic_map_pin.xml diff --git a/app/src/main/res/drawable/ic_mastodon_badge.xml b/android/src/main/res/drawable/ic_mastodon_badge.xml similarity index 100% rename from app/src/main/res/drawable/ic_mastodon_badge.xml rename to android/src/main/res/drawable/ic_mastodon_badge.xml diff --git a/app/src/main/res/drawable/ic_message_circle.xml b/android/src/main/res/drawable/ic_message_circle.xml similarity index 100% rename from app/src/main/res/drawable/ic_message_circle.xml rename to android/src/main/res/drawable/ic_message_circle.xml diff --git a/app/src/main/res/drawable/ic_mood_smile.xml b/android/src/main/res/drawable/ic_mood_smile.xml similarity index 100% rename from app/src/main/res/drawable/ic_mood_smile.xml rename to android/src/main/res/drawable/ic_mood_smile.xml diff --git a/app/src/main/res/drawable/ic_note.xml b/android/src/main/res/drawable/ic_note.xml similarity index 100% rename from app/src/main/res/drawable/ic_note.xml rename to android/src/main/res/drawable/ic_note.xml diff --git a/app/src/main/res/drawable/ic_photo.xml b/android/src/main/res/drawable/ic_photo.xml similarity index 100% rename from app/src/main/res/drawable/ic_photo.xml rename to android/src/main/res/drawable/ic_photo.xml diff --git a/app/src/main/res/drawable/ic_planet.xml b/android/src/main/res/drawable/ic_planet.xml similarity index 100% rename from app/src/main/res/drawable/ic_planet.xml rename to android/src/main/res/drawable/ic_planet.xml diff --git a/app/src/main/res/drawable/ic_poll.xml b/android/src/main/res/drawable/ic_poll.xml similarity index 100% rename from app/src/main/res/drawable/ic_poll.xml rename to android/src/main/res/drawable/ic_poll.xml diff --git a/app/src/main/res/drawable/ic_refresh.xml b/android/src/main/res/drawable/ic_refresh.xml similarity index 100% rename from app/src/main/res/drawable/ic_refresh.xml rename to android/src/main/res/drawable/ic_refresh.xml diff --git a/app/src/main/res/drawable/ic_repeat.xml b/android/src/main/res/drawable/ic_repeat.xml similarity index 100% rename from app/src/main/res/drawable/ic_repeat.xml rename to android/src/main/res/drawable/ic_repeat.xml diff --git a/app/src/main/res/drawable/ic_search.xml b/android/src/main/res/drawable/ic_search.xml similarity index 100% rename from app/src/main/res/drawable/ic_search.xml rename to android/src/main/res/drawable/ic_search.xml diff --git a/app/src/main/res/drawable/ic_send.xml b/android/src/main/res/drawable/ic_send.xml similarity index 100% rename from app/src/main/res/drawable/ic_send.xml rename to android/src/main/res/drawable/ic_send.xml diff --git a/app/src/main/res/drawable/ic_send_thread.xml b/android/src/main/res/drawable/ic_send_thread.xml similarity index 100% rename from app/src/main/res/drawable/ic_send_thread.xml rename to android/src/main/res/drawable/ic_send_thread.xml diff --git a/app/src/main/res/drawable/ic_settings_notification.xml b/android/src/main/res/drawable/ic_settings_notification.xml similarity index 100% rename from app/src/main/res/drawable/ic_settings_notification.xml rename to android/src/main/res/drawable/ic_settings_notification.xml diff --git a/app/src/main/res/drawable/ic_share.xml b/android/src/main/res/drawable/ic_share.xml similarity index 100% rename from app/src/main/res/drawable/ic_share.xml rename to android/src/main/res/drawable/ic_share.xml diff --git a/app/src/main/res/drawable/ic_shirt.xml b/android/src/main/res/drawable/ic_shirt.xml similarity index 100% rename from app/src/main/res/drawable/ic_shirt.xml rename to android/src/main/res/drawable/ic_shirt.xml diff --git a/app/src/main/res/drawable/ic_telegram.xml b/android/src/main/res/drawable/ic_telegram.xml similarity index 100% rename from app/src/main/res/drawable/ic_telegram.xml rename to android/src/main/res/drawable/ic_telegram.xml diff --git a/app/src/main/res/drawable/ic_template.xml b/android/src/main/res/drawable/ic_template.xml similarity index 100% rename from app/src/main/res/drawable/ic_template.xml rename to android/src/main/res/drawable/ic_template.xml diff --git a/app/src/main/res/drawable/ic_thread_mode.xml b/android/src/main/res/drawable/ic_thread_mode.xml similarity index 100% rename from app/src/main/res/drawable/ic_thread_mode.xml rename to android/src/main/res/drawable/ic_thread_mode.xml diff --git a/app/src/main/res/drawable/ic_triangle_square_circle.xml b/android/src/main/res/drawable/ic_triangle_square_circle.xml similarity index 100% rename from app/src/main/res/drawable/ic_triangle_square_circle.xml rename to android/src/main/res/drawable/ic_triangle_square_circle.xml diff --git a/app/src/main/res/drawable/ic_twitter.xml b/android/src/main/res/drawable/ic_twitter.xml similarity index 100% rename from app/src/main/res/drawable/ic_twitter.xml rename to android/src/main/res/drawable/ic_twitter.xml diff --git a/app/src/main/res/drawable/ic_twitter_badge.xml b/android/src/main/res/drawable/ic_twitter_badge.xml similarity index 100% rename from app/src/main/res/drawable/ic_twitter_badge.xml rename to android/src/main/res/drawable/ic_twitter_badge.xml diff --git a/app/src/main/res/drawable/ic_twitter_logo_white.xml b/android/src/main/res/drawable/ic_twitter_logo_white.xml similarity index 100% rename from app/src/main/res/drawable/ic_twitter_logo_white.xml rename to android/src/main/res/drawable/ic_twitter_logo_white.xml diff --git a/app/src/main/res/drawable/ic_user.xml b/android/src/main/res/drawable/ic_user.xml similarity index 100% rename from app/src/main/res/drawable/ic_user.xml rename to android/src/main/res/drawable/ic_user.xml diff --git a/app/src/main/res/drawable/ic_user_exclamation.xml b/android/src/main/res/drawable/ic_user_exclamation.xml similarity index 100% rename from app/src/main/res/drawable/ic_user_exclamation.xml rename to android/src/main/res/drawable/ic_user_exclamation.xml diff --git a/app/src/main/res/drawable/ic_user_plus.xml b/android/src/main/res/drawable/ic_user_plus.xml similarity index 100% rename from app/src/main/res/drawable/ic_user_plus.xml rename to android/src/main/res/drawable/ic_user_plus.xml diff --git a/app/src/main/res/drawable/ic_users.xml b/android/src/main/res/drawable/ic_users.xml similarity index 100% rename from app/src/main/res/drawable/ic_users.xml rename to android/src/main/res/drawable/ic_users.xml diff --git a/app/src/main/res/drawable/ic_x.xml b/android/src/main/res/drawable/ic_x.xml similarity index 100% rename from app/src/main/res/drawable/ic_x.xml rename to android/src/main/res/drawable/ic_x.xml diff --git a/app/src/main/res/layout/exo_player_control_view.xml b/android/src/main/res/layout/exo_player_control_view.xml similarity index 100% rename from app/src/main/res/layout/exo_player_control_view.xml rename to android/src/main/res/layout/exo_player_control_view.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/android/src/main/res/mipmap-hdpi/ic_launcher_background.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_background.png rename to android/src/main/res/mipmap-hdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to android/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/android/src/main/res/mipmap-mdpi/ic_launcher_background.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_background.png rename to android/src/main/res/mipmap-mdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to android/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_background.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_background.png rename to android/src/main/res/mipmap-xhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to android/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_background.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png rename to android/src/main/res/mipmap-xxhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png rename to android/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to android/src/main/res/values/strings.xml diff --git a/app/src/main/res/values/strings_untranslatable.xml b/android/src/main/res/values/strings_untranslatable.xml similarity index 100% rename from app/src/main/res/values/strings_untranslatable.xml rename to android/src/main/res/values/strings_untranslatable.xml diff --git a/app/src/main/res/values/themes.xml b/android/src/main/res/values/themes.xml similarity index 100% rename from app/src/main/res/values/themes.xml rename to android/src/main/res/values/themes.xml diff --git a/app/src/main/res/xml/authenticator.xml b/android/src/main/res/xml/authenticator.xml similarity index 100% rename from app/src/main/res/xml/authenticator.xml rename to android/src/main/res/xml/authenticator.xml diff --git a/app/src/main/res/xml/filer_provider_path.xml b/android/src/main/res/xml/filer_provider_path.xml similarity index 100% rename from app/src/main/res/xml/filer_provider_path.xml rename to android/src/main/res/xml/filer_provider_path.xml diff --git a/app/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt b/android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt rename to android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/MockCenter.kt b/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/MockCenter.kt rename to android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt rename to android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt b/android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt rename to android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt diff --git a/app/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt b/android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt rename to android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt rename to android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt rename to android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/android/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt rename to android/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/android/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt rename to android/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt b/android/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt rename to android/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/android/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt rename to android/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt b/android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt rename to android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt rename to android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt rename to android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt diff --git a/app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt rename to android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt rename to android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt diff --git a/app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt similarity index 100% rename from app/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt rename to android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt diff --git a/app/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt b/android/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt similarity index 100% rename from app/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt rename to android/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt diff --git a/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker similarity index 100% rename from app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker rename to android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index a9334dac5..3d228226b 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -8,6 +8,7 @@ fun Project.configRepository() { google() mavenCentral() maven("https://jitpack.io") + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 2672e3895..671992297 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -21,6 +21,7 @@ object Versions { const val retrofit2 = "2.9.0" const val hson = "0.1.4" const val compose = "1.0.0" + const val compose_jb = "0.5.0-build270" const val constraintLayout = "1.0.0-beta01" const val paging = "3.1.0-alpha03" const val paging_compose = "1.0.0-alpha12" diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 000000000..52245e540 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,46 @@ +import org.jetbrains.compose.compose + +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") version Versions.compose_jb + id("com.android.library") +} + +group = Package.group +version = Package.versionName + +repositories { + google() +} + +kotlin { + android() + jvm("desktop") { + compilations.all { + kotlinOptions.jvmTarget = Versions.Java.jvmTarget + } + } + sourceSets { + val commonMain by getting { + dependencies { + api(compose.runtime) + api(compose.foundation) + api(compose.material) + } + } + val commonTest by getting + val androidMain by getting + val androidTest by getting + val desktopMain by getting + val desktopTest by getting + } +} + +android { + compileSdk = AndroidSdk.compile + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + defaultConfig { + minSdk = AndroidSdk.min + targetSdk = AndroidSdk.target + } +} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts new file mode 100644 index 000000000..58193a31d --- /dev/null +++ b/desktop/build.gradle.kts @@ -0,0 +1,38 @@ +import org.jetbrains.compose.compose +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") version Versions.compose_jb +} + +group = Package.group +version = Package.versionName + +kotlin { + jvm { + compilations.all { + kotlinOptions.jvmTarget = Versions.Java.jvmTarget + } + } + sourceSets { + val jvmMain by getting { + dependencies { + implementation(project(":common")) + implementation(compose.desktop.currentOs) + } + } + val jvmTest by getting + } +} + +compose.desktop { + application { + mainClass = "com.twidere.twiderex.MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.AppImage) + packageName = Package.id + packageVersion = Package.versionName.split("-").firstOrNull() + } + } +} diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt new file mode 100644 index 000000000..013609dff --- /dev/null +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex + +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.singleWindowApplication + +@ExperimentalComposeUiApi +fun main() = singleWindowApplication { +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 019542bb2..32fd2202b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,11 +2,13 @@ pluginManagement { repositories { gradlePluginPortal() google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } } rootProject.name = "TwidereX" -include(":app", ":services", ":assistedProcessor", ":routeProcessor") +include(":android", ":services", ":common", ":desktop", ":assistedProcessor", ":routeProcessor") enableFeaturePreview("VERSION_CATALOGS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") From 05839dff45be52b99fa2429b9808c9112fe7b900 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 30 Jul 2021 15:45:19 +0800 Subject: [PATCH 002/615] add common android manifest --- common/src/androidMain/AndroidManifest.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 common/src/androidMain/AndroidManifest.xml diff --git a/common/src/androidMain/AndroidManifest.xml b/common/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..66290fb44 --- /dev/null +++ b/common/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 890170fa39afdf7bfe230d5522798a85cf3cf94a Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 30 Jul 2021 15:56:45 +0800 Subject: [PATCH 003/615] init app --- buildSrc/src/main/kotlin/Versions.kt | 4 +-- .../kotlin/com/twidere/twiderex/App.kt | 35 +++++++++++++++++++ desktop/build.gradle.kts | 16 +++++---- .../kotlin/com/twidere/twiderex/main.kt | 5 ++- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/App.kt diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 671992297..d71eab12a 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.5.10" + const val lang = "1.5.21" const val coroutines = "1.5.0" const val serialization = "1.2.2" } @@ -12,7 +12,7 @@ object Versions { val java = JavaVersion.VERSION_11 } - const val ksp = "${Kotlin.lang}-1.0.0-beta02" + const val ksp = "${Kotlin.lang}-1.0.0-beta06" const val agp = "7.0.0" const val spotless = "5.14.2" const val ktlint = "0.41.0" diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt new file mode 100644 index 000000000..5e22e6535 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex + +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable + +@Composable +fun App() { + MaterialTheme { + Scaffold { + Text("Twidere X!") + } + } +} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 58193a31d..0dc095f22 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -26,13 +26,15 @@ kotlin { } } -compose.desktop { - application { - mainClass = "com.twidere.twiderex.MainKt" - nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.AppImage) - packageName = Package.id - packageVersion = Package.versionName.split("-").firstOrNull() +compose { + desktop { + application { + mainClass = "com.twidere.twiderex.MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.AppImage) + packageName = Package.id + packageVersion = Package.versionName.split("-").firstOrNull() + } } } } diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index 013609dff..5e39c8703 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -24,5 +24,8 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.singleWindowApplication @ExperimentalComposeUiApi -fun main() = singleWindowApplication { +fun main() = singleWindowApplication( + title = "Twidere X" +) { + App() } From 5ba35bec4781a7573a8ae4b3f52e8788e703bd7e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 2 Aug 2021 12:24:32 +0800 Subject: [PATCH 004/615] upgrade compose-jb to 1.0.0-alpha1-rc1 --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index d71eab12a..a5c5437e0 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -21,7 +21,7 @@ object Versions { const val retrofit2 = "2.9.0" const val hson = "0.1.4" const val compose = "1.0.0" - const val compose_jb = "0.5.0-build270" + const val compose_jb = "1.0.0-alpha1-rc1" const val constraintLayout = "1.0.0-beta01" const val paging = "3.1.0-alpha03" const val paging_compose = "1.0.0-alpha12" From 58636033cc03e0c8a3fa0fa784706269713c1b39 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 2 Aug 2021 14:39:53 +0800 Subject: [PATCH 005/615] define expect DataProvider in commonMain --- .../twiderex/dataprovider/DataProvider.kt | 39 ++++++++++++++ .../twidere/twiderex/db/AndroidAppDatabase.kt | 38 ++++++++++++++ .../twiderex/db/AndroidCacheDatabase.kt | 38 ++++++++++++++ .../twiderex/dataprovider/DataProvider.kt | 35 +++++++++++++ .../com/twidere/twiderex/db/AppDatabase.kt | 29 +++++++++++ .../com/twidere/twiderex/db/CacheDatabase.kt | 51 +++++++++++++++++++ .../com/twidere/twiderex/db/Database.kt | 25 +++++++++ .../db/dao/DirectMessageConversationDao.kt | 23 +++++++++ .../twiderex/db/dao/DirectMessageEventDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/DraftDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/ListsDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/MediaDao.kt | 24 +++++++++ .../twiderex/db/dao/NotificationCursorDao.kt | 24 +++++++++ .../twiderex/db/dao/PagingTimelineDao.kt | 23 +++++++++ .../twidere/twiderex/db/dao/ReactionDao.kt | 23 +++++++++ .../com/twidere/twiderex/db/dao/SearchDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/StatusDao.kt | 23 +++++++++ .../twiderex/db/dao/StatusReferenceDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/TrendDao.kt | 24 +++++++++ .../twiderex/db/dao/TrendHistoryDao.kt | 24 +++++++++ .../twidere/twiderex/db/dao/UrlEntityDao.kt | 24 +++++++++ .../com/twidere/twiderex/db/dao/UserDao.kt | 23 +++++++++ .../twiderex/dataprovider/DataProvider.kt | 39 ++++++++++++++ .../twiderex/db/DesktopAppDatabaseImpl.kt | 38 ++++++++++++++ .../twiderex/db/DesktopCacheDatabaseImpl.kt | 27 ++++++++++ 25 files changed, 714 insertions(+) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt new file mode 100644 index 000000000..e6d004adb --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase + +actual class DataProvider { + // data provide functions.... + actual companion object Factory { + actual fun create(): DataProvider { + TODO("Not yet implemented") + } + } + + actual val appDatabase: AppDatabase + get() = TODO("Not yet implemented") + + actual val cacheDatabase: CacheDatabase + get() = TODO("Not yet implemented") +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt new file mode 100644 index 000000000..92d80f16f --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.dao.SearchDao + +internal class AndroidAppDatabase : AppDatabase { + override fun clearAllTables() { + TODO("Not yet implemented") + } + + override fun draftDao(): DraftDao { + TODO("Not yet implemented") + } + + override fun searchDao(): SearchDao { + TODO("Not yet implemented") + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt new file mode 100644 index 000000000..8c67d8631 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.dao.SearchDao + +internal class AndroidCacheDatabase : AppDatabase { + override fun draftDao(): DraftDao { + TODO("Not yet implemented") + } + + override fun searchDao(): SearchDao { + TODO("Not yet implemented") + } + + override fun clearAllTables() { + TODO("Not yet implemented") + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt new file mode 100644 index 000000000..878567572 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase + +expect class DataProvider { + companion object Factory { + fun create(): DataProvider + } + // data provide functions.... + + val appDatabase: AppDatabase + + val cacheDatabase: CacheDatabase +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt new file mode 100644 index 000000000..bab79a3d2 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.dao.SearchDao + +interface AppDatabase : Database { + fun draftDao(): DraftDao + fun searchDao(): SearchDao +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt new file mode 100644 index 000000000..25c0985e9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.dao.DirectMessageConversationDao +import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.db.dao.ListsDao +import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.db.dao.NotificationCursorDao +import com.twidere.twiderex.db.dao.PagingTimelineDao +import com.twidere.twiderex.db.dao.ReactionDao +import com.twidere.twiderex.db.dao.StatusDao +import com.twidere.twiderex.db.dao.StatusReferenceDao +import com.twidere.twiderex.db.dao.TrendDao +import com.twidere.twiderex.db.dao.TrendHistoryDao +import com.twidere.twiderex.db.dao.UrlEntityDao +import com.twidere.twiderex.db.dao.UserDao + +interface CacheDatabase : Database { + abstract fun statusDao(): StatusDao + abstract fun mediaDao(): MediaDao + abstract fun userDao(): UserDao + abstract fun reactionDao(): ReactionDao + abstract fun pagingTimelineDao(): PagingTimelineDao + abstract fun urlEntityDao(): UrlEntityDao + abstract fun statusReferenceDao(): StatusReferenceDao + abstract fun listsDao(): ListsDao + abstract fun notificationCursorDao(): NotificationCursorDao + abstract fun trendDao(): TrendDao + abstract fun trendHistoryDao(): TrendHistoryDao + abstract fun directMessageConversationDao(): DirectMessageConversationDao + abstract fun directMessageDao(): DirectMessageEventDao +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt new file mode 100644 index 000000000..d65f3d220 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +interface Database { + fun clearAllTables() +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt new file mode 100644 index 000000000..6d98d81d3 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt @@ -0,0 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +interface DirectMessageConversationDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt new file mode 100644 index 000000000..6a51ceef9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface DirectMessageEventDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt new file mode 100644 index 000000000..38064b907 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface DraftDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt new file mode 100644 index 000000000..f7d39f07b --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface ListsDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt new file mode 100644 index 000000000..a681f2ce6 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface MediaDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt new file mode 100644 index 000000000..118c6e3f4 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface NotificationCursorDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt new file mode 100644 index 000000000..cce132e62 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt @@ -0,0 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +interface PagingTimelineDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt new file mode 100644 index 000000000..032dcd125 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt @@ -0,0 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +interface ReactionDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt new file mode 100644 index 000000000..8a8c5aa7b --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface SearchDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt new file mode 100644 index 000000000..339e37194 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -0,0 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao +// TODO OPERATION +interface StatusDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt new file mode 100644 index 000000000..8bdd77dff --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface StatusReferenceDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt new file mode 100644 index 000000000..e39705389 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface TrendDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt new file mode 100644 index 000000000..4549562f3 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface TrendHistoryDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt new file mode 100644 index 000000000..9cc28ac4b --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +// TODO OPERATION +interface UrlEntityDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt new file mode 100644 index 000000000..787fbc600 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt @@ -0,0 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao +// TODO OPERATION +interface UserDao diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt new file mode 100644 index 000000000..e6d004adb --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase + +actual class DataProvider { + // data provide functions.... + actual companion object Factory { + actual fun create(): DataProvider { + TODO("Not yet implemented") + } + } + + actual val appDatabase: AppDatabase + get() = TODO("Not yet implemented") + + actual val cacheDatabase: CacheDatabase + get() = TODO("Not yet implemented") +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt new file mode 100644 index 000000000..5f2018cc2 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.dao.SearchDao + +internal class DesktopAppDatabaseImpl : AppDatabase { + override fun draftDao(): DraftDao { + TODO("Not yet implemented") + } + + override fun searchDao(): SearchDao { + TODO("Not yet implemented") + } + + override fun clearAllTables() { + TODO("Not yet implemented") + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt new file mode 100644 index 000000000..800aa797a --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +internal class DesktopCacheDatabaseImpl : Database { + override fun clearAllTables() { + TODO("Not yet implemented") + } +} From 2efd28f029f9adc9eaf5d2bb944c2c58a3478c1b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 2 Aug 2021 15:56:28 +0800 Subject: [PATCH 006/615] enableGranularSourceSetsMetadata --- gradle.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1e090ae24..c60902fc7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,4 +19,5 @@ android.useAndroidX=true android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -android.injected.testOnly=false \ No newline at end of file +android.injected.testOnly=false +kotlin.mpp.enableGranularSourceSetsMetadata=true \ No newline at end of file From 71a13d25a26ce35acc0fe5c615431b360b01739c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 2 Aug 2021 16:37:21 +0800 Subject: [PATCH 007/615] add paging dependency --- common/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 52245e540..a86dba8b8 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -26,6 +26,7 @@ kotlin { api(compose.runtime) api(compose.foundation) api(compose.material) + api("androidx.paging:paging-common:${Versions.paging}") } } val commonTest by getting From 7e32544166c13d9f7445f25741bce23193a462e4 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 09:34:53 +0800 Subject: [PATCH 008/615] add datastore and serialization dependencies --- common/build.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a86dba8b8..f7f06ca1a 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.compose.compose plugins { kotlin("multiplatform") id("org.jetbrains.compose") version Versions.compose_jb + kotlin("plugin.serialization") version Versions.Kotlin.lang id("com.android.library") } @@ -27,6 +28,10 @@ kotlin { api(compose.foundation) api(compose.material) api("androidx.paging:paging-common:${Versions.paging}") + api("androidx.datastore:datastore-core:${Versions.datastore}") + api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") + api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}") + api("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${Versions.Kotlin.serialization}") } } val commonTest by getting From 1db1650156e5c4632c16793fe7b67ffdd89cdc91 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 13:38:23 +0800 Subject: [PATCH 009/615] add common test dependency --- common/build.gradle.kts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index f7f06ca1a..16ff283a8 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -20,6 +20,9 @@ kotlin { compilations.all { kotlinOptions.jvmTarget = Versions.Java.jvmTarget } + testRuns["test"].executionTask.configure { + useJUnitPlatform() + } } sourceSets { val commonMain by getting { @@ -34,7 +37,11 @@ kotlin { api("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${Versions.Kotlin.serialization}") } } - val commonTest by getting + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } val androidMain by getting val androidTest by getting val desktopMain by getting From e57c80c32e7fee0a71528146d9ad07bac0228be1 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 14:26:32 +0800 Subject: [PATCH 010/615] move precompose to common --- common/build.gradle.kts | 7 +- .../lifecycle/PreComposeActivity.kt | 184 +++++++++++ .../tlaster/precompose/lifecycle/Lifecycle.kt | 35 +++ .../precompose/lifecycle/LifecycleObserver.kt | 25 ++ .../precompose/lifecycle/LifecycleOwner.kt | 25 ++ .../precompose/lifecycle/LifecycleRegistry.kt | 51 +++ .../precompose/lifecycle/RepeatOnLifecycle.kt | 74 +++++ .../precompose}/navigation/BackStackEntry.kt | 34 +- .../navigation/NavControllerViewModel.kt | 18 +- .../tlaster/precompose}/navigation/NavHost.kt | 8 +- .../precompose}/navigation/NavOptions.kt | 0 .../precompose}/navigation/Navigator.kt | 0 .../tlaster/precompose}/navigation/PopUpTo.kt | 0 .../precompose}/navigation/QueryString.kt | 0 .../precompose}/navigation/RouteBuilder.kt | 18 +- .../precompose}/navigation/RouteGraph.kt | 0 .../precompose}/navigation/RouteMatch.kt | 0 .../navigation/RouteMatchResult.kt | 0 .../precompose}/navigation/RouteParser.kt | 0 .../precompose}/navigation/RouteStack.kt | 0 .../navigation/RouteStackManager.kt | 58 ++-- .../navigation/route/ComposeRoute.kt | 0 .../navigation/route/DialogRoute.kt | 0 .../precompose}/navigation/route/Route.kt | 2 +- .../navigation/route/SceneRoute.kt | 0 .../transition/AnimatedDialogRoute.kt | 6 +- .../navigation/transition/AnimatedRoute.kt | 6 +- .../navigation/transition/DialogTransition.kt | 0 .../navigation/transition/NavTransition.kt | 0 .../tlaster/precompose/ui/BackPressAdapter.kt | 54 ++++ .../precompose/ui/ComposeCompositionLocal.kt | 33 ++ .../tlaster/precompose/ui/ViewModelAdapter.kt | 70 +++++ .../viewmodel/CloseableCoroutineScope.kt | 57 ++++ .../tlaster/precompose/viewmodel/ViewModel.kt | 63 ++++ .../precompose/viewmodel/ViewModelProvider.kt | 47 +++ .../precompose/viewmodel/ViewModelStore.kt | 45 +++ .../viewmodel/ViewModelStoreOwner.kt | 25 ++ .../precompose/lifecycle/LifecycleTest.kt | 44 +++ .../lifecycle/TestLifecycleOwner.kt | 27 ++ .../precompose/navigation/QueryStringTest.kt | 109 +++++++ .../precompose/navigation/RouteBuilderTest.kt | 70 +++++ .../precompose/navigation/RouteParserTest.kt | 0 .../precompose/navigation/RouteParserTest2.kt | 296 ++++++++++++++++++ .../precompose/navigation/TestRoute.kt | 38 +++ .../viewmodel/ViewModelStoreTest.kt | 53 ++++ .../precompose/viewmodel/ViewModelTest.kt | 63 ++++ .../tlaster/precompose/PreComposeWindow.kt | 105 +++++++ 47 files changed, 1673 insertions(+), 77 deletions(-) create mode 100644 common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/BackStackEntry.kt (72%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/NavControllerViewModel.kt (72%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/NavHost.kt (94%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/NavOptions.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/Navigator.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/PopUpTo.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/QueryString.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteBuilder.kt (79%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteGraph.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteMatch.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteMatchResult.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteParser.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteStack.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/RouteStackManager.kt (83%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/route/ComposeRoute.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/route/DialogRoute.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/route/Route.kt (97%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/route/SceneRoute.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/transition/AnimatedDialogRoute.kt (98%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/transition/AnimatedRoute.kt (97%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/transition/DialogTransition.kt (100%) rename {android/src/main/kotlin/com/twidere/twiderex/component/foundation => common/src/commonMain/kotlin/moe/tlaster/precompose}/navigation/transition/NavTransition.kt (100%) create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt rename {android/src/test/java => common/src/commonTest/kotlin}/moe/tlaster/precompose/navigation/RouteParserTest.kt (100%) create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt create mode 100644 common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt create mode 100644 common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 16ff283a8..9118d4021 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -42,7 +42,12 @@ kotlin { implementation(kotlin("test")) } } - val androidMain by getting + val androidMain by getting { + dependencies { + api("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01") + api("androidx.savedstate:savedstate-ktx:1.1.0") + } + } val androidTest by getting val desktopMain by getting val desktopTest by getting diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt new file mode 100644 index 000000000..69b198b7b --- /dev/null +++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -0,0 +1,184 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +import android.app.Activity +import android.os.Bundle +import android.view.ViewGroup +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionContext +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.ComposeView +import androidx.lifecycle.ViewTreeLifecycleOwner +import androidx.savedstate.SavedStateRegistry +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner +import androidx.savedstate.ViewTreeSavedStateRegistryOwner +import moe.tlaster.precompose.ui.BackDispatcher +import moe.tlaster.precompose.ui.BackDispatcherOwner +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner +import moe.tlaster.precompose.ui.LocalLifecycleOwner +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.ViewModelStore +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner + +open class PreComposeActivity : + Activity(), + LifecycleOwner, + ViewModelStoreOwner, + androidx.lifecycle.LifecycleOwner, + SavedStateRegistryOwner, + BackDispatcherOwner { + override val lifecycle by lazy { + LifecycleRegistry() + } + + override val viewModelStore by lazy { + ViewModelStore() + } + + private val androidLifecycle by lazy { + androidx.lifecycle.LifecycleRegistry(this) + } + + private val savedStateRegistryController by lazy { + SavedStateRegistryController.create(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + savedStateRegistryController.performRestore(savedInstanceState) + super.onCreate(savedInstanceState) + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_CREATE) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + savedStateRegistryController.performSave(outState) + } + + override fun onStart() { + super.onStart() + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_START) + } + + override fun onResume() { + super.onResume() + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_RESUME) + lifecycle.currentState = Lifecycle.State.Active + } + + override fun onPause() { + super.onPause() + lifecycle.currentState = Lifecycle.State.InActive + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_PAUSE) + } + + override fun onStop() { + super.onStop() + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_STOP) + } + + override fun onDestroy() { + super.onDestroy() + lifecycle.currentState = Lifecycle.State.Destroyed + androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_DESTROY) + } + + override fun getLifecycle(): androidx.lifecycle.Lifecycle { + return androidLifecycle + } + + override fun getSavedStateRegistry(): SavedStateRegistry { + return savedStateRegistryController.savedStateRegistry + } + + override val backDispatcher by lazy { + BackDispatcher() + } + + override fun onBackPressed() { + if (!backDispatcher.onBackPress()) { + super.onBackPressed() + } + } +} + +fun PreComposeActivity.setContent( + parent: CompositionContext? = null, + content: @Composable () -> Unit +) { + val existingComposeView = window.decorView + .findViewById(android.R.id.content) + .getChildAt(0) as? ComposeView + + if (existingComposeView != null) with(existingComposeView) { + setParentCompositionContext(parent) + setContent { + ContentInternal(content) + } + } else ComposeView(this).apply { + // Set content and parent **before** setContentView + // to have ComposeView create the composition on attach + setParentCompositionContext(parent) + setContent { + ContentInternal(content) + } + // Set the view tree owners before setting the content view so that the inflation process + // and attach listeners will see them already present + setOwners() + setContentView(this, DefaultActivityContentLayoutParams) + } +} + +private fun PreComposeActivity.setOwners() { + val decorView = window.decorView + if (ViewTreeLifecycleOwner.get(decorView) == null) { + ViewTreeLifecycleOwner.set(decorView, this) + } + if (ViewTreeSavedStateRegistryOwner.get(decorView) == null) { + ViewTreeSavedStateRegistryOwner.set(decorView, this) + } +} + +@Composable +private fun PreComposeActivity.ContentInternal(content: @Composable () -> Unit) { + ProvideAndroidCompositionLocals { + content.invoke() + } +} + +@Composable +private fun PreComposeActivity.ProvideAndroidCompositionLocals( + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalLifecycleOwner provides this, + LocalViewModelStoreOwner provides this, + LocalBackDispatcherOwner provides this, + ) { + content.invoke() + } +} + +private val DefaultActivityContentLayoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT +) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt new file mode 100644 index 000000000..2c42f9698 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +interface Lifecycle { + enum class State { + Initialized, + Active, + InActive, + Destroyed, + } + + val currentState: State + fun removeObserver(observer: LifecycleObserver) + fun addObserver(observer: LifecycleObserver) + fun hasObserver(): Boolean +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt new file mode 100644 index 000000000..e2d6194a0 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +interface LifecycleObserver { + fun onStateChanged(state: Lifecycle.State) +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt new file mode 100644 index 000000000..022739896 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +interface LifecycleOwner { + val lifecycle: Lifecycle +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt new file mode 100644 index 000000000..39b4a02be --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +class LifecycleRegistry : Lifecycle { + private val observers = arrayListOf() + override var currentState: Lifecycle.State = Lifecycle.State.Initialized + set(value) { + if (field == Lifecycle.State.Destroyed || value == Lifecycle.State.Initialized) { + return + } + field = value + dispatchState(value) + } + + private fun dispatchState(value: Lifecycle.State) { + observers.toMutableList().forEach { + it.onStateChanged(value) + } + } + + override fun removeObserver(observer: LifecycleObserver) { + observers.remove(observer) + } + + override fun addObserver(observer: LifecycleObserver) { + observers.add(observer) + } + + override fun hasObserver(): Boolean { + return observers.isNotEmpty() + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt new file mode 100644 index 000000000..cd07dcf84 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt @@ -0,0 +1,74 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import kotlin.coroutines.resume + +suspend fun Lifecycle.repeatOnLifecycle( + block: suspend CoroutineScope.() -> Unit +) { + if (currentState === Lifecycle.State.Destroyed) { + return + } + coroutineScope { + withContext(Dispatchers.Main.immediate) { + if (currentState === Lifecycle.State.Destroyed) return@withContext + var launchedJob: Job? = null + var observer: LifecycleObserver? = null + try { + suspendCancellableCoroutine { cont -> + object : LifecycleObserver { + override fun onStateChanged(state: Lifecycle.State) { + when (state) { + Lifecycle.State.Initialized -> Unit + Lifecycle.State.Active -> { + launchedJob = this@coroutineScope.launch(block = block) + } + Lifecycle.State.InActive -> { + launchedJob?.cancel() + launchedJob = null + } + Lifecycle.State.Destroyed -> { + cont.resume(Unit) + } + } + } + }.let { + observer = it + this@repeatOnLifecycle.addObserver(it) + } + } + } finally { + launchedJob?.cancel() + observer?.let { + this@repeatOnLifecycle.removeObserver(it) + } + } + } + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt similarity index 72% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt index 2442d6629..02e53687c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/BackStackEntry.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt @@ -20,12 +20,12 @@ */ package moe.tlaster.precompose.navigation -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.lifecycle.ViewModelStore -import androidx.lifecycle.ViewModelStoreOwner +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LifecycleOwner +import moe.tlaster.precompose.lifecycle.LifecycleRegistry import moe.tlaster.precompose.navigation.route.ComposeRoute +import moe.tlaster.precompose.viewmodel.ViewModelStore +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner class BackStackEntry internal constructor( val id: Long, @@ -36,38 +36,32 @@ class BackStackEntry internal constructor( ) : ViewModelStoreOwner, LifecycleOwner { private var destroyAfterTransition = false - override fun getViewModelStore(): ViewModelStore { - return viewModel.get(id = id) - } + override val viewModelStore: ViewModelStore + get() = viewModel.get(id = id) private val lifecycleRegistry by lazy { - LifecycleRegistry(this) + LifecycleRegistry() } - override fun getLifecycle(): Lifecycle { - return lifecycleRegistry - } + override val lifecycle: Lifecycle + get() = lifecycleRegistry fun active() { - lifecycleRegistry.currentState = Lifecycle.State.RESUMED + lifecycleRegistry.currentState = Lifecycle.State.Active } fun inActive() { - if (lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.RESUMED)) { - lifecycleRegistry.currentState = Lifecycle.State.STARTED - } + lifecycleRegistry.currentState = Lifecycle.State.InActive if (destroyAfterTransition) { destroy() } } fun destroy() { - if (lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.RESUMED) || - lifecycleRegistry.currentState == Lifecycle.State.INITIALIZED - ) { + if (lifecycleRegistry.currentState != Lifecycle.State.InActive) { destroyAfterTransition = true } else { - lifecycleRegistry.currentState = Lifecycle.State.DESTROYED + lifecycleRegistry.currentState = Lifecycle.State.Destroyed viewModelStore.clear() } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt similarity index 72% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt index e0a12e6e3..e038444de 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavControllerViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt @@ -20,9 +20,9 @@ */ package moe.tlaster.precompose.navigation -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelStore +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModelStore +import moe.tlaster.precompose.viewmodel.getViewModel internal class NavControllerViewModel : ViewModel() { private val viewModelStores = hashMapOf() @@ -46,15 +46,9 @@ internal class NavControllerViewModel : ViewModel() { companion object { fun create(viewModelStore: ViewModelStore): NavControllerViewModel { - return ViewModelProvider( - viewModelStore, - object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - @Suppress("UNCHECKED_CAST") - return NavControllerViewModel() as T - } - } - ).get(NavControllerViewModel::class.java) + return viewModelStore.getViewModel { + NavControllerViewModel() + } } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt index 1a9ccae8a..1ffcc3136 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavHost.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt @@ -20,19 +20,19 @@ */ package moe.tlaster.precompose.navigation -import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveableStateHolder -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import moe.tlaster.precompose.navigation.transition.AnimatedDialogRoute import moe.tlaster.precompose.navigation.transition.AnimatedRoute import moe.tlaster.precompose.navigation.transition.DialogTransition import moe.tlaster.precompose.navigation.transition.NavTransition +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner +import moe.tlaster.precompose.ui.LocalLifecycleOwner +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner /** * Provides in place in the Compose hierarchy for self contained navigation to occur. @@ -70,7 +70,7 @@ fun NavHost( val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { "NavHost requires a ViewModelStoreOwner to be provided via LocalViewModelStoreOwner" } - val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + val backDispatcher = LocalBackDispatcherOwner.current?.backDispatcher DisposableEffect(manager, lifecycleOwner, viewModelStoreOwner, backDispatcher) { manager.lifeCycleOwner = lifecycleOwner manager.setViewModelStore(viewModelStoreOwner.viewModelStore) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/NavOptions.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/Navigator.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/PopUpTo.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/QueryString.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt similarity index 79% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt index dc282a2a0..80bb3cc4b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteBuilder.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt @@ -65,5 +65,21 @@ class RouteBuilder( ) } - internal fun build() = RouteGraph(initialRoute, route.toList()) + fun addRoute(route: Route) { + this.route += route + } + + internal fun build(): RouteGraph { + if (initialRoute.isEmpty() && route.isEmpty()) { + // FIXME: 2021/4/2 Show warning + } else { + require(route.any { it.route == initialRoute }) { + "No initial route target fot this route graph" + } + } + require(!route.groupBy { it.route }.any { it.value.size > 1 }) { + "Duplicate route can not be applied" + } + return RouteGraph(initialRoute, route.toList()) + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteGraph.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatch.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteMatchResult.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteParser.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStack.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt similarity index 83% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt index 8c1aa3f1a..ca8128630 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/RouteStackManager.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt @@ -20,18 +20,18 @@ */ package moe.tlaster.precompose.navigation -import androidx.activity.OnBackPressedCallback -import androidx.activity.OnBackPressedDispatcher import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.saveable.SaveableStateHolder -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModelStore +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LifecycleObserver +import moe.tlaster.precompose.lifecycle.LifecycleOwner import moe.tlaster.precompose.navigation.route.ComposeRoute import moe.tlaster.precompose.navigation.route.DialogRoute import moe.tlaster.precompose.navigation.route.SceneRoute +import moe.tlaster.precompose.ui.BackDispatcher +import moe.tlaster.precompose.ui.BackHandler +import moe.tlaster.precompose.viewmodel.ViewModelStore import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -40,20 +40,15 @@ import kotlin.coroutines.suspendCoroutine internal class RouteStackManager( private val stateHolder: SaveableStateHolder, private val routeGraph: RouteGraph, -) : LifecycleEventObserver { +) : LifecycleObserver, BackHandler { // FIXME: 2021/4/1 Temp workaround for deeplink private var pendingNavigation: String? = null private val _suspendResult = linkedMapOf>() - private val backPressCallback: OnBackPressedCallback = object : OnBackPressedCallback(false) { - override fun handleOnBackPressed() { - goBack() - } - } - var backDispatcher: OnBackPressedDispatcher? = null + var backDispatcher: BackDispatcher? = null set(value) { - backPressCallback.remove() + field?.unregister(this) field = value - value?.addCallback(backPressCallback) + value?.register(this) } private var stackEntryId = Long.MIN_VALUE private var routeStackId = Long.MIN_VALUE @@ -149,13 +144,11 @@ internal class RouteStackManager( _backStacks.removeRange(0, _backStacks.lastIndex) } } - updateBackPressCallback() } fun goBack(result: Any? = null) { if (!canGoBack) { - updateBackPressCallback() - backDispatcher?.onBackPressed() + backDispatcher?.onBackPress() return } when { @@ -177,31 +170,23 @@ internal class RouteStackManager( }?.let { _suspendResult.remove(it)?.resume(result) } - updateBackPressCallback() } suspend fun waitingForResult(entry: BackStackEntry): Any? = suspendCoroutine { _suspendResult[entry] = it } - private fun updateBackPressCallback() { - backPressCallback.isEnabled = canGoBack - } - - override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { - when (event) { - Lifecycle.Event.ON_CREATE -> Unit - Lifecycle.Event.ON_START -> Unit - Lifecycle.Event.ON_RESUME -> currentStack?.onActive() - Lifecycle.Event.ON_PAUSE -> currentStack?.onInActive() - Lifecycle.Event.ON_STOP -> currentStack?.onInActive() - Lifecycle.Event.ON_DESTROY -> { + override fun onStateChanged(state: Lifecycle.State) { + when (state) { + Lifecycle.State.Initialized -> Unit + Lifecycle.State.Active -> currentStack?.onActive() + Lifecycle.State.InActive -> currentStack?.onInActive() + Lifecycle.State.Destroyed -> { _backStacks.forEach { it.onDestroyed() } _backStacks.clear() } - Lifecycle.Event.ON_ANY -> Unit } } @@ -209,6 +194,15 @@ internal class RouteStackManager( return _backStacks.indexOf(stack) } + override fun handleBackPress(): Boolean { + return if (canGoBack) { + goBack() + true + } else { + false + } + } + fun navigateInitial(initialRoute: String) { navigate(initialRoute) pendingNavigation?.let { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/ComposeRoute.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/DialogRoute.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt index 4308f0a10..5b4fc1f59 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/Route.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt @@ -20,7 +20,7 @@ */ package moe.tlaster.precompose.navigation.route -internal interface Route { +interface Route { val route: String @Deprecated("store path key in route node in order to match different links in one route") val pathKeys: List diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/route/SceneRoute.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt index 3ec5c208d..46b6b3771 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedDialogRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt @@ -34,7 +34,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer -import androidx.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.navigation.BackStackEntry import moe.tlaster.precompose.navigation.RouteStack @@ -64,8 +64,8 @@ internal fun AnimatedDialogRoute( .takeIf { it >= 0 || // Workaround for navOptions - targetState?.lifecycle?.currentState == Lifecycle.State.INITIALIZED && - previousState?.lifecycle?.currentState == Lifecycle.State.RESUMED + targetState?.lifecycle?.currentState == Lifecycle.State.Initialized && + previousState?.lifecycle?.currentState == Lifecycle.State.Active } ?: Int.MAX_VALUE // Only manipulate the list when the state is changed, or in the first run. val keys = items.map { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt index da60842fc..e28e8b211 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/AnimatedRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt @@ -34,7 +34,7 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer -import androidx.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.navigation.RouteStack import moe.tlaster.precompose.navigation.RouteStackManager @@ -59,8 +59,8 @@ internal fun AnimatedRoute( .takeIf { it >= 0 || // Workaround for navOptions - targetState.currentEntry?.lifecycle?.currentState == Lifecycle.State.INITIALIZED && - previousState.currentEntry?.lifecycle?.currentState == Lifecycle.State.RESUMED + targetState.currentEntry?.lifecycle?.currentState == Lifecycle.State.Initialized && + previousState.currentEntry?.lifecycle?.currentState == Lifecycle.State.Active } ?: Int.MAX_VALUE val actualNavTransition = run { if (indexOfNew >= indexOfOld) targetState else previousState diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/DialogTransition.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/navigation/transition/NavTransition.kt rename to common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt new file mode 100644 index 000000000..c84bd7e2c --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt @@ -0,0 +1,54 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.ui + +import androidx.compose.runtime.compositionLocalOf + +val LocalBackDispatcherOwner = compositionLocalOf { null } + +interface BackDispatcherOwner { + val backDispatcher: BackDispatcher +} + +class BackDispatcher { + private val handlers = arrayListOf() + + fun onBackPress(): Boolean { + for (it in handlers) { + if (it.handleBackPress()) { + return true + } + } + return false + } + + internal fun register(handler: BackHandler) { + handlers.add(0, handler) + } + + internal fun unregister(handler: BackHandler) { + handlers.remove(handler) + } +} + +interface BackHandler { + fun handleBackPress(): Boolean +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt new file mode 100644 index 000000000..0f49066b7 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.ui + +import androidx.compose.runtime.compositionLocalOf +import moe.tlaster.precompose.lifecycle.LifecycleOwner +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner + +val LocalLifecycleOwner = compositionLocalOf { null } + +val LocalViewModelStoreOwner = compositionLocalOf { null } + +private fun noLocalProvidedFor(name: String): Nothing { + error("CompositionLocal $name not present") +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt new file mode 100644 index 000000000..ab4ecfe1c --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner +import kotlin.reflect.KClass + +@Composable +inline fun viewModel( + keys: List = emptyList(), + noinline creator: () -> T, +): T = viewModel(T::class, keys, creator = creator) + +@Composable +fun viewModel( + modelClass: KClass, + keys: List = emptyList(), + creator: () -> T, +): T { + val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { + "Require LocalViewModelStoreOwner not null for $modelClass" + } + return remember( + modelClass, keys, creator, viewModelStoreOwner + ) { + viewModelStoreOwner.getViewModel(keys, modelClass = modelClass, creator = creator) + } +} + +private fun ViewModelStoreOwner.getViewModel( + keys: List = emptyList(), + modelClass: KClass, + creator: () -> T, +): T { + val key = (keys.map { it.hashCode().toString() } + modelClass.qualifiedName).joinToString() + val existing = viewModelStore[key] + if (existing != null && modelClass.isInstance(existing)) { + @Suppress("UNCHECKED_CAST") + return existing as T + } else { + @Suppress("ControlFlowWithEmptyBody") + if (existing != null) { + // TODO: log a warning. + } + } + val viewModel = creator.invoke() + viewModelStore.put(key, viewModel) + return viewModel +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt new file mode 100644 index 000000000..ad9f8d396 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt @@ -0,0 +1,57 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import java.io.Closeable +import kotlin.coroutines.CoroutineContext + +private const val JOB_KEY = "moe.tlaster.precompose.viewmodel.ViewModelCoroutineScope.JOB_KEY" + +/** + * [CoroutineScope] tied to this [ViewModel]. + * This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called + * + * This scope is bound to + * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] + */ +val ViewModel.viewModelScope: CoroutineScope + get() { + val scope: CoroutineScope? = getTag(JOB_KEY) + if (scope != null) { + return scope + } + return setTagIfAbsent( + JOB_KEY, + CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + ) + } + +internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope { + override val coroutineContext: CoroutineContext = context + + override fun close() { + coroutineContext.cancel() + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt new file mode 100644 index 000000000..8b974dd70 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt @@ -0,0 +1,63 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +import java.io.Closeable + +abstract class ViewModel { + @Volatile + private var disposed = false + private val bagOfTags = hashMapOf() + + protected open fun onCleared() {} + + fun clear() { + disposed = true + bagOfTags.let { + for (value in it.values) { + disposeWithRuntimeException(value) + } + } + onCleared() + } + + open fun setTagIfAbsent(key: String, newValue: T): T { + @Suppress("UNCHECKED_CAST") + return bagOfTags.getOrPut(key) { + newValue as Any + }.also { + if (disposed) { + disposeWithRuntimeException(it) + } + } as T + } + + open fun getTag(key: String): T? { + @Suppress("UNCHECKED_CAST") + return bagOfTags[key] as T? + } + + private fun disposeWithRuntimeException(obj: Any) { + if (obj is Closeable) { + obj.close() + } + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt new file mode 100644 index 000000000..8562bd852 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt @@ -0,0 +1,47 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +internal inline fun ViewModelStore.getViewModel( + creator: () -> T, +): T { + val key = T::class.qualifiedName.toString() + return getViewModel(key, creator) +} + +internal inline fun ViewModelStore.getViewModel( + key: String, + creator: () -> T, +): T { + val existing = get(key) + if (existing != null && existing is T) { + @Suppress("UNCHECKED_CAST") + return existing + } else { + @Suppress("ControlFlowWithEmptyBody") + if (existing != null) { + // TODO: log a warning. + } + } + val viewModel = creator.invoke() + put(key, viewModel) + return viewModel +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt new file mode 100644 index 000000000..c1b2df8b9 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt @@ -0,0 +1,45 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +class ViewModelStore { + private val map = hashMapOf() + + fun put(key: String, viewModel: ViewModel) { + val oldViewModel = map.put(key, viewModel) + oldViewModel?.clear() + } + + operator fun get(key: String): ViewModel? { + return map[key] + } + + fun keys(): Set { + return HashSet(map.keys) + } + + fun clear() { + for (vm in map.values) { + vm.clear() + } + map.clear() + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt new file mode 100644 index 000000000..4ad9843b6 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +interface ViewModelStoreOwner { + val viewModelStore: ViewModelStore +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt new file mode 100644 index 000000000..0b228c35c --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +import kotlin.test.Test +import kotlin.test.assertEquals + +class LifecycleTest { + @Test + fun lifeCycleTest() { + val lifecycleOwner = TestLifecycleOwner() + assertEquals(Lifecycle.State.Initialized, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active + assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.Initialized + assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.InActive + assertEquals(Lifecycle.State.InActive, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active + assertEquals(Lifecycle.State.Active, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.Destroyed + assertEquals(Lifecycle.State.Destroyed, lifecycleOwner.lifecycle.currentState) + lifecycleOwner.lifecycle.currentState = Lifecycle.State.Active + assertEquals(Lifecycle.State.Destroyed, lifecycleOwner.lifecycle.currentState) + } +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt new file mode 100644 index 000000000..5f51bb9aa --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.lifecycle + +class TestLifecycleOwner : LifecycleOwner { + override val lifecycle by lazy { + LifecycleRegistry() + } +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt new file mode 100644 index 000000000..a4b608e3a --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt @@ -0,0 +1,109 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.navigation + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class QueryStringTest { + @Test + fun simpleQueryString() { + QueryString("&foo=bar").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("foo")) + assertTrue(it.map.containsValue(listOf("bar"))) + assertEquals("bar", it.query("foo")) + } + QueryString("foo=bar&").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("foo")) + assertTrue(it.map.containsValue(listOf("bar"))) + assertEquals("bar", it.query("foo")) + } + QueryString("foo=bar&&").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("foo")) + assertTrue(it.map.containsValue(listOf("bar"))) + assertEquals("bar", it.query("foo")) + } + QueryString("foo=bar").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("foo")) + assertTrue(it.map.containsValue(listOf("bar"))) + assertEquals("bar", it.query("foo")) + } + QueryString("a=1&b=2").let { + assertTrue(it.map.size == 2) + assertTrue(it.map.containsKey("a")) + assertTrue(it.map.containsValue(listOf("1"))) + assertTrue(it.map.containsKey("b")) + assertTrue(it.map.containsValue(listOf("2"))) + assertEquals("1", it.query("a")) + assertEquals("2", it.query("b")) + } + QueryString("a=1&b=2&").let { + assertTrue(it.map.size == 2) + assertTrue(it.map.containsKey("a")) + assertTrue(it.map.containsValue(listOf("1"))) + assertTrue(it.map.containsKey("b")) + assertTrue(it.map.containsValue(listOf("2"))) + assertEquals("1", it.query("a")) + assertEquals("2", it.query("b")) + } + QueryString("a=1&&b=2&").let { + assertTrue(it.map.size == 2) + assertTrue(it.map.containsKey("a")) + assertTrue(it.map.containsValue(listOf("1"))) + assertTrue(it.map.containsKey("b")) + assertTrue(it.map.containsValue(listOf("2"))) + assertEquals("1", it.query("a")) + assertEquals("2", it.query("b")) + } + QueryString("a=1&a=2").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("a")) + assertTrue(it.map.containsValue(listOf("1", "2"))) + assertEquals(listOf("1", "2"), it.queryList("a").filterNotNull()) + } + QueryString("a=1;a=2").let { + assertTrue(it.map.isEmpty()) + } + QueryString("a=").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("a")) + assertEquals(emptyList(), it.queryList("a").filterNotNull()) + } + QueryString("a=&").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("a")) + assertEquals(emptyList(), it.queryList("a").filterNotNull()) + } + QueryString("a=&&").let { + assertTrue(it.map.size == 1) + assertTrue(it.map.containsKey("a")) + assertEquals(emptyList(), it.queryList("a").filterNotNull()) + } + QueryString("").let { + assertTrue(it.map.isEmpty()) + } + } +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt new file mode 100644 index 000000000..8b192987c --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.navigation + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class RouteBuilderTest { + @Test + fun testInitialRouteNotInRoutes() { + assertFailsWith(IllegalArgumentException::class, "No initial route target fot this route graph") { + RouteBuilder("/home").build() + } + assertFailsWith(IllegalArgumentException::class, "No initial route target fot this route graph") { + RouteBuilder("/home").apply { + testRoute("/detail", "1") + }.build() + } + } + + @Test + fun testEmptyRoute() { + val graph = RouteBuilder("").build() + assertTrue(graph.routes.isEmpty()) + } + + @Test + fun testSingleRoute() { + RouteBuilder("/home").apply { + testRoute("/home", "home") + }.build().apply { + assertTrue(routes.size == 1) + routes.first().let { + assertTrue(it is TestRoute) + assertEquals("/home", it.route) + assertEquals("home", it.id) + } + } + } + + @Test + fun testMultipleRouteWithSameRoute() { + assertFailsWith(IllegalArgumentException::class, "Duplicate route can not be applied") { + RouteBuilder("/home").apply { + testRoute("/home", "home") + testRoute("/home", "home") + }.build() + } + } +} diff --git a/android/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt similarity index 100% rename from android/src/test/java/moe/tlaster/precompose/navigation/RouteParserTest.kt rename to common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt new file mode 100644 index 000000000..dd0972650 --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt @@ -0,0 +1,296 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.navigation + +import moe.tlaster.precompose.navigation.route.Route +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class RouteParserTest2 { + @Test + fun pathKeys() { + pathKeys("/{lang:[a-z]{2}}") { keys -> assertEquals(listOf("lang"), keys) } + + pathKeys("/edit/{id}?") { keys -> assertEquals(listOf("id"), keys) } + + pathKeys("/path/{id}/{start}?/{end}?") { keys -> assertEquals(listOf("id", "start", "end"), keys) } + + pathKeys("/*") { keys -> assertEquals(1, keys.size) } + + pathKeys("/foo/?*") { keys -> assertEquals(1, keys.size) } + + pathKeys("/foo") { keys -> assertEquals(0, keys.size) } + pathKeys("/") { keys -> assertEquals(0, keys.size) } + pathKeys("/foo/bar") { keys -> assertEquals(0, keys.size) } + pathKeys("/foo/*") { keys -> assertEquals(1, keys.size) } + pathKeys("/foo/*name") { keys -> assertEquals(1, keys.size) } + pathKeys("/foo/{x}") { keys -> assertEquals(1, keys.size) } + + pathKeys("aaa://{lang:[a-z]{2}}") { keys -> assertEquals(listOf("lang"), keys) } + pathKeys("bbb://path/{id}/{start}?/{end}?") { keys -> assertEquals(listOf("id", "start", "end"), keys) } + } + + @Test + fun pathKeyMap() { + pathKeyMap("/{lang:[a-z]{2}}") { map -> assertEquals("[a-z]{2}", map["lang"]) } + pathKeyMap("/{id:[0-9]+}") { map -> assertEquals("[0-9]+", map["id"]) } + pathKeyMap("/edit/{id}?") { keys -> assertEquals(null, keys["id"]) } + pathKeyMap("/path/{id}/{start}?/{end}?") { keys -> + assertEquals(null, keys["id"]) + assertEquals(null, keys["start"]) + assertEquals(null, keys["end"]) + } + pathKeyMap("/*") { keys -> assertEquals("\\.*", keys["*"]) } + pathKeyMap("/foo/?*") { keys -> assertEquals("\\.*", keys["*"]) } + pathKeyMap("/foo/*name") { keys -> assertEquals("\\.*", keys["name"]) } + + pathKeyMap("aaa://foo/?*") { keys -> assertEquals("\\.*", keys["*"]) } + pathKeyMap("bbb://foo/*name") { keys -> assertEquals("\\.*", keys["name"]) } + } + + private fun pathKeys(pattern: String, consumer: (List) -> Unit) { + consumer.invoke(RouteParser.pathKeys(pattern)) + } + + private fun pathKeyMap(pattern: String, consumer: (Map) -> Unit) { + val map: MutableMap = HashMap() + RouteParser.pathKeys(pattern) { key: String, value: String? -> map[key] = value } + consumer.invoke(map) + } + + @Test + fun wildOnRoot() { + val parser = RouteParser() + parser.insert(route("/foo/?*", "foo")) + parser.insert(route("/bar/*", "bar")) + parser.insert(route("/*", "root")) + parser.find("/").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("root", it.route.id) + } + parser.find("/foo").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("foo", it.route.id) + } + parser.find("/bar").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("root", it.route.id) + } + parser.find("/foox").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("root", it.route.id) + } + parser.find("/foo/").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("foo", it.route.id) + } + parser.find("/foo/x").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("foo", it.route.id) + } + parser.find("/bar/x").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("bar", it.route.id) + } + } + + @Test + fun searchString() { + val parser = RouteParser() + parser.insert(route("/regex/{nid:[0-9]+}", "nid")) + parser.insert(route("/regex/{zid:[0-9]+}/edit", "zid")) + parser.insert(route("/articles/{id}", "id")) + parser.insert(route("/articles/*", "*")) + + parser.find("/regex/678/edit").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("zid", it.route.id) + } + parser.find("/articles/tail/match").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("*", it.route.id) + } + parser.find("/articles/123").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("id", it.route.id) + } + } + + @Test + fun searchParam() { + val parser = RouteParser() + parser.insert(route("/articles/{id}", "id")) + parser.insert(route("/articles/*", "catchall")) + + parser.find("/articles/123").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("id", it.route.id) + } + parser.find("/articles/a/b").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("catchall", it.route.id) + } + } + + @Test + fun multipleRegex() { + val parser = RouteParser() + parser.insert(route("/{lang:[a-z][a-z]}/{page:[^.]+}/", "1515")) + + parser.find("/12/f/").let { + assertNull(it) + } + parser.find("/ar/page/").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("1515", it.route.id) + } + parser.find("/arx/page/").let { + assertNull(it) + } + } + + @Test + fun regexWithQuantity() { + val parser = RouteParser() + parser.insert(route("/{lang:[a-z]{2}}/", "qx")) + parser.find("/12/").let { + assertNull(it) + } + parser.find("/ar/").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("qx", it.route.id) + } + } + + @Test + fun withSchema() { + val parser = RouteParser() + parser.insert(route("aaa://home", "1")) + parser.insert(route("bbb://home", "2")) + parser.find("bbb://home").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("2", it.route.id) + } + } + + @Test + fun withSchemaAndRegex() { + val parser = RouteParser() + parser.insert(route("aaa://home", "1")) + parser.insert(route("bbb://home", "2")) + parser.insert(route("aaa://home/{id:[0-9]+}", "3")) + parser.find("aaa://home/1232").let { + assertNotNull(it) + assertTrue(it.route is TestRoute) + assertEquals("3", it.route.id) + } + } + + @Test + fun shouldExpandOptionalParams() { + RouteParser.expandOptionalVariables("/{lang:[a-z]{2}}?").let { paths -> + assertEquals(2, paths.size) + assertEquals("/", paths.get(0)) + assertEquals("/{lang:[a-z]{2}}", paths.get(1)) + } + RouteParser.expandOptionalVariables("/{lang:[a-z]{2}}").let { paths -> + assertEquals(1, paths.size) + assertEquals("/{lang:[a-z]{2}}", paths.get(0)) + } + RouteParser.expandOptionalVariables("/edit/{id:[0-9]+}?").let { paths -> + assertEquals(2, paths.size) + assertEquals("/edit", paths.get(0)) + assertEquals("/edit/{id:[0-9]+}", paths.get(1)) + } + RouteParser.expandOptionalVariables("/path/{id}/{start}?/{end}?").let { paths -> + assertEquals(3, paths.size) + assertEquals("/path/{id}", paths.get(0)) + assertEquals("/path/{id}/{start}", paths.get(1)) + assertEquals("/path/{id}/{start}/{end}", paths.get(2)) + } + RouteParser.expandOptionalVariables("/{id}?/suffix").let { paths -> + assertEquals(3, paths.size) + assertEquals("/", paths.get(0)) + assertEquals("/{id}/suffix", paths.get(1)) + assertEquals("/suffix", paths.get(2)) + } + RouteParser.expandOptionalVariables("/prefix/{id}?").let { paths -> + assertEquals(2, paths.size) + assertEquals("/prefix", paths.get(0)) + assertEquals("/prefix/{id}", paths.get(1)) + } + RouteParser.expandOptionalVariables("/{id}?").let { paths -> + assertEquals(2, paths.size) + assertEquals("/", paths.get(0)) + assertEquals("/{id}", paths.get(1)) + } + RouteParser.expandOptionalVariables("/path").let { paths -> + assertEquals(1, paths.size) + assertEquals("/path", paths.get(0)) + } + RouteParser.expandOptionalVariables("/path/subpath").let { paths -> + assertEquals(1, paths.size) + assertEquals("/path/subpath", paths.get(0)) + } + RouteParser.expandOptionalVariables("/{id}").let { paths -> + assertEquals(1, paths.size) + assertEquals("/{id}", paths.get(0)) + } + RouteParser.expandOptionalVariables("/{id}/suffix").let { paths -> + assertEquals(1, paths.size) + assertEquals("/{id}/suffix", paths.get(0)) + } + RouteParser.expandOptionalVariables("/prefix/{id}").let { paths -> + assertEquals(1, paths.size) + assertEquals("/prefix/{id}", paths.get(0)) + } + RouteParser.expandOptionalVariables("/").let { paths -> + assertEquals(1, paths.size) + assertEquals("/", paths.get(0)) + } + RouteParser.expandOptionalVariables("").let { paths -> + assertEquals(1, paths.size) + assertEquals("/", paths.get(0)) + } + } + + private fun route(path: String, id: String): Route { + return TestRoute(path, id) + } +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt new file mode 100644 index 000000000..9a3c1d172 --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.navigation + +import moe.tlaster.precompose.navigation.route.Route + +class TestRoute( + override val route: String, + val id: String, + override val pathKeys: List = emptyList(), +) : Route + +fun RouteBuilder.testRoute( + route: String, + id: String, +) { + addRoute( + TestRoute(route = route, id = id) + ) +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt new file mode 100644 index 000000000..e08683b38 --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt @@ -0,0 +1,53 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class ViewModelStoreTest { + @Test + fun testClear() { + val store = ViewModelStore() + val viewModel1 = TestViewModel() + val viewModel2 = TestViewModel() + val mockViewModel = TestViewModel() + store.put("a", viewModel1) + store.put("b", viewModel2) + store.put("mock", mockViewModel) + assertFalse(viewModel1.cleared) + assertFalse(viewModel2.cleared) + store.clear() + assertTrue(viewModel1.cleared) + assertTrue(viewModel2.cleared) + assertNull(store["a"]) + assertNull(store["b"]) + } + + internal class TestViewModel : ViewModel() { + var cleared = false + override fun onCleared() { + cleared = true + } + } +} diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt new file mode 100644 index 000000000..d643b5732 --- /dev/null +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt @@ -0,0 +1,63 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose.viewmodel + +import java.io.Closeable +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class ViewModelTest { + internal class CloseableImpl : Closeable { + var wasDisposable = false + + override fun close() { + wasDisposable = true + } + } + + internal class ViewModel : moe.tlaster.precompose.viewmodel.ViewModel() + + @Test + fun testCloseableTag() { + val vm = ViewModel() + val impl = CloseableImpl() + vm.setTagIfAbsent("totally_not_coroutine_context", impl) + vm.clear() + assertTrue(impl.wasDisposable) + } + + @Test + fun testCloseableTagAlreadyClearedVM() { + val vm = ViewModel() + vm.clear() + val impl = CloseableImpl() + vm.setTagIfAbsent("key", impl) + assertTrue(impl.wasDisposable) + } + + @Test + fun testAlreadyAssociatedKey() { + val vm = ViewModel() + assertEquals("first", vm.setTagIfAbsent("key", "first")) + assertEquals("first", vm.setTagIfAbsent("key", "second")) + } +} diff --git a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt new file mode 100644 index 000000000..36b33951d --- /dev/null +++ b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt @@ -0,0 +1,105 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.precompose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.window.FrameWindowScope +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.rememberWindowState +import moe.tlaster.precompose.lifecycle.LifecycleOwner +import moe.tlaster.precompose.lifecycle.LifecycleRegistry +import moe.tlaster.precompose.ui.BackDispatcher +import moe.tlaster.precompose.ui.BackDispatcherOwner +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner +import moe.tlaster.precompose.ui.LocalLifecycleOwner +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.ViewModelStore +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner + +@Composable +fun PreComposeWindow( + onCloseRequest: () -> Unit, + state: WindowState = rememberWindowState(), + visible: Boolean = true, + title: String = "Untitled", + icon: Painter? = null, + undecorated: Boolean = false, + resizable: Boolean = true, + enabled: Boolean = true, + focusable: Boolean = true, + alwaysOnTop: Boolean = false, + onPreviewKeyEvent: (KeyEvent) -> Boolean = { false }, + onKeyEvent: (KeyEvent) -> Boolean = { false }, + content: @Composable FrameWindowScope.() -> Unit +) { + Window( + onCloseRequest, + state, + visible, + title, + icon, + undecorated, + resizable, + enabled, + focusable, + alwaysOnTop, + onPreviewKeyEvent, + onKeyEvent, + content = { + ProvideDesktopCompositionLocals { + content.invoke(this) + } + }, + ) +} + +@Composable +private fun ProvideDesktopCompositionLocals( + content: @Composable () -> Unit, +) { + val holder = remember { + PreComposeWindowHolder() + } + CompositionLocalProvider( + LocalLifecycleOwner provides holder, + LocalViewModelStoreOwner provides holder, + LocalBackDispatcherOwner provides holder, + ) { + content.invoke() + } +} + +private class PreComposeWindowHolder : LifecycleOwner, ViewModelStoreOwner, BackDispatcherOwner { + override val lifecycle by lazy { + LifecycleRegistry() + } + override val viewModelStore by lazy { + ViewModelStore() + } + override val backDispatcher by lazy { + BackDispatcher() + } +} From 9961e3e40fadbe8a0635c2f4e6bc61a5927cba10 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 14:57:58 +0800 Subject: [PATCH 011/615] update androidx lifecycle dependency --- common/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 9118d4021..f3ad6b5d2 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -44,7 +44,7 @@ kotlin { } val androidMain by getting { dependencies { - api("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01") + api("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") api("androidx.savedstate:savedstate-ktx:1.1.0") } } From aacefb7ad0455524686fc7d7e70899d9f47d1934 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 16:36:03 +0800 Subject: [PATCH 012/615] migrate preferences to common --- .../preferences/AppearancePreferences.kt | 47 +++++++++++++++++++ .../preferences/DisplayPreferences.kt | 45 ++++++++++++++++++ .../twiderex/preferences/MiscPreferences.kt | 40 ++++++++++++++++ .../preferences/NotificationPreferences.kt | 28 +++++++++++ .../preferences/ProvidePreferences.kt | 12 ++--- .../AppearancePreferencesSerializer.kt | 24 ++++------ .../DisplayPreferencesSerializer.kt | 26 ++++------ .../serializer/MiscPreferencesSerializer.kt | 20 ++++---- .../NotificationPreferencesSerializer.kt | 23 ++++----- 9 files changed, 200 insertions(+), 65 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt (67%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt (65%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt (69%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt (67%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt new file mode 100644 index 000000000..ffb3004c2 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt @@ -0,0 +1,47 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.preferences + +import kotlinx.serialization.Serializable + +@Serializable +data class AppearancePreferences( + val primaryColorIndex: Int = 0, + val tapPosition: TabPosition = TabPosition.Bottom, + val theme: Theme = Theme.Auto, + val hideTabBarWhenScroll: Boolean = false, + val hideFabWhenScroll: Boolean = false, + val hideAppBarWhenScroll: Boolean = false, + val isDarkModePureBlack: Boolean = false, +) { + @Serializable + enum class TabPosition { + Top, + Bottom, + } + + @Serializable + enum class Theme { + Auto, + Light, + Dark, + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt new file mode 100644 index 000000000..646f33f24 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt @@ -0,0 +1,45 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.preferences + +import kotlinx.serialization.Serializable + +@Serializable +data class DisplayPreferences( + val useSystemFontSize: Boolean = true, + val fontScale: Float = 1f, + val avatarStyle: AvatarStyle = AvatarStyle.Round, + val mediaPreview: Boolean = true, + val autoPlayback: AutoPlayback = AutoPlayback.Auto, + val urlPreview: Boolean = false, +) { + @Serializable + enum class AvatarStyle { + Round, + Square, + } + @Serializable + enum class AutoPlayback { + Auto, + Always, + Off, + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt new file mode 100644 index 000000000..d684c40b8 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt @@ -0,0 +1,40 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.preferences + +import kotlinx.serialization.Serializable + +@Serializable +data class MiscPreferences( + val nitterInstance: String = "", + val useProxy: Boolean = false, + val proxyType: ProxyType = ProxyType.HTTP, + val proxyServer: String = "", + val proxyPort: Int = 0, + val proxyUserName: String = "", + val proxyPassword: String = "", +) { + @Serializable + enum class ProxyType { + HTTP, + REVERSE, + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt new file mode 100644 index 000000000..7e41b2ba4 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.preferences + +import kotlinx.serialization.Serializable + +@Serializable +data class NotificationPreferences( + val enableNotification: Boolean = true, +) diff --git a/android/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt index 975f58c52..f0b90cab9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt @@ -28,21 +28,16 @@ import androidx.compose.runtime.getValue import androidx.datastore.core.DataStore import com.twidere.services.http.config.HttpConfig import com.twidere.services.proxy.ProxyConfig -import com.twidere.twiderex.preferences.proto.AppearancePreferences -import com.twidere.twiderex.preferences.proto.DisplayPreferences -import com.twidere.twiderex.preferences.proto.MiscPreferences -import com.twidere.twiderex.ui.LocalVideoPlayback import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map -import javax.inject.Inject val LocalAppearancePreferences = compositionLocalOf { error("No AppearancePreferences") } val LocalDisplayPreferences = compositionLocalOf { error("No DisplayPreferences") } val LocalHttpConfig = compositionLocalOf { error("No Http config preferences") } -data class PreferencesHolder @Inject constructor( +data class PreferencesHolder( val appearancePreferences: DataStore, val displayPreferences: DataStore, val miscPreferences: DataStore @@ -63,10 +58,10 @@ fun ProvidePreferences( ) { val appearances by holder.appearancePreferences .data - .collectAsState(initial = AppearancePreferences.getDefaultInstance()) + .collectAsState(initial = AppearancePreferences()) val display by holder.displayPreferences .data - .collectAsState(initial = DisplayPreferences.getDefaultInstance()) + .collectAsState(initial = DisplayPreferences()) val proxyConfig by holder.miscPreferences .data .map { @@ -89,7 +84,6 @@ fun ProvidePreferences( CompositionLocalProvider( LocalAppearancePreferences provides appearances, LocalDisplayPreferences provides display, - LocalVideoPlayback provides display.autoPlayback, LocalHttpConfig provides proxyConfig ) { content.invoke() diff --git a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt similarity index 67% rename from android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt index 3539673cf..2ceb27cad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt @@ -20,31 +20,25 @@ */ package com.twidere.twiderex.preferences.serializer -import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.AppearancePreferences +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf import java.io.InputStream import java.io.OutputStream +@OptIn(ExperimentalSerializationApi::class) object AppearancePreferencesSerializer : Serializer { override suspend fun readFrom(input: InputStream): AppearancePreferences { - try { - return AppearancePreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + return ProtoBuf.decodeFromByteArray(input.readAllBytes()) } - override suspend fun writeTo( t: AppearancePreferences, output: OutputStream - ) = t.writeTo(output) + ) = output.write(ProtoBuf.encodeToByteArray(t)) override val defaultValue: AppearancePreferences - get() = AppearancePreferences - .getDefaultInstance() - .toBuilder() - .setTapPosition(AppearancePreferences.TabPosition.Bottom) - .build() + get() = AppearancePreferences() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt similarity index 65% rename from android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt index 90bfed8be..997acde65 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt @@ -20,30 +20,24 @@ */ package com.twidere.twiderex.preferences.serializer -import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.DisplayPreferences +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf import java.io.InputStream import java.io.OutputStream +@OptIn(ExperimentalSerializationApi::class) object DisplayPreferencesSerializer : Serializer { override val defaultValue: DisplayPreferences - get() = DisplayPreferences - .getDefaultInstance() - .toBuilder() - .setMediaPreview(true) - .setUseSystemFontSize(true) - .setFontScale(1F) - .build() + get() = DisplayPreferences() override suspend fun readFrom(input: InputStream): DisplayPreferences { - try { - return DisplayPreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + return ProtoBuf.decodeFromByteArray(input.readAllBytes()) } - override suspend fun writeTo(t: DisplayPreferences, output: OutputStream) = t.writeTo(output) + override suspend fun writeTo(t: DisplayPreferences, output: OutputStream) = + output.write(ProtoBuf.encodeToByteArray(t)) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt similarity index 69% rename from android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt index 3056c76cf..9e9bf3657 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt @@ -20,26 +20,22 @@ */ package com.twidere.twiderex.preferences.serializer -import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import com.twidere.twiderex.preferences.proto.MiscPreferences +import com.twidere.twiderex.preferences.MiscPreferences +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf import java.io.InputStream import java.io.OutputStream object MiscPreferencesSerializer : Serializer { override val defaultValue: MiscPreferences - get() = MiscPreferences.getDefaultInstance() - .toBuilder() - .build() + get() = MiscPreferences() override suspend fun readFrom(input: InputStream): MiscPreferences { - try { - return MiscPreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + return ProtoBuf.decodeFromByteArray(input.readAllBytes()) } - override suspend fun writeTo(t: MiscPreferences, output: OutputStream) = t.writeTo(output) + override suspend fun writeTo(t: MiscPreferences, output: OutputStream) = + output.write(ProtoBuf.encodeToByteArray(t)) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt similarity index 67% rename from android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt index 3b873bf63..4927c3b50 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt @@ -20,27 +20,24 @@ */ package com.twidere.twiderex.preferences.serializer -import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import com.twidere.twiderex.preferences.proto.NotificationPreferences +import com.twidere.twiderex.preferences.NotificationPreferences +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf import java.io.InputStream import java.io.OutputStream +@OptIn(ExperimentalSerializationApi::class) object NotificationPreferencesSerializer : Serializer { override val defaultValue: NotificationPreferences - get() = NotificationPreferences.getDefaultInstance() - .toBuilder() - .setEnableNotification(true) - .build() + get() = NotificationPreferences() override suspend fun readFrom(input: InputStream): NotificationPreferences { - try { - return NotificationPreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + return ProtoBuf.decodeFromByteArray(input.readAllBytes()) } - override suspend fun writeTo(t: NotificationPreferences, output: OutputStream) = t.writeTo(output) + override suspend fun writeTo(t: NotificationPreferences, output: OutputStream) = + output.write(ProtoBuf.encodeToByteArray(t)) } From 8caf8827228fcb9c579ec6b205b58b2aa7fb6e38 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 3 Aug 2021 16:52:02 +0800 Subject: [PATCH 013/615] add services dependency to common --- common/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 16ff283a8..7933ea4c1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -30,6 +30,7 @@ kotlin { api(compose.runtime) api(compose.foundation) api(compose.material) + implementation(projects.services) api("androidx.paging:paging-common:${Versions.paging}") api("androidx.datastore:datastore-core:${Versions.datastore}") api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") From 5bbcdbcfb131813c3adb4db3416d5c24855a5ffa Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 16:10:47 +0800 Subject: [PATCH 014/615] upgrade compose jb to 1.0.0-alpha2 --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index ae1e6add9..063914602 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -21,7 +21,7 @@ object Versions { const val retrofit2 = "2.9.0" const val hson = "0.1.4" const val compose = "1.1.0-alpha01" - const val compose_jb = "1.0.0-alpha1-rc1" + const val compose_jb = "1.0.0-alpha2" const val constraintLayout = "1.0.0-beta01" const val paging = "3.1.0-alpha03" const val paging_compose = "1.0.0-alpha12" From ff4574e230908bb27321e2c96387c2c71affa779 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 16:37:59 +0800 Subject: [PATCH 015/615] add koin dependency --- buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 1 + 2 files changed, 2 insertions(+) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 063914602..bc68b080a 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -46,4 +46,5 @@ object Versions { const val androidx_test = "1.4.0" const val extJUnitVersion = "1.1.3-rc01" const val espressoVersion = "3.4.0-rc01" + const val koin = "3.1.2" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7933ea4c1..d11ca84cc 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}") api("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${Versions.Kotlin.serialization}") + api("io.insert-koin:koin-core:${Versions.koin}") } } val commonTest by getting { From 0da5665c41bde0fbfb552fdeabb3dd78e8298cd2 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 17:13:56 +0800 Subject: [PATCH 016/615] add koin dependency for android --- common/build.gradle.kts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index d11ca84cc..b97781426 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -44,7 +44,11 @@ kotlin { implementation(kotlin("test")) } } - val androidMain by getting + val androidMain by getting { + dependencies { + api("io.insert-koin:koin-android:${Versions.koin}") + } + } val androidTest by getting val desktopMain by getting val desktopTest by getting From 3eb8dd271ae34276cb82f570d8357da8c0afaf87 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 17:14:06 +0800 Subject: [PATCH 017/615] add start koin to desktop main --- .../kotlin/com/twidere/twiderex/main.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index 5e39c8703..c97cd12be 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -21,11 +21,25 @@ package com.twidere.twiderex import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.singleWindowApplication +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import org.koin.core.context.startKoin +import org.koin.core.context.stopKoin @ExperimentalComposeUiApi -fun main() = singleWindowApplication( - title = "Twidere X" -) { - App() +fun main() { + startKoin { + printLogger() + } + application { + Window( + onCloseRequest = { + stopKoin() + exitApplication() + }, + title = "Twidere X" + ) { + App() + } + } } From 7782afc4451493e938d64ed02ec0efe8c8dddceb Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 17:39:56 +0800 Subject: [PATCH 018/615] update package to deb --- desktop/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 0dc095f22..373c07ae1 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -31,7 +31,7 @@ compose { application { mainClass = "com.twidere.twiderex.MainKt" nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.AppImage) + targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Deb) packageName = Package.id packageVersion = Package.versionName.split("-").firstOrNull() } From eb8b156c081c56b70cb7c4357a51b22dcb4bba88 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 17:47:16 +0800 Subject: [PATCH 019/615] upgrade github actions --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5cda18337..cf759989b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: build-reports - path: ./app/build/reports + path: '**/build/reports' unit-test: runs-on: ubuntu-latest From 09aaddb820fa7faf90ae1670debcdd7aa8aaf091 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 5 Aug 2021 18:39:53 +0800 Subject: [PATCH 020/615] add modules setup --- .../twidere/twiderex/TwidereApplication.kt | 38 +++++++++++++++++++ .../kotlin/com/twidere/twiderex/di/Setup.kt | 24 ++++++++++++ .../kotlin/com/twidere/twiderex/main.kt | 2 + 3 files changed, 64 insertions(+) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt new file mode 100644 index 000000000..bf154078e --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex + +import android.app.Application +import com.twidere.twiderex.di.setupModules +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin + +abstract class TwidereApplication : Application() { + override fun onCreate() { + super.onCreate() + startKoin { + androidLogger() + androidContext(this@TwidereApplication) + setupModules() + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt new file mode 100644 index 000000000..a94e01659 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +fun setupModules() { +} diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index c97cd12be..03e5b7ed8 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.Window import androidx.compose.ui.window.application +import com.twidere.twiderex.di.setupModules import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -30,6 +31,7 @@ import org.koin.core.context.stopKoin fun main() { startKoin { printLogger() + setupModules() } application { Window( From 7b98edb04126378bfcd312797a44c59484e3db04 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 6 Aug 2021 11:31:13 +0800 Subject: [PATCH 021/615] migrate CacheRepository to commonMain --- .../com/twidere/twiderex/cache/MediaCache.kt | 25 ++++++++ .../com/twidere/twiderex/db/dao/SearchDao.kt | 4 +- .../twiderex/repository/CacheRepository.kt | 63 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt new file mode 100644 index 000000000..ffa7fc130 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.cache + +expect class MediaCache { + fun clear() +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt index 8a8c5aa7b..2b0a7fe0d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt @@ -21,4 +21,6 @@ package com.twidere.twiderex.db.dao // TODO OPERATION -interface SearchDao +interface SearchDao { + fun clear() +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt new file mode 100644 index 000000000..c2b63aba4 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -0,0 +1,63 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.cache.MediaCache +import com.twidere.twiderex.dataprovider.DataProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import okhttp3.Cache +import java.io.File + +class CacheRepository( + private val dataProvider: DataProvider, + private val cache: Cache, + private val mediaCache: MediaCache, + private val cacheDirs: List, +) { + suspend fun clearDatabaseCache() = coroutineScope { + launch(Dispatchers.IO) { + dataProvider.cacheDatabase.clearAllTables() + } + } + + suspend fun clearImageCache() = coroutineScope { + mediaCache.clear() + cache.directory().deleteRecursively() + } + + suspend fun clearCacheDir() = coroutineScope { + launch(Dispatchers.IO) { + cacheDirs.forEach { + it.listFiles()?.forEach { file -> + file.deleteRecursively() + } + } + } + } + + suspend fun clearSearchHistory() = coroutineScope { + launch(Dispatchers.IO) { + dataProvider.appDatabase.searchDao().clear() + } + } +} From 0c5aff3f47c3c5a5d9a2fa5b8f7b76d5a82b9517 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 13:52:45 +0800 Subject: [PATCH 022/615] add koin workmanager --- common/build.gradle.kts | 1 + .../kotlin/com/twidere/twiderex/TwidereApplication.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b97781426..a34222058 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -47,6 +47,7 @@ kotlin { val androidMain by getting { dependencies { api("io.insert-koin:koin-android:${Versions.koin}") + api("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") } } val androidTest by getting diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt index bf154078e..fef66e17f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -24,14 +24,18 @@ import android.app.Application import com.twidere.twiderex.di.setupModules import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger +import org.koin.androidx.workmanager.koin.workManagerFactory +import org.koin.core.KoinExperimentalAPI import org.koin.core.context.startKoin abstract class TwidereApplication : Application() { + @OptIn(KoinExperimentalAPI::class) override fun onCreate() { super.onCreate() startKoin { androidLogger() androidContext(this@TwidereApplication) + workManagerFactory() setupModules() } } From aa05fd097c0e6d36841ed5ef98b63c74b8b0e84e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 14:25:13 +0800 Subject: [PATCH 023/615] add koin compose ext --- .../com/twidere/twiderex/di/ext/ComposeExt.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt new file mode 100644 index 000000000..4a6517630 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.ext + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import org.koin.core.Koin +import org.koin.core.context.GlobalContext +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.qualifier.Qualifier + +@Composable +inline fun get( + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null, +): T = remember(qualifier, parameters) { + GlobalContext.get().get(qualifier, parameters) +} + +@Composable +fun getKoin(): Koin = remember { + GlobalContext.get() +} From 9450b5cb44660ac30f4c58630cb78b2163c7d685 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 6 Aug 2021 14:59:17 +0800 Subject: [PATCH 024/615] migrate ui model/enum model/some common models to commonMain --- .../component/lazy/ui/LazyUiDMEventList.kt | 7 +- .../transform/DmConversationTransform.kt | 12 +++- .../twiderex/repository/CacheRepository.kt | 65 ------------------- .../com/twidere/twiderex/model/AmUser.kt | 0 .../twidere/twiderex/model/MicroBlogKey.kt | 0 .../twidere/twiderex/model/TwidereAccount.kt | 0 .../twidere/twiderex/model/enums/ListType.kt | 0 .../twidere/twiderex/model/enums/Mastodon.kt | 0 .../twidere/twiderex/model/enums/MediaType.kt | 0 .../twiderex/model/enums/PlatformType.kt | 0 .../twiderex/model/enums/ReferenceType.kt | 0 .../twidere/twiderex/model/enums/Twitter.kt | 0 .../com/twidere/twiderex/model/ui/UiCard.kt | 0 .../twiderex/model/ui/UiDMConversation.kt | 10 ++- .../twidere/twiderex/model/ui/UiDMEvent.kt | 9 ++- .../twiderex/model/ui/UiEmojiCategory.kt | 0 .../com/twidere/twiderex/model/ui/UiGeo.kt | 0 .../com/twidere/twiderex/model/ui/UiList.kt | 0 .../com/twidere/twiderex/model/ui/UiMedia.kt | 8 +-- .../com/twidere/twiderex/model/ui/UiPoll.kt | 0 .../com/twidere/twiderex/model/ui/UiStatus.kt | 4 +- .../com/twidere/twiderex/model/ui/UiTrend.kt | 0 .../twidere/twiderex/model/ui/UiUrlEntity.kt | 0 .../com/twidere/twiderex/model/ui/UiUser.kt | 4 +- .../model/ui/mastodon/MastodonStatusExtra.kt | 0 .../model/ui/mastodon/MastodonUserExtra.kt | 0 .../model/ui/twitter/TwitterStatusExtra.kt | 0 .../model/ui/twitter/TwitterUserExtra.kt | 0 28 files changed, 32 insertions(+), 87 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/AmUser.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/TwidereAccount.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/ListType.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/MediaType.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/enums/Twitter.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiCard.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiList.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt (84%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/UiUser.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt (100%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 5d5a833fa..06cbf985c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -77,7 +77,6 @@ import com.twidere.twiderex.component.status.StatusMediaDefaults import com.twidere.twiderex.component.status.StatusMediaPreviewItem import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserAvatarDefaults -import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.navigation.RootRoute @@ -139,15 +138,15 @@ private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiD Column(horizontalAlignment = Alignment.End) { Row(verticalAlignment = Alignment.CenterVertically) { when (event.sendStatus) { - DbDMEvent.SendStatus.PENDING -> { + UiDMEvent.SendStatus.PENDING -> { CircularProgressIndicator( modifier = Modifier.size(DMOutComeEventDefaults.Loading.size), strokeWidth = DMOutComeEventDefaults.Loading.width, color = MaterialTheme.colors.primary ) } - DbDMEvent.SendStatus.SUCCESS -> {} - DbDMEvent.SendStatus.FAILED -> { + UiDMEvent.SendStatus.SUCCESS -> {} + UiDMEvent.SendStatus.FAILED -> { Box( modifier = Modifier .clip(shape = CircleShape) diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt index b851f1cc1..65b3844c8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.model.transform import com.twidere.twiderex.db.model.DbDMConversation +import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage import com.twidere.twiderex.model.ui.UiDMConversation @@ -34,7 +35,10 @@ fun DbDMConversation.toUi() = UiDMConversation( conversationAvatar = conversationAvatar, conversationName = conversationName, conversationSubName = conversationSubName, - conversationType = conversationType, + conversationType = when (conversationType) { + DbDMConversation.Type.ONE_TO_ONE -> UiDMConversation.Type.ONE_TO_ONE + DbDMConversation.Type.GROUP -> UiDMConversation.Type.GROUP + }, recipientKey = recipientKey, ) @@ -55,7 +59,11 @@ fun DbDMEventWithAttachments.toUi() = UiDMEvent( messageType = message.messageType, senderAccountKey = message.senderAccountKey, recipientAccountKey = message.recipientAccountKey, - sendStatus = message.sendStatus, + sendStatus = when (message.sendStatus) { + DbDMEvent.SendStatus.PENDING -> UiDMEvent.SendStatus.PENDING + DbDMEvent.SendStatus.SUCCESS -> UiDMEvent.SendStatus.SUCCESS + DbDMEvent.SendStatus.FAILED -> UiDMEvent.SendStatus.FAILED + }, media = media.toUi(), urlEntity = urlEntity.toUi(), sender = sender.toUi() diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt deleted file mode 100644 index a49be0495..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository - -import coil.ImageLoader -import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.CacheDatabase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import okhttp3.Cache -import java.io.File - -class CacheRepository( - private val database: CacheDatabase, - private val cache: Cache, - private val imageLoader: ImageLoader, - private val appDatabase: AppDatabase, - private val cacheDirs: List, -) { - suspend fun clearDatabaseCache() = coroutineScope { - launch(Dispatchers.IO) { - database.clearAllTables() - } - } - - suspend fun clearImageCache() = coroutineScope { - imageLoader.memoryCache.clear() - cache.directory.deleteRecursively() - } - - suspend fun clearCacheDir() = coroutineScope { - launch(Dispatchers.IO) { - cacheDirs.forEach { - it.listFiles()?.forEach { file -> - file.deleteRecursively() - } - } - } - } - - suspend fun clearSearchHistory() = coroutineScope { - launch(Dispatchers.IO) { - appDatabase.searchDao().clear() - } - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/AmUser.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/TwidereAccount.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/TwidereAccount.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/ListType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/ListType.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/MediaType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/MediaType.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/enums/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/enums/Twitter.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiCard.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiCard.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt index 2fe028df2..14574680a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.model.ui -import com.twidere.twiderex.db.model.DbDMConversation import com.twidere.twiderex.model.MicroBlogKey data class UiDMConversation( @@ -31,9 +30,14 @@ data class UiDMConversation( val conversationAvatar: String, val conversationName: String, val conversationSubName: String, - val conversationType: DbDMConversation.Type, + val conversationType: Type, val recipientKey: MicroBlogKey, -) +) { + enum class Type { + ONE_TO_ONE, + GROUP + } +} data class UiDMConversationWithLatestMessage( val conversation: UiDMConversation, diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt index 4972e7372..29536a905 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.model.ui -import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.model.MicroBlogKey data class UiDMEvent( @@ -37,11 +36,17 @@ data class UiDMEvent( val messageType: String, val senderAccountKey: MicroBlogKey, val recipientAccountKey: MicroBlogKey, - val sendStatus: DbDMEvent.SendStatus, + val sendStatus: SendStatus, val media: List, val urlEntity: List, val sender: UiUser ) { val isInCome: Boolean get() = recipientAccountKey == accountKey + + enum class SendStatus { + PENDING, + SUCCESS, + FAILED + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt similarity index 84% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 0c959c218..1d1e34252 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -23,8 +23,6 @@ package com.twidere.twiderex.model.ui import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable -import androidx.compose.ui.res.painterResource -import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.MediaType @Immutable @@ -64,10 +62,10 @@ data class UiMedia( UiMedia( url = null, mediaUrl = null, - previewUrl = painterResource(id = R.drawable.featured_graphics), + previewUrl = "", // painterResource(id = R.drawable.featured_graphics), type = MediaType.photo, - width = painterResource(id = R.drawable.featured_graphics).intrinsicSize.width.toLong(), - height = painterResource(id = R.drawable.featured_graphics).intrinsicSize.height.toLong(), + width = 0, // painterResource(id = R.drawable.featured_graphics).intrinsicSize.width.toLong(), + height = 0, // painterResource(id = R.drawable.featured_graphics).intrinsicSize.height.toLong(), pageUrl = null, altText = "", ), diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt index 5d73ffcc3..d3d2b233b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.model.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.PlatformType @@ -98,7 +96,7 @@ data class UiStatus( @Composable fun sample() = UiStatus( statusId = "", - htmlText = stringResource(id = R.string.scene_settings_display_preview_thank_for_using_twidere_x), + htmlText = "", // stringResource(id = R.string.scene_settings_display_preview_thank_for_using_twidere_x), timestamp = System.currentTimeMillis(), metrics = StatusMetrics( retweet = 1200, diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt index e96cae9bd..4ad5fbb3f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/ui/UiUser.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.model.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable -import androidx.compose.ui.res.painterResource -import com.twidere.twiderex.R import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra @@ -68,7 +66,7 @@ data class UiUser( id = "", name = "Twidere", screenName = "TwidereProject", - profileImage = painterResource(id = R.drawable.ic_profile_image_twidere), + profileImage = "", // painterResource(id = R.drawable.ic_profile_image_twidere), profileBackgroundImage = null, metrics = UserMetrics( fans = 0, diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt From 796f5469086dfe44e5705f1f721a9836246d5013 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 6 Aug 2021 15:11:05 +0800 Subject: [PATCH 025/615] migrate MediaRepository to commonMain --- .../com/twidere/twiderex/cache/MediaCache.kt | 27 +++++++++++++++++++ .../twidere/twiderex/db/AndroidAppDatabase.kt | 2 +- .../twiderex/db/AndroidCacheDatabase.kt | 2 +- .../com/twidere/twiderex/db/Database.kt | 2 +- .../com/twidere/twiderex/db/dao/MediaDao.kt | 7 ++++- .../twiderex/repository/CacheRepository.kt | 2 +- .../twiderex/repository/MediaRepository.kt | 9 +++---- .../com/twidere/twiderex/cache/MediaCache.kt | 27 +++++++++++++++++++ .../twiderex/db/DesktopAppDatabaseImpl.kt | 2 +- .../twiderex/db/DesktopCacheDatabaseImpl.kt | 2 +- 10 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/MediaRepository.kt (80%) create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt new file mode 100644 index 000000000..1c785cfbd --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.cache + +actual class MediaCache { + actual fun clear() { + TODO("Not yet implemented") + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt index 92d80f16f..40922351e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt @@ -24,7 +24,7 @@ import com.twidere.twiderex.db.dao.DraftDao import com.twidere.twiderex.db.dao.SearchDao internal class AndroidAppDatabase : AppDatabase { - override fun clearAllTables() { + override suspend fun clearAllTables() { TODO("Not yet implemented") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt index 8c67d8631..81342c490 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt @@ -32,7 +32,7 @@ internal class AndroidCacheDatabase : AppDatabase { TODO("Not yet implemented") } - override fun clearAllTables() { + override suspend fun clearAllTables() { TODO("Not yet implemented") } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt index d65f3d220..09dddfbb2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt @@ -21,5 +21,5 @@ package com.twidere.twiderex.db interface Database { - fun clearAllTables() + suspend fun clearAllTables() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt index a681f2ce6..eee500b0a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt @@ -20,5 +20,10 @@ */ package com.twidere.twiderex.db.dao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiMedia + // TODO OPERATION -interface MediaDao +interface MediaDao { + suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt index c2b63aba4..3aa8af310 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -29,7 +29,7 @@ import okhttp3.Cache import java.io.File class CacheRepository( - private val dataProvider: DataProvider, + private val dataProvider: DataProvider = DataProvider.create(), private val cache: Cache, private val mediaCache: MediaCache, private val cacheDirs: List, diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt similarity index 80% rename from android/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt index 000574a11..c9e4e1f39 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/MediaRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt @@ -19,14 +19,11 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.repository - -import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.dataprovider.DataProvider import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi - -class MediaRepository(private val database: CacheDatabase) { +class MediaRepository(private val provider: DataProvider = DataProvider.create()) { suspend fun findMediaByBelongToKey( belongToKey: MicroBlogKey - ) = database.mediaDao().findMediaByBelongToKey(belongToKey).toUi() + ) = provider.cacheDatabase.mediaDao().findMediaByBelongToKey(belongToKey) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt new file mode 100644 index 000000000..1c785cfbd --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.cache + +actual class MediaCache { + actual fun clear() { + TODO("Not yet implemented") + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt index 5f2018cc2..c72c050dc 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt @@ -32,7 +32,7 @@ internal class DesktopAppDatabaseImpl : AppDatabase { TODO("Not yet implemented") } - override fun clearAllTables() { + override suspend fun clearAllTables() { TODO("Not yet implemented") } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt index 800aa797a..94e93da80 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db internal class DesktopCacheDatabaseImpl : Database { - override fun clearAllTables() { + override suspend fun clearAllTables() { TODO("Not yet implemented") } } From 41c7569cb520d36ee9d76d3fad43ac026c220b79 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 15:22:29 +0800 Subject: [PATCH 026/615] add koin test --- common/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a34222058..3f37a7a43 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -42,6 +42,8 @@ kotlin { val commonTest by getting { dependencies { implementation(kotlin("test")) + implementation("io.insert-koin:koin-test:${Versions.koin}") + implementation("io.insert-koin:koin-test-junit5:${Versions.koin}") } } val androidMain by getting { From f539539510ccf88bb2797e6a093a6786048d814c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 15:22:40 +0800 Subject: [PATCH 027/615] fix koin context --- .../kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt index 4a6517630..2c3cde996 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt @@ -23,19 +23,19 @@ package com.twidere.twiderex.di.ext import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import org.koin.core.Koin -import org.koin.core.context.GlobalContext import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier +import org.koin.mp.KoinPlatformTools @Composable inline fun get( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null, ): T = remember(qualifier, parameters) { - GlobalContext.get().get(qualifier, parameters) + KoinPlatformTools.defaultContext().get().get(qualifier, parameters) } @Composable fun getKoin(): Koin = remember { - GlobalContext.get() + KoinPlatformTools.defaultContext().get() } From ebd9cdad2e7a16738d9582ad3e1fa22a7bda7352 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 6 Aug 2021 15:27:13 +0800 Subject: [PATCH 028/615] migrate paging source/Constants to commonMain --- .../kotlin/com/twidere/twiderex/Consts.kt | 0 .../twiderex/dataprovider/DataTransform.kt | 27 +++++++++++++++++++ .../paging/source/FollowersPagingSource.kt | 5 ++-- .../paging/source/FollowingPagingSource.kt | 5 ++-- .../source/ListsSubscribersPagingSource.kt | 0 .../MastodonSearchHashtagPagingSource.kt | 0 .../paging/source/SearchUserPagingSource.kt | 5 ++-- .../paging/source/UserPagingSource.kt | 6 ++--- 8 files changed, 36 insertions(+), 12 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/Consts.kt (100%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt (93%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/Consts.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/Consts.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt new file mode 100644 index 000000000..12888c098 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.services.microblog.model.IUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser + +expect fun IUser.toUi(accountKey: MicroBlogKey): UiUser diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt index 14277c844..90a221137 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt @@ -24,9 +24,8 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser class FollowersPagingSource( @@ -38,7 +37,7 @@ class FollowersPagingSource( val page = params.key val result = service.followers(userKey.id, nextPage = page) val users = result.map { - it.toDbUser(userKey).toUi() + it.toUi(userKey) } val nextPage = if (result is IPaging) { result.nextPage diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt index c6b31390b..913e2d9af 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt @@ -24,9 +24,8 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser class FollowingPagingSource( @@ -38,7 +37,7 @@ class FollowingPagingSource( val page = params.key val result = service.following(userKey.id, nextPage = page) val users = result.map { - it.toDbUser(userKey).toUi() + it.toUi(userKey) } val nextPage = if (result is IPaging) { result.nextPage diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt index 58b71b1ad..4208a1859 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt @@ -23,10 +23,9 @@ package com.twidere.twiderex.paging.source import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser class SearchUserPagingSource( @@ -39,7 +38,7 @@ class SearchUserPagingSource( return try { val page = params.key ?: 0 val result = service.searchUsers(query, page = page, count = defaultLoadCount, following = following).map { - it.toDbUser(accountKey).toUi() + it.toUi(accountKey) } LoadResult.Page( data = result, diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt index db4a7a96b..d1781d589 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt @@ -24,14 +24,14 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.model.IPaging import com.twidere.services.microblog.model.IUser -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser abstract class UserPagingSource( protected val userKey: MicroBlogKey, ) : PagingSource() { + override fun getRefreshKey(state: PagingState): String? { return null } @@ -40,7 +40,7 @@ abstract class UserPagingSource( return try { val result = loadUsers(params) val users = result.map { - it.toDbUser(userKey).toUi() + it.toUi(userKey) } val nextPage = if (result is IPaging && users.isNotEmpty()) { result.nextPage From 57c877dfc6d95474f71c615affe4eb7eb369e494 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 15:57:25 +0800 Subject: [PATCH 029/615] fix koin dependencies --- common/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 3f37a7a43..efb8c1e41 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -43,7 +43,6 @@ kotlin { dependencies { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") - implementation("io.insert-koin:koin-test-junit5:${Versions.koin}") } } val androidMain by getting { From 5702b6ca39df4581fcd135f8238b5c6f7e25ed5c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 6 Aug 2021 17:01:27 +0800 Subject: [PATCH 030/615] migrate paging/notification repository to commonMain --- .../twiderex/dataprovider/DataTransform.kt | 10 ++++ .../com/twidere/twiderex/db/Database.kt | 1 + .../db/dao/DirectMessageConversationDao.kt | 8 ++- .../twiderex/db/dao/DirectMessageEventDao.kt | 11 +++- .../com/twidere/twiderex/db/dao/ListsDao.kt | 14 ++++- .../twiderex/db/dao/NotificationCursorDao.kt | 9 ++- .../twiderex/db/dao/PagingTimelineDao.kt | 17 +++++- .../com/twidere/twiderex/db/dao/StatusDao.kt | 9 ++- .../com/twidere/twiderex/db/dao/TrendDao.kt | 11 +++- .../model/enums/NotificationCursorType.kt | 27 +++++++++ .../model/paging/NotificationCursor.kt | 31 ++++++++++ .../twiderex/model/paging/PagingTimeLine.kt | 59 +++++++++++++++++++ .../twidere/twiderex/paging/IPagingList.kt | 0 .../paging/crud/MemoryCachePagingMediator.kt | 0 .../paging/crud/MemoryCachePagingSource.kt | 0 .../twiderex/paging/crud/PagingMemoryCache.kt | 0 .../mediator/dm/BaseDirectMessageMediator.kt | 0 .../mediator/dm/DMConversationMediator.kt | 18 ++---- .../paging/mediator/dm/DMEventMediator.kt | 18 ++---- .../paging/mediator/list/ListsMediator.kt | 30 +++------- .../mediator/list/ListsMembersMediator.kt | 0 .../mediator/list/ListsTimelineMediator.kt | 0 .../mediator/list/ListsUserPagingMediator.kt | 5 +- .../mediator/paging/CursorPagingMediator.kt | 4 +- .../CursorWithCustomOrderPagingMediator.kt | 18 +++--- .../mediator/paging/MaxIdPagingMediator.kt | 6 +- .../paging/mediator/paging/PagingMediator.kt | 8 +-- .../paging/PagingTimelineMediatorBase.kt | 23 ++++---- .../mediator/paging/PagingWithGapMediator.kt | 21 ++++--- .../mediator/search/SearchMediaMediator.kt | 0 .../mediator/search/SearchStatusMediator.kt | 4 +- .../status/MastodonStatusContextMediator.kt | 4 +- .../status/TwitterConversationMediator.kt | 0 .../mediator/timeline/HomeTimelineMediator.kt | 0 .../MastodonHashtagTimelineMediator.kt | 0 .../timeline/MentionTimelineMediator.kt | 12 ++-- .../timeline/NotificationTimelineMediator.kt | 12 ++-- .../mastodon/FederatedTimelineMediator.kt | 0 .../mastodon/LocalTimelineMediator.kt | 0 .../paging/mediator/trend/TrendMediator.kt | 31 +++------- .../mediator/user/UserFavouriteMediator.kt | 8 +-- .../paging/mediator/user/UserMediaMediator.kt | 22 +++---- .../mediator/user/UserStatusMediator.kt | 4 +- .../twiderex/repository/MediaRepository.kt | 6 +- .../repository/NotificationRepository.kt | 34 +++++------ 45 files changed, 320 insertions(+), 175 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/IPagingList.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt (73%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt (76%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt (73%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt (82%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt (87%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt (83%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt (70%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt (79%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt (77%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt index 12888c098..901a1077d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -20,8 +20,18 @@ */ package com.twidere.twiderex.dataprovider +import com.twidere.services.microblog.model.INotification +import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.IUser import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser expect fun IUser.toUi(accountKey: MicroBlogKey): UiUser + +expect fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus + +expect fun INotification.toUi(accountKey: MicroBlogKey): UiStatus + +expect fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt index 09dddfbb2..f9517ec59 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt @@ -22,4 +22,5 @@ package com.twidere.twiderex.db interface Database { suspend fun clearAllTables() + fun withTransaction(block: suspend () -> R): R } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt index 6d98d81d3..19d349944 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt @@ -20,4 +20,10 @@ */ package com.twidere.twiderex.db.dao -interface DirectMessageConversationDao +import androidx.paging.PagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage + +interface DirectMessageConversationDao { + fun getPagingSource(accountKey: MicroBlogKey): PagingSource +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt index 6a51ceef9..266075481 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt @@ -20,5 +20,14 @@ */ package com.twidere.twiderex.db.dao +import androidx.paging.PagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent + // TODO OPERATION -interface DirectMessageEventDao +interface DirectMessageEventDao { + fun getPagingSource( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): PagingSource +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt index f7d39f07b..03cf9ba64 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt @@ -20,5 +20,17 @@ */ package com.twidere.twiderex.db.dao +import androidx.paging.PagingSource +import com.twidere.services.microblog.model.IListModel +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList + // TODO OPERATION -interface ListsDao +interface ListsDao { + // Todo implement + // database.listsDao().clearAll(accountKey) + // database.listsDao().insertAll(lists) + fun saveLists(accountKey: MicroBlogKey, lists: List) + + fun getPagingSource(accountKey: MicroBlogKey): PagingSource +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt index 118c6e3f4..627a2b525 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt @@ -20,5 +20,12 @@ */ package com.twidere.twiderex.db.dao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor + // TODO OPERATION -interface NotificationCursorDao +interface NotificationCursorDao { + suspend fun find(accountKey: MicroBlogKey, type: NotificationCursorType): NotificationCursor? + suspend fun add(notificationCursor: NotificationCursor) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt index cce132e62..be0aa93b8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt @@ -20,4 +20,19 @@ */ package com.twidere.twiderex.db.dao -interface PagingTimelineDao +import androidx.paging.PagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus + +interface PagingTimelineDao { + fun getPagingSource( + pagingKey: String, + accountKey: MicroBlogKey + ): PagingSource + + suspend fun clearAll(pagingKey: String, accountKey: MicroBlogKey) + suspend fun getLatest(pagingKey: String, accountKey: MicroBlogKey): PagingTimeLineWithStatus? + suspend fun findWithStatusKey(maxStatusKey: MicroBlogKey, accountKey: MicroBlogKey): PagingTimeLine? + suspend fun insertAll(listOf: List) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 339e37194..60d9f903e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -19,5 +19,12 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiStatus + // TODO OPERATION -interface StatusDao +interface StatusDao { + suspend fun findWithStatusKey(it: MicroBlogKey): UiStatus + suspend fun insertAll(it: List) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt index e39705389..bf8a83844 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt @@ -20,5 +20,14 @@ */ package com.twidere.twiderex.db.dao +import androidx.paging.PagingSource +import com.twidere.services.microblog.model.ITrend +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend + // TODO OPERATION -interface TrendDao +interface TrendDao { + // Todo clear/add , just like listDao + fun saveTrend(accountKey: MicroBlogKey, trends: List) + fun getPagingSource(accountKey: MicroBlogKey): PagingSource +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt new file mode 100644 index 000000000..f18cc8a1c --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.enums + +enum class NotificationCursorType { + General, + Mentions, + Follower, +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt new file mode 100644 index 000000000..2771c77f9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.paging +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType + +data class NotificationCursor( + val _id: String, + val accountKey: MicroBlogKey, + val type: NotificationCursorType, + val value: String, + val timestamp: Long, +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt new file mode 100644 index 000000000..370b2f045 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt @@ -0,0 +1,59 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.paging + +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiStatus + +data class PagingTimeLine( + val accountKey: MicroBlogKey, + val pagingKey: String, + val statusKey: MicroBlogKey, + val timestamp: Long, + val sortId: Long, + var isGap: Boolean, + var status: UiStatus +) + +data class PagingTimeLineWithStatus( + val timeline: PagingTimeLine, + val status: UiStatus, +) + +enum class UserTimelineType { + Status, + Media, + Favourite +} + +fun UserTimelineType.pagingKey(accountKey: MicroBlogKey) = "user:$accountKey:$this" + +suspend fun List.saveToDb( + database: CacheDatabase, +) { + this.map { it.status }.let { + database.statusDao().insertAll(it) + } + this.map { it.timeline }.let { + database.pagingTimelineDao().insertAll(it) + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/IPagingList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt similarity index 73% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt index 8bea147a7..ada0c6a9d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt @@ -25,23 +25,19 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.PagingSource -import androidx.paging.map import com.twidere.services.microblog.model.IDirectMessage import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) class DMConversationMediator( database: CacheDatabase, accountKey: MicroBlogKey, realFetch: suspend (key: String?) -> List -) : BaseDirectMessageMediator(database, accountKey, realFetch) { +) : BaseDirectMessageMediator(database, accountKey, realFetch) { override fun reverse() = false fun pager( @@ -49,10 +45,10 @@ class DMConversationMediator( pageSize = defaultLoadCount, enablePlaceholders = false ), - pagingSourceFactory: () -> PagingSource = { + pagingSourceFactory: () -> PagingSource = { database.directMessageConversationDao().getPagingSource(accountKey = accountKey) } - ): Pager { + ): Pager { return Pager( config = config, remoteMediator = this, @@ -61,12 +57,8 @@ class DMConversationMediator( } companion object { - fun Pager.toUi(): Flow> { - return this.flow.map { pagingData -> - pagingData.map { - it.toUi() - } - } + fun Pager.toUi(): Flow> { + return this.flow } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt similarity index 76% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt index 53b01a4cf..6e8cb7180 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt @@ -25,16 +25,12 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.PagingSource -import androidx.paging.map import com.twidere.services.microblog.model.IDirectMessage import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiDMEvent import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) class DMEventMediator( @@ -42,7 +38,7 @@ class DMEventMediator( database: CacheDatabase, accountKey: MicroBlogKey, realFetch: suspend (key: String?) -> List -) : BaseDirectMessageMediator(database, accountKey, realFetch) { +) : BaseDirectMessageMediator(database, accountKey, realFetch) { override fun reverse() = true @@ -51,11 +47,11 @@ class DMEventMediator( pageSize = defaultLoadCount, enablePlaceholders = false ), - pagingSourceFactory: () -> PagingSource = { + pagingSourceFactory: () -> PagingSource = { database.directMessageDao() .getPagingSource(accountKey = accountKey, conversationKey = conversationKey) } - ): Pager { + ): Pager { return Pager( config = config, remoteMediator = this, @@ -64,12 +60,8 @@ class DMEventMediator( } companion object { - fun Pager.toUi(): Flow> { - return this.flow.map { pagingData -> - pagingData.map { - it.toUi() - } - } + fun Pager.toUi(): Flow> { + return this.flow } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt similarity index 73% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt index 9657e8c64..035382f71 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt @@ -28,30 +28,25 @@ import androidx.paging.PagingData import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator -import androidx.paging.map import com.twidere.services.microblog.ListsService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbList -import com.twidere.twiderex.db.model.DbList import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiList import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) class ListsMediator( private val database: CacheDatabase, private val service: ListsService, private val accountKey: MicroBlogKey -) : RemoteMediator() { +) : RemoteMediator() { - override suspend fun load(loadType: LoadType, state: PagingState): MediatorResult { + override suspend fun load(loadType: LoadType, state: PagingState): MediatorResult { return try { if (loadType == LoadType.REFRESH) { - val lists = service.lists().map { it.toDbList(accountKey) } - saveLists(lists) + val lists = service.lists() + database.listsDao().saveLists(accountKey = accountKey, lists = lists) } MediatorResult.Success(endOfPaginationReached = true) } catch (e: Throwable) { @@ -64,10 +59,10 @@ class ListsMediator( pageSize = defaultLoadCount, enablePlaceholders = false ), - pagingSourceFactory: () -> PagingSource = { + pagingSourceFactory: () -> PagingSource = { database.listsDao().getPagingSource(accountKey = accountKey) } - ): Pager { + ): Pager { return Pager( config = config, remoteMediator = this, @@ -75,18 +70,9 @@ class ListsMediator( ) } - private suspend fun saveLists(lists: List) { - database.listsDao().clearAll(accountKey) - database.listsDao().insertAll(lists) - } - companion object { - fun Pager.toUi(): Flow> { - return this.flow.map { pagingData -> - pagingData.map { - it.toUi() - } - } + fun Pager.toUi(): Flow> { + return this.flow } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt index 629353a6c..ce23e5f57 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt @@ -26,9 +26,8 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.model.IPaging import com.twidere.services.microblog.model.IUser -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.MemoryCachePagingMediator import com.twidere.twiderex.paging.crud.PagingMemoryCache @@ -46,7 +45,7 @@ abstract class ListsUserPagingMediator( return try { val result = loadUsers(key, state.config.pageSize) val users = result.map { - it.toDbUser(userKey).toUi() + it.toUi(userKey) } val nextKey = if (result is IPaging && users.isNotEmpty()) { result.nextPage diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt index 12ef35fbb..2d529b1d5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.paging.mediator.paging import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.ArrayListCompat import com.twidere.twiderex.paging.CursorPagination @@ -35,7 +35,7 @@ abstract class CursorPagingMediator( ) : PagingTimelineMediatorBase(accountKey, database) { override fun provideNextPage( raw: List, - result: List + result: List ): CursorPagination { return if (raw is CursorPagingResult<*>) { CursorPagination(cursor = raw.cursor) diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt similarity index 82% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt index c68c9fa24..65db08574 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt @@ -23,8 +23,8 @@ package com.twidere.twiderex.paging.mediator.paging import androidx.paging.PagingState import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.ArrayListCompat import com.twidere.twiderex.paging.IPagination @@ -48,7 +48,7 @@ abstract class CursorWithCustomOrderPagingMediator( ) { override fun provideNextPage( raw: List, - result: List + result: List ): CursorWithCustomOrderPagination { return if (raw is CursorWithCustomOrderPagingResult<*>) { CursorWithCustomOrderPagination( @@ -57,25 +57,25 @@ abstract class CursorWithCustomOrderPagingMediator( ) } else { CursorWithCustomOrderPagination( - cursor = result.lastOrNull()?.status?.status?.data?.statusId, + cursor = result.lastOrNull()?.status?.statusId, nextOrder = (result.lastOrNull()?.timeline?.sortId ?: 0) - result.size ) } } override fun transform( - state: PagingState, - data: List, + state: PagingState, + data: List, list: List - ): List { + ): List { val lastId = if (list is CursorWithCustomOrderPagingResult<*>) { list.nextOrder } else { state.lastItemOrNull()?.timeline?.sortId ?: 0 } - return data.mapIndexed { index, dbPagingTimelineWithStatus -> - dbPagingTimelineWithStatus.copy( - timeline = dbPagingTimelineWithStatus.timeline.copy( + return data.mapIndexed { index, pagingTimelineWithStatus -> + pagingTimelineWithStatus.copy( + timeline = pagingTimelineWithStatus.timeline.copy( sortId = lastId - index ) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt index b68345e5d..aaf5837fa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.paging.mediator.paging import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.SinceMaxPagination abstract class MaxIdPagingMediator( @@ -32,8 +32,8 @@ abstract class MaxIdPagingMediator( ) : PagingTimelineMediatorBase(accountKey, database) { override fun provideNextPage( raw: List, - result: List + result: List ): SinceMaxPagination { - return SinceMaxPagination(maxId = result.lastOrNull()?.status?.status?.data?.statusId) + return SinceMaxPagination(maxId = result.lastOrNull()?.status?.statusId) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt similarity index 87% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt index 42dc1ccbd..d5ea6244d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt @@ -26,15 +26,15 @@ import androidx.paging.PagingConfig import androidx.paging.PagingSource import androidx.paging.RemoteMediator import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus @OptIn(ExperimentalPagingApi::class) abstract class PagingMediator( val database: CacheDatabase, val accountKey: MicroBlogKey, -) : RemoteMediator() { +) : RemoteMediator() { abstract val pagingKey: String } @@ -44,10 +44,10 @@ fun PagingMediator.pager( pageSize = defaultLoadCount, enablePlaceholders = true, ), - pagingSourceFactory: () -> PagingSource = { + pagingSourceFactory: () -> PagingSource = { database.pagingTimelineDao().getPagingSource(pagingKey = pagingKey, accountKey = accountKey) } -): Pager { +): Pager { return Pager( config = config, remoteMediator = this, diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt similarity index 83% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index 16195b90e..c48940257 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -23,13 +23,12 @@ package com.twidere.twiderex.paging.mediator.paging import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingState -import androidx.room.withTransaction import com.twidere.services.microblog.model.IStatus +import com.twidere.twiderex.dataprovider.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbPagingTimeline -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.saveToDb import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.IPagingList @@ -42,7 +41,7 @@ abstract class PagingTimelineMediatorBase( override suspend fun load( loadType: LoadType, - state: PagingState + state: PagingState ): MediatorResult { try { val key = when (loadType) { @@ -61,9 +60,9 @@ abstract class PagingTimelineMediatorBase( val last = state.lastItemOrNull() val result = load(pageSize, key).let { list -> list.map { status -> - status.toDbPagingTimeline(accountKey, pagingKey) + status.toPagingTimeline(accountKey, pagingKey) }.filter { - last?.status?.status?.data?.statusKey != it.status.status.data.statusKey + last?.status?.statusKey != it.status.statusKey }.let { transform(state, it, list) }.also { @@ -92,19 +91,19 @@ abstract class PagingTimelineMediatorBase( protected abstract fun provideNextPage( raw: List, - result: List + result: List ): T protected open fun transform( - state: PagingState, - data: List, + state: PagingState, + data: List, list: List - ): List { + ): List { return data } protected open fun hasMore( - result: List, + result: List, pageSize: Int ) = result.isNotEmpty() diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index 9e2206ca6..a1ede3d2e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -23,13 +23,12 @@ package com.twidere.twiderex.paging.mediator.paging import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingState -import androidx.room.withTransaction import com.twidere.services.microblog.model.IStatus +import com.twidere.twiderex.dataprovider.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbPagingTimeline -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.saveToDb import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -51,7 +50,7 @@ abstract class PagingWithGapMediator( override suspend fun load( loadType: LoadType, - state: PagingState + state: PagingState ): MediatorResult { val maxStatusKey = when (loadType) { LoadType.APPEND -> { @@ -59,7 +58,7 @@ abstract class PagingWithGapMediator( ?: return MediatorResult.Success( endOfPaginationReached = true ) - lastItem.status.status.data.statusKey + lastItem.status.statusKey } LoadType.PREPEND -> { return MediatorResult.Success(endOfPaginationReached = true) @@ -78,7 +77,7 @@ abstract class PagingWithGapMediator( LoadType.REFRESH -> { withContext(Dispatchers.IO) { database.pagingTimelineDao() - .getLatest(pagingKey, accountKey)?.status?.status?.data?.statusKey + .getLatest(pagingKey, accountKey)?.status?.statusKey } } } @@ -103,7 +102,7 @@ abstract class PagingWithGapMediator( } val result = loadBetweenImpl(pageSize, max_id = max_id, since_id = null).let { list -> list.map { - it.toDbPagingTimeline(accountKey, pagingKey) + it.toPagingTimeline(accountKey, pagingKey) }.let { transform(it, list) } @@ -117,7 +116,7 @@ abstract class PagingWithGapMediator( } if (sinceStatusKey != null) { result.lastOrNull()?.let { - database.pagingTimelineDao().findWithStatusKey(it.timeline.statusKey, accountKey = accountKey) + database.pagingTimelineDao().findWithStatusKey(it.status.statusKey, accountKey = accountKey) }.let { result.lastOrNull()?.timeline?.isGap = it == null } @@ -137,9 +136,9 @@ abstract class PagingWithGapMediator( } protected open suspend fun transform( - data: List, + data: List, list: List - ): List { + ): List { return data } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt index f81ce5016..31b5716c4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt @@ -23,8 +23,8 @@ package com.twidere.twiderex.paging.mediator.search import com.twidere.services.microblog.SearchService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.CursorPagination import com.twidere.twiderex.paging.mediator.paging.CursorPagingMediator import com.twidere.twiderex.paging.mediator.paging.CursorPagingResult @@ -41,7 +41,7 @@ class SearchStatusMediator( return CursorPagingResult(result.status, result.nextPage) } - override fun hasMore(result: List, pageSize: Int): Boolean { + override fun hasMore(result: List, pageSize: Int): Boolean { return result.size == pageSize } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt index 4b03ddcaf..76ca079e8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt @@ -23,8 +23,8 @@ package com.twidere.twiderex.paging.mediator.status import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagination import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagingMediator import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagingResult @@ -50,7 +50,7 @@ class MastodonStatusContextMediator( override val pagingKey: String = "status:$statusKey" - override fun hasMore(result: List, pageSize: Int): Boolean { + override fun hasMore(result: List, pageSize: Int): Boolean { return false } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt index a3542733d..184d4052f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt @@ -23,9 +23,9 @@ package com.twidere.twiderex.paging.mediator.timeline import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.NotificationCursorType import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.repository.NotificationRepository @@ -42,15 +42,15 @@ class MentionTimelineMediator( ) = service.mentionsTimeline(pageSize, max_id = max_id, since_id = since_id) override suspend fun transform( - data: List, + data: List, list: List - ): List { + ): List { if (data.any()) { notificationRepository.addCursorIfNeeded( accountKey, NotificationCursorType.Mentions, - data.first().status.status.data.statusId, - data.first().status.status.data.timestamp, + data.first().status.statusId, + data.first().status.timestamp, ) } return super.transform(data, list) diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt index b855d7f13..ab4c6273b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt @@ -23,9 +23,9 @@ package com.twidere.twiderex.paging.mediator.timeline import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.NotificationCursorType import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.repository.NotificationRepository @@ -42,15 +42,15 @@ class NotificationTimelineMediator( ) = service.notificationTimeline(count = pageSize, max_id = max_id, since_id = since_id) override suspend fun transform( - data: List, + data: List, list: List - ): List { + ): List { if (data.any()) { notificationRepository.addCursorIfNeeded( accountKey, NotificationCursorType.General, - data.first().status.status.data.statusId, - data.first().status.status.data.timestamp, + data.first().status.statusId, + data.first().status.timestamp, ) } return super.transform(data, list) diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt similarity index 70% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt index 70e26db02..99fa9a586 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt @@ -28,18 +28,12 @@ import androidx.paging.PagingData import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator -import androidx.paging.map import com.twidere.services.microblog.TrendService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbTrend -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiTrend import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) class TrendMediator( @@ -47,16 +41,16 @@ class TrendMediator( private val service: TrendService, private val accountKey: MicroBlogKey, private val locationId: String -) : RemoteMediator() { +) : RemoteMediator() { override suspend fun load( loadType: LoadType, - state: PagingState + state: PagingState ): MediatorResult { return try { if (loadType == LoadType.REFRESH) { - val lists = service.trends(locationId).map { it.toDbTrend(accountKey) } - saveLists(lists) + val lists = service.trends(locationId) + database.trendDao().saveTrend(accountKey = accountKey, trends = lists) } MediatorResult.Success(endOfPaginationReached = true) } catch (e: Throwable) { @@ -69,10 +63,10 @@ class TrendMediator( pageSize = defaultLoadCount, enablePlaceholders = false ), - pagingSourceFactory: () -> PagingSource = { + pagingSourceFactory: () -> PagingSource = { database.trendDao().getPagingSource(accountKey = accountKey) } - ): Pager { + ): Pager { return Pager( config = config, remoteMediator = this, @@ -80,18 +74,9 @@ class TrendMediator( ) } - private suspend fun saveLists(lists: List) { - database.trendDao().clearAll(accountKey) - lists.saveToDb(database) - } - companion object { - fun Pager.toUi(): Flow> { - return this.flow.map { pagingData -> - pagingData.map { - it.toUi() - } - } + fun Pager.toUi(): Flow> { + return this.flow } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt index 0cc277532..f07ede8d1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt @@ -25,11 +25,11 @@ import com.twidere.services.mastodon.model.MastodonPaging import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.UserTimelineType -import com.twidere.twiderex.db.model.pagingKey import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.UserTimelineType +import com.twidere.twiderex.model.paging.pagingKey import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagination import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagingMediator import com.twidere.twiderex.paging.mediator.paging.CursorWithCustomOrderPagingResult @@ -65,7 +65,7 @@ class UserFavouriteMediator( } } - override fun hasMore(result: List, pageSize: Int): Boolean { + override fun hasMore(result: List, pageSize: Int): Boolean { return if (platformType == PlatformType.Mastodon) { result.size == pageSize } else { diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt similarity index 79% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt index b40a6b194..c95b9bc8e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt @@ -25,11 +25,11 @@ import androidx.paging.PagingState import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.UserTimelineType -import com.twidere.twiderex.db.model.pagingKey import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ReferenceType +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.UserTimelineType +import com.twidere.twiderex.model.paging.pagingKey import com.twidere.twiderex.paging.PagingList import com.twidere.twiderex.paging.SinceMaxPagination import com.twidere.twiderex.paging.mediator.paging.MaxIdPagingMediator @@ -55,7 +55,7 @@ class UserMediaMediator( override fun provideNextPage( raw: List, - result: List + result: List ): SinceMaxPagination { if (result is PagingList<*, *>) { return result.nextPage as SinceMaxPagination @@ -64,17 +64,17 @@ class UserMediaMediator( } override fun transform( - state: PagingState, - data: List, + state: PagingState, + data: List, list: List, - ): List { + ): List { return PagingList( data.filter { - val content = it.status.status - !it.status.references.any { it.reference.referenceType == ReferenceType.Retweet } && - content.data.hasMedia && content.user.userKey == userKey + val content = it.status + !it.status.referenceStatus.any { reference -> reference.key == ReferenceType.Retweet } && + content.hasMedia && content.user.userKey == userKey }, - SinceMaxPagination(maxId = data.lastOrNull()?.status?.status?.data?.statusId) + SinceMaxPagination(maxId = data.lastOrNull()?.status?.statusId) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt index 563b74fee..f4c19448a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt @@ -24,9 +24,9 @@ import androidx.paging.ExperimentalPagingApi import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.UserTimelineType -import com.twidere.twiderex.db.model.pagingKey import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.UserTimelineType +import com.twidere.twiderex.model.paging.pagingKey import com.twidere.twiderex.paging.SinceMaxPagination import com.twidere.twiderex.paging.mediator.paging.MaxIdPagingMediator diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt index c9e4e1f39..637a01740 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt @@ -19,11 +19,11 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.repository -import com.twidere.twiderex.dataprovider.DataProvider +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey -class MediaRepository(private val provider: DataProvider = DataProvider.create()) { +class MediaRepository(private val cacheDatabase: CacheDatabase) { suspend fun findMediaByBelongToKey( belongToKey: MicroBlogKey - ) = provider.cacheDatabase.mediaDao().findMediaByBelongToKey(belongToKey) + ) = cacheDatabase.mediaDao().findMediaByBelongToKey(belongToKey) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt similarity index 77% rename from android/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt index ab2708500..b0eb6cb4d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt @@ -20,15 +20,14 @@ */ package com.twidere.twiderex.repository +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbStatusWithReference -import com.twidere.twiderex.db.model.DbNotificationCursor -import com.twidere.twiderex.db.model.NotificationCursorType -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor import com.twidere.twiderex.model.ui.UiStatus import java.util.UUID @@ -36,21 +35,22 @@ class NotificationRepository( private val database: CacheDatabase, ) { suspend fun activities( - account: AccountDetails, + accountKey: MicroBlogKey, + service: MicroBlogService ): List { - return when (val service = account.service) { + return when (service) { is NotificationService -> { val notifications = service.notificationTimeline() - .map { it.toDbStatusWithReference(account.accountKey) } - .map { it.toUi(account.accountKey) } - takeActivities(account, NotificationCursorType.General, notifications) + .map { + it.toUi(accountKey) + } + takeActivities(accountKey, NotificationCursorType.General, notifications) } else -> { if (service is TimelineService) { val mentions = service.mentionsTimeline() - .map { it.toDbStatusWithReference(account.accountKey) } - .map { it.toUi(account.accountKey) } - takeActivities(account, NotificationCursorType.Mentions, mentions) + .map { it.toUi(accountKey) } + takeActivities(accountKey, NotificationCursorType.Mentions, mentions) } else { emptyList() } @@ -59,14 +59,14 @@ class NotificationRepository( } private suspend fun takeActivities( - account: AccountDetails, + accountKey: MicroBlogKey, type: NotificationCursorType, notifications: List ): List { - val currentCursor = findCursor(account.accountKey, type) + val currentCursor = findCursor(accountKey, type) if (notifications.any() && (currentCursor == null || currentCursor.timestamp < notifications.first().timestamp)) { addCursor( - accountKey = account.accountKey, + accountKey = accountKey, type = type, value = notifications.first().statusId, timestamp = notifications.first().timestamp, @@ -95,7 +95,7 @@ class NotificationRepository( ) { database.notificationCursorDao() .add( - DbNotificationCursor( + NotificationCursor( _id = UUID.randomUUID().toString(), accountKey = accountKey, type = type, From 078d5e0c5a9c95874f157d68a13fb76768f39e91 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 6 Aug 2021 18:03:48 +0800 Subject: [PATCH 031/615] add preferences module koin setup --- .../twidere/twiderex/di/PreferencesModule.kt | 33 ++++++++ .../twidere/twiderex/di/PreferencesModule.kt | 78 +++++++++++++++++++ .../kotlin/com/twidere/twiderex/di/Setup.kt | 1 + .../preferences/ProvidePreferences.kt | 27 ++----- .../twidere/twiderex/di/PreferencesModule.kt | 30 +++++++ 5 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt new file mode 100644 index 000000000..3fe8033c1 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import android.content.Context +import com.twidere.twiderex.di.ext.get +import java.io.File + +internal actual fun createDataStoreFile(name: String): File { + val context = get() + return File( + context.applicationContext.filesDir, + "datastore/$name" + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt new file mode 100644 index 000000000..5ab9649bd --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt @@ -0,0 +1,78 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory +import androidx.datastore.core.Serializer +import com.twidere.twiderex.preferences.AppearancePreferences +import com.twidere.twiderex.preferences.DisplayPreferences +import com.twidere.twiderex.preferences.MiscPreferences +import com.twidere.twiderex.preferences.NotificationPreferences +import com.twidere.twiderex.preferences.serializer.AppearancePreferencesSerializer +import com.twidere.twiderex.preferences.serializer.DisplayPreferencesSerializer +import com.twidere.twiderex.preferences.serializer.MiscPreferencesSerializer +import com.twidere.twiderex.preferences.serializer.NotificationPreferencesSerializer +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.firstOrNull +import org.koin.dsl.module +import java.io.File + +internal fun preferencesModule() = module { + single { + PreferencesHolder( + appearancePreferences = createDataStore("appearances.pb", AppearancePreferencesSerializer), + displayPreferences = createDataStore("display.pb", DisplayPreferencesSerializer), + miscPreferences = createDataStore("misc.pb", MiscPreferencesSerializer), + notificationPreferences = createDataStore("notification.pb", NotificationPreferencesSerializer), + ) + } +} + +internal data class PreferencesHolder( + val appearancePreferences: DataStore, + val displayPreferences: DataStore, + val miscPreferences: DataStore, + val notificationPreferences: DataStore, +) { + suspend fun warmup() = coroutineScope { + awaitAll( + async { appearancePreferences.data.firstOrNull() }, + async { displayPreferences.data.firstOrNull() }, + async { miscPreferences.data.firstOrNull() }, + async { notificationPreferences.data.firstOrNull() }, + ) + } +} + +internal inline fun createDataStore( + name: String, + serializer: Serializer, +) = DataStoreFactory.create( + serializer, + produceFile = { + createDataStoreFile(name) + }, +) + +internal expect fun createDataStoreFile(name: String): File diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index a94e01659..0248014d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -21,4 +21,5 @@ package com.twidere.twiderex.di fun setupModules() { + preferencesModule() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt index 395883b29..0bc44cf71 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt @@ -26,36 +26,19 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.datastore.core.DataStore import com.twidere.services.http.config.HttpConfig import com.twidere.services.proxy.ProxyConfig -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.firstOrNull +import com.twidere.twiderex.di.PreferencesHolder import kotlinx.coroutines.flow.map -val LocalAppearancePreferences = +internal val LocalAppearancePreferences = compositionLocalOf { error("No AppearancePreferences") } -val LocalDisplayPreferences = +internal val LocalDisplayPreferences = compositionLocalOf { error("No DisplayPreferences") } -val LocalHttpConfig = compositionLocalOf { error("No Http config preferences") } -data class PreferencesHolder( - val appearancePreferences: DataStore, - val displayPreferences: DataStore, - val miscPreferences: DataStore -) { - suspend fun warmup() = coroutineScope { - awaitAll( - async { appearancePreferences.data.firstOrNull() }, - async { displayPreferences.data.firstOrNull() }, - async { miscPreferences.data.firstOrNull() }, - ) - } -} +internal val LocalHttpConfig = compositionLocalOf { error("No Http config preferences") } @Composable -fun ProvidePreferences( +internal fun ProvidePreferences( holder: PreferencesHolder, content: @Composable () -> Unit, ) { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt new file mode 100644 index 000000000..c8e1751d3 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import java.io.File + +internal actual fun createDataStoreFile(name: String): File { + return File( + File(System.getProperty("user.home")), + "datastore/$name" + ) +} From 434c3ebf4ebd2c37a4695803824b86ca07590c9f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 12:50:35 +0800 Subject: [PATCH 032/615] add references to common --- android/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 3be76d667..310e20164 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -182,6 +182,7 @@ dependencies { kotlinSerialization() kotlinCoroutines() implementation(projects.services) + implementation(projects.common) ksp(projects.assistedProcessor) implementation(projects.routeProcessor) ksp(projects.routeProcessor) From 9459a900d86fde642430f9c534cb4d9d04020f11 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 14:21:47 +0800 Subject: [PATCH 033/615] apply jetbrains.compose plugins in android --- android/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 310e20164..21ffc2b02 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -28,6 +28,7 @@ plugins { kotlin("android") kotlin("kapt") id("com.google.protobuf").version("0.8.17") + id("org.jetbrains.compose").version(Versions.compose_jb) kotlin("plugin.serialization").version(Versions.Kotlin.lang) id("com.google.devtools.ksp").version(Versions.ksp) } From b50fe0f2c150da7a3baea03b7a7126c5ac5f09b2 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 14:52:56 +0800 Subject: [PATCH 034/615] fix build --- .../{PreferencesModule.kt => PreferencesModule.android.kt} | 4 ++-- .../di/{PreferencesModule.kt => preferencesModule.kt} | 5 +++-- .../serializer/AppearancePreferencesSerializer.kt | 2 +- .../preferences/serializer/DisplayPreferencesSerializer.kt | 2 +- .../preferences/serializer/MiscPreferencesSerializer.kt | 4 +++- .../serializer/NotificationPreferencesSerializer.kt | 2 +- .../{PreferencesModule.kt => PreferencesModule.desktop.kt} | 3 ++- 7 files changed, 13 insertions(+), 9 deletions(-) rename common/src/androidMain/kotlin/com/twidere/twiderex/di/{PreferencesModule.kt => PreferencesModule.android.kt} (90%) rename common/src/commonMain/kotlin/com/twidere/twiderex/di/{PreferencesModule.kt => preferencesModule.kt} (94%) rename common/src/desktopMain/kotlin/com/twidere/twiderex/di/{PreferencesModule.kt => PreferencesModule.desktop.kt} (90%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt similarity index 90% rename from common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt index 3fe8033c1..4ea159d0a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt @@ -21,10 +21,10 @@ package com.twidere.twiderex.di import android.content.Context -import com.twidere.twiderex.di.ext.get +import org.koin.core.scope.Scope import java.io.File -internal actual fun createDataStoreFile(name: String): File { +internal actual fun Scope.createDataStoreFile(name: String): File { val context = get() return File( context.applicationContext.filesDir, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt similarity index 94% rename from common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt index 5ab9649bd..cca500c59 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.firstOrNull +import org.koin.core.scope.Scope import org.koin.dsl.module import java.io.File @@ -65,7 +66,7 @@ internal data class PreferencesHolder( } } -internal inline fun createDataStore( +internal inline fun Scope.createDataStore( name: String, serializer: Serializer, ) = DataStoreFactory.create( @@ -75,4 +76,4 @@ internal inline fun createDataStore( }, ) -internal expect fun createDataStoreFile(name: String): File +internal expect fun Scope.createDataStoreFile(name: String): File diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt index 2ceb27cad..00c1f0641 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt @@ -32,7 +32,7 @@ import java.io.OutputStream @OptIn(ExperimentalSerializationApi::class) object AppearancePreferencesSerializer : Serializer { override suspend fun readFrom(input: InputStream): AppearancePreferences { - return ProtoBuf.decodeFromByteArray(input.readAllBytes()) + return ProtoBuf.decodeFromByteArray(input.readBytes()) } override suspend fun writeTo( t: AppearancePreferences, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt index 997acde65..9ff845249 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt @@ -35,7 +35,7 @@ object DisplayPreferencesSerializer : Serializer { get() = DisplayPreferences() override suspend fun readFrom(input: InputStream): DisplayPreferences { - return ProtoBuf.decodeFromByteArray(input.readAllBytes()) + return ProtoBuf.decodeFromByteArray(input.readBytes()) } override suspend fun writeTo(t: DisplayPreferences, output: OutputStream) = diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt index 9e9bf3657..5dd4606e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt @@ -22,18 +22,20 @@ package com.twidere.twiderex.preferences.serializer import androidx.datastore.core.Serializer import com.twidere.twiderex.preferences.MiscPreferences +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.protobuf.ProtoBuf import java.io.InputStream import java.io.OutputStream +@OptIn(ExperimentalSerializationApi::class) object MiscPreferencesSerializer : Serializer { override val defaultValue: MiscPreferences get() = MiscPreferences() override suspend fun readFrom(input: InputStream): MiscPreferences { - return ProtoBuf.decodeFromByteArray(input.readAllBytes()) + return ProtoBuf.decodeFromByteArray(input.readBytes()) } override suspend fun writeTo(t: MiscPreferences, output: OutputStream) = diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt index 4927c3b50..7bc321e4e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt @@ -35,7 +35,7 @@ object NotificationPreferencesSerializer : Serializer { get() = NotificationPreferences() override suspend fun readFrom(input: InputStream): NotificationPreferences { - return ProtoBuf.decodeFromByteArray(input.readAllBytes()) + return ProtoBuf.decodeFromByteArray(input.readBytes()) } override suspend fun writeTo(t: NotificationPreferences, output: OutputStream) = diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt similarity index 90% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt index c8e1751d3..42aad38c9 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt @@ -20,9 +20,10 @@ */ package com.twidere.twiderex.di +import org.koin.core.scope.Scope import java.io.File -internal actual fun createDataStoreFile(name: String): File { +internal actual fun Scope.createDataStoreFile(name: String): File { return File( File(System.getProperty("user.home")), "datastore/$name" From 4fcc536590b2eb617f71c7ecbd4f441489cf7875 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 14:57:14 +0800 Subject: [PATCH 035/615] Make TwidereApp extends from TwidereApplication --- android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt index 9fefbffda..86f6abfd4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex -import android.app.Application import android.content.Context import androidx.hilt.work.HiltWorkerFactory import androidx.startup.AppInitializer @@ -32,7 +31,7 @@ import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @HiltAndroidApp -class TwidereApp : Application(), Configuration.Provider { +class TwidereApp : TwidereApplication(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory From 380b626afd10eefcd6a378518a3aca9ea54253d8 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 16:23:41 +0800 Subject: [PATCH 036/615] migrate preferences to common --- android/build.gradle.kts | 20 -------- .../com/twidere/twiderex/TwidereXActivity.kt | 4 +- .../component/foundation/VideoPlayer.kt | 3 +- .../component/lazy/ui/LazyUiDMEventList.kt | 2 +- .../lazy/ui/LazyUiStatusImageList.kt | 2 +- .../twiderex/component/status/UserAvatar.kt | 5 +- .../com/twidere/twiderex/di/TwidereModule.kt | 2 +- .../twiderex/extensions/ComposeExtensions.kt | 2 +- .../http/TwidereHttpConfigProvider.kt | 2 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 4 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 2 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 2 +- .../search/tabs/TwitterSearchMediaItem.kt | 2 +- .../scenes/settings/AppearanceScene.kt | 4 +- .../twiderex/scenes/settings/DisplayScene.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 2 +- .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 2 +- .../kotlin/com/twidere/twiderex/ui/Theme.kt | 2 +- .../viewmodel/settings/AppearanceViewModel.kt | 16 +++---- .../viewmodel/settings/DisplayViewModel.kt | 14 +++--- .../viewmodel/settings/MiscViewModel.kt | 31 ++++-------- .../settings/NotificationViewModel.kt | 6 +-- .../twiderex/worker/NotificationWorker.kt | 2 +- .../main/proto/AppearancePreferences.proto | 25 ---------- .../src/main/proto/DisplayPreferences.proto | 24 ---------- android/src/main/proto/MiscPreferences.proto | 19 -------- .../main/proto/NotificationPreferences.proto | 8 ---- ...ferencesModule.kt => PreferencesModule.kt} | 38 ++++----------- .../kotlin/com/twidere/twiderex/di/Setup.kt | 6 ++- .../twiderex/preferences/PreferencesHolder.kt | 47 +++++++++++++++++++ .../preferences/ProvidePreferences.kt | 12 +++-- .../{ => model}/AppearancePreferences.kt | 4 +- .../{ => model}/DisplayPreferences.kt | 2 +- .../{ => model}/MiscPreferences.kt | 2 +- .../{ => model}/NotificationPreferences.kt | 2 +- .../AppearancePreferencesSerializer.kt | 2 +- .../DisplayPreferencesSerializer.kt | 2 +- .../serializer/MiscPreferencesSerializer.kt | 2 +- .../NotificationPreferencesSerializer.kt | 2 +- 39 files changed, 124 insertions(+), 206 deletions(-) delete mode 100644 android/src/main/proto/AppearancePreferences.proto delete mode 100644 android/src/main/proto/DisplayPreferences.proto delete mode 100644 android/src/main/proto/MiscPreferences.proto delete mode 100644 android/src/main/proto/NotificationPreferences.proto rename common/src/commonMain/kotlin/com/twidere/twiderex/di/{preferencesModule.kt => PreferencesModule.kt} (58%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt rename common/src/commonMain/kotlin/com/twidere/twiderex/preferences/{ => model}/AppearancePreferences.kt (92%) rename common/src/commonMain/kotlin/com/twidere/twiderex/preferences/{ => model}/DisplayPreferences.kt (96%) rename common/src/commonMain/kotlin/com/twidere/twiderex/preferences/{ => model}/MiscPreferences.kt (96%) rename common/src/commonMain/kotlin/com/twidere/twiderex/preferences/{ => model}/NotificationPreferences.kt (95%) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 21ffc2b02..872265206 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,7 +1,3 @@ -import com.google.protobuf.gradle.builtins -import com.google.protobuf.gradle.generateProtoTasks -import com.google.protobuf.gradle.protobuf -import com.google.protobuf.gradle.protoc import org.gradle.kotlin.dsl.support.unzipTo import org.json.JSONObject import java.util.Properties @@ -27,7 +23,6 @@ plugins { id("com.android.application") kotlin("android") kotlin("kapt") - id("com.google.protobuf").version("0.8.17") id("org.jetbrains.compose").version(Versions.compose_jb) kotlin("plugin.serialization").version(Versions.Kotlin.lang) id("com.google.devtools.ksp").version(Versions.ksp) @@ -159,21 +154,6 @@ android { } } -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${Versions.protobuf}" - } - generateProtoTasks { - all().forEach { - it.builtins { - create("java") { - option("lite") - } - } - } - } -} - // TODO: workaround for https://github.com/google/ksp/issues/518 evaluationDependsOn(":assistedProcessor") evaluationDependsOn(":routeProcessor") diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index c2f51c77e..eb407eca7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -84,6 +84,7 @@ import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.MutableStateFlow import moe.tlaster.precompose.navigation.NavController +import org.koin.android.ext.android.inject import javax.inject.Inject @AndroidEntryPoint @@ -112,8 +113,7 @@ class TwidereXActivity : ComponentActivity() { @Inject lateinit var statusActions: StatusActions - @Inject - lateinit var preferencesHolder: PreferencesHolder + val preferencesHolder: PreferencesHolder by inject() @Inject lateinit var inAppNotification: InAppNotification diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 3ac33fa92..c02c327ac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -61,7 +61,7 @@ import com.twidere.twiderex.R import com.twidere.twiderex.component.status.UserAvatarDefaults import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.utils.video.CacheDataSourceFactory @@ -86,7 +86,6 @@ fun VideoPlayer( DisplayPreferences.AutoPlayback.Auto -> !isActiveNetworkMetered DisplayPreferences.AutoPlayback.Always -> true DisplayPreferences.AutoPlayback.Off -> false - DisplayPreferences.AutoPlayback.UNRECOGNIZED -> true } var autoPlay by remember(url) { mutableStateOf(playInitial) } val context = LocalContext.current diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 5d5a833fa..3d6832ef3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -81,7 +81,7 @@ import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt index 9d1de1263..b8d45a41c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt @@ -43,7 +43,7 @@ import com.twidere.twiderex.component.placeholder.UiImagePlaceholder import com.twidere.twiderex.component.status.StatusMediaPreviewItem import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalVideoPlayback @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt index d4b3cbb14..898144e5e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt @@ -24,7 +24,6 @@ import androidx.compose.animation.core.animateInt import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -40,7 +39,7 @@ import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.preferences.LocalDisplayPreferences -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences @Composable fun UserAvatar( @@ -84,7 +83,7 @@ fun Modifier.withAvatarClip(): Modifier { val percent by transition.animateInt { when (it) { DisplayPreferences.AvatarStyle.Round -> 50 - DisplayPreferences.AvatarStyle.Square, DisplayPreferences.AvatarStyle.UNRECOGNIZED, null -> 10 + DisplayPreferences.AvatarStyle.Square -> 10 } } this.clip(RoundedCornerShape(percent = percent)) diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt index fd78bd461..4c9d4d910 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -40,7 +40,7 @@ import com.twidere.twiderex.kmp.android.AndroidRemoteNavigator import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.preferences.proto.MiscPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import com.twidere.twiderex.utils.PlatformResolver import dagger.Module import dagger.Provides diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt index 7c6e785df..48495158c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt @@ -26,7 +26,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.compose.viewModel import com.twidere.twiderex.preferences.LocalAppearancePreferences -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences @Composable inline fun viewModel( diff --git a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt index f695d5bb2..dea862cbd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import com.twidere.services.http.HttpConfigProvider import com.twidere.services.http.config.HttpConfig import com.twidere.services.proxy.ProxyConfig -import com.twidere.twiderex.preferences.proto.MiscPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.runBlocking diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index 2ecbbb248..bab6ec1b5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -93,7 +93,7 @@ import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.preferences.LocalAppearancePreferences -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences import com.twidere.twiderex.scenes.home.HomeMenus import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalActiveAccountViewModel @@ -108,7 +108,7 @@ import kotlinx.coroutines.launch fun HomeScene() { val account = LocalActiveAccount.current ?: return val scope = rememberCoroutineScope() - val tabPosition = LocalAppearancePreferences.current.tapPosition + val tabPosition = LocalAppearancePreferences.current.tabPosition val hideTab = LocalAppearancePreferences.current.hideTabBarWhenScroll val hideFab = LocalAppearancePreferences.current.hideFabWhenScroll val hideAppBar = LocalAppearancePreferences.current.hideAppBarWhenScroll diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 90e05382a..95655f4bc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -100,7 +100,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index f2a4a6804..d4a17829e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -68,7 +68,7 @@ import com.twidere.twiderex.extensions.setOnSystemBarsVisibilityChangeListener import com.twidere.twiderex.extensions.showControls import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.ui.LocalWindow diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index 5d7391075..13b78ea80 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusImageList import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.viewmodel.twitter.search.TwitterSearchMediaViewModel diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index 701f2a570..168d65940 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -67,7 +67,7 @@ import com.twidere.twiderex.component.status.UserAvatarDefaults import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.isDarkTheme import com.twidere.twiderex.preferences.LocalAppearancePreferences -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.ui.primaryColors import com.twidere.twiderex.viewmodel.settings.AppearanceViewModel @@ -134,7 +134,7 @@ fun AppearanceScene() { AppearancePreferences.TabPosition.Top, AppearancePreferences.TabPosition.Bottom, ), - value = appearance.tapPosition, + value = appearance.tabPosition, onChanged = { viewModel.setTabPosition(it) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 66380e8a2..77d84fce4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -56,7 +56,7 @@ import com.twidere.twiderex.component.status.TimelineStatusComponent import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.preferences.LocalDisplayPreferences -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.DisplayViewModel diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index a062afccd..c8beb6e98 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -64,7 +64,7 @@ import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.preferences.proto.MiscPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.MiscViewModel diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt index e309c1365..565a1dc44 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt @@ -27,7 +27,7 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf import androidx.core.view.WindowInsetsControllerCompat import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import moe.tlaster.precompose.navigation.NavController diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt index 61f58aa10..fbe46da0e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt @@ -59,7 +59,7 @@ import com.google.accompanist.insets.statusBarsHeight import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.LocalDisplayPreferences -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences @Composable fun TwidereTheme( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt index 576d4b8d3..d378b435d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.twidere.twiderex.preferences.proto.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch @@ -38,43 +38,43 @@ class AppearanceViewModel @AssistedInject constructor( fun setPrimaryColorIndex(index: Int) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setPrimaryColorIndex(index).build() + it.copy(primaryColorIndex = index) } } fun setTabPosition(position: AppearancePreferences.TabPosition) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setTapPosition(position).build() + it.copy(tabPosition = position) } } fun setTheme(theme: AppearancePreferences.Theme) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setTheme(theme).build() + it.copy(theme = theme) } } fun setHideTabBarWhenScrolling(hide: Boolean) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setHideTabBarWhenScroll(hide).build() + it.copy(hideTabBarWhenScroll = hide) } } fun setHideFabWhenScrolling(hide: Boolean) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setHideFabWhenScroll(hide).build() + it.copy(hideFabWhenScroll = hide) } } fun setHideAppBarWhenScrolling(hide: Boolean) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setHideAppBarWhenScroll(hide).build() + it.copy(hideAppBarWhenScroll = hide) } } fun setIsDarkModePureBlack(value: Boolean) = viewModelScope.launch { appearancePreferences.updateData { - it.toBuilder().setIsDarkModePureBlack(value).build() + it.copy(isDarkModePureBlack = value) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index 0be86b177..c0d0b3cdf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.twidere.twiderex.preferences.proto.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch @@ -38,37 +38,37 @@ class DisplayViewModel @AssistedInject constructor( fun setUseSystemFontSize(value: Boolean) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setUseSystemFontSize(value).build() + it.copy(useSystemFontSize = value) } } fun setAvatarStyle(value: DisplayPreferences.AvatarStyle) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setAvatarStyle(value).build() + it.copy(avatarStyle = value) } } fun setMediaPreview(value: Boolean) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setMediaPreview(value).build() + it.copy(mediaPreview = value) } } fun setUrlPreview(value: Boolean) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setUrlPreview(value).build() + it.copy(urlPreview = value) } } fun setAutoPlayback(value: DisplayPreferences.AutoPlayback) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setAutoPlayback(value).build() + it.copy(autoPlayback = value) } } fun commitFontScale(value: Float) = viewModelScope.launch { displayPreferences.updateData { - it.toBuilder().setFontScale(value).build() + it.copy(fontScale = value) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index 9036d9a5f..14aafd602 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -25,13 +25,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.twidere.twiderex.R import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.preferences.proto.MiscPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import java.lang.NumberFormatException @OptIn(FlowPreview::class) class MiscViewModel @AssistedInject constructor( @@ -87,9 +86,7 @@ class MiscViewModel @AssistedInject constructor( nitter.value = value viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setNitterInstance(value) - .build() + it.copy(nitterInstance = value) } } } @@ -98,9 +95,7 @@ class MiscViewModel @AssistedInject constructor( useProxy.value = value viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setUseProxy(value) - .build() + it.copy(useProxy = value) } } } @@ -109,9 +104,7 @@ class MiscViewModel @AssistedInject constructor( proxyType.value = MiscPreferences.ProxyType.valueOf(value) viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setProxyType(proxyType.value) - .build() + it.copy(proxyType = proxyType.value) } } } @@ -120,9 +113,7 @@ class MiscViewModel @AssistedInject constructor( proxyServer.value = value viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setProxyServer(value) - .build() + it.copy(proxyServer = value) } } } @@ -136,9 +127,7 @@ class MiscViewModel @AssistedInject constructor( } viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setProxyPort(value.toInt()) - .build() + it.copy(proxyPort = value.toInt()) } } } @@ -147,9 +136,7 @@ class MiscViewModel @AssistedInject constructor( proxyUserName.value = value viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setProxyUserName(value) - .build() + it.copy(proxyUserName = value) } } } @@ -158,9 +145,7 @@ class MiscViewModel @AssistedInject constructor( proxyPassword.value = value viewModelScope.launch { miscPreferences.updateData { - it.toBuilder() - .setProxyPassword(value) - .build() + it.copy(proxyPassword = value) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt index d02b48bbc..7ec38097b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.twidere.twiderex.preferences.proto.NotificationPreferences +import com.twidere.twiderex.preferences.model.NotificationPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -41,9 +41,7 @@ class NotificationViewModel @AssistedInject constructor( fun setEnabled(value: Boolean) = viewModelScope.launch { notification.updateData { - it.toBuilder() - .setEnableNotification(value) - .build() + it.copy(enableNotification = value) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt index cdc1f91a9..cf10c11ba 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt @@ -26,7 +26,7 @@ import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.common.NotificationJob -import com.twidere.twiderex.preferences.proto.NotificationPreferences +import com.twidere.twiderex.preferences.model.NotificationPreferences import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.first diff --git a/android/src/main/proto/AppearancePreferences.proto b/android/src/main/proto/AppearancePreferences.proto deleted file mode 100644 index a3f29b3a3..000000000 --- a/android/src/main/proto/AppearancePreferences.proto +++ /dev/null @@ -1,25 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.twidere.twiderex.preferences.proto"; -option java_multiple_files = true; - -message AppearancePreferences { - enum TabPosition { - Top = 0; - Bottom = 1; - } - - enum Theme { - Auto = 0; - Light = 1; - Dark = 2; - } - - int32 primaryColorIndex = 1; - TabPosition tapPosition = 2; - Theme theme = 3; - bool hideTabBarWhenScroll = 4; - bool hideFabWhenScroll = 5; - bool hideAppBarWhenScroll = 6; - bool isDarkModePureBlack = 7; -} diff --git a/android/src/main/proto/DisplayPreferences.proto b/android/src/main/proto/DisplayPreferences.proto deleted file mode 100644 index 6a64d0be1..000000000 --- a/android/src/main/proto/DisplayPreferences.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.twidere.twiderex.preferences.proto"; -option java_multiple_files = true; - -message DisplayPreferences { - enum AvatarStyle { - Round = 0; - Square = 1; - } - - enum AutoPlayback { - Auto = 0; - Always = 1; - Off = 2; - } - - bool useSystemFontSize = 1; - float fontScale = 2; - AvatarStyle avatarStyle = 3; - bool mediaPreview = 4; - AutoPlayback autoPlayback = 5; - bool urlPreview = 6; -} \ No newline at end of file diff --git a/android/src/main/proto/MiscPreferences.proto b/android/src/main/proto/MiscPreferences.proto deleted file mode 100644 index 89772e720..000000000 --- a/android/src/main/proto/MiscPreferences.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.twidere.twiderex.preferences.proto"; -option java_multiple_files = true; - -message MiscPreferences { - enum ProxyType { - HTTP = 0; - REVERSE = 1; - } - - string nitterInstance = 1; - bool useProxy = 2; - ProxyType proxyType = 3; - string proxyServer = 4; - int32 proxyPort = 5; - string proxyUserName = 6; - string proxyPassword = 7; -} diff --git a/android/src/main/proto/NotificationPreferences.proto b/android/src/main/proto/NotificationPreferences.proto deleted file mode 100644 index d941934c7..000000000 --- a/android/src/main/proto/NotificationPreferences.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; - -option java_package = "com.twidere.twiderex.preferences.proto"; -option java_multiple_files = true; - -message NotificationPreferences { - bool enableNotification = 1; -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt similarity index 58% rename from common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt index cca500c59..e2d963199 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/preferencesModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt @@ -20,48 +20,30 @@ */ package com.twidere.twiderex.di -import androidx.datastore.core.DataStore import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.AppearancePreferences -import com.twidere.twiderex.preferences.DisplayPreferences -import com.twidere.twiderex.preferences.MiscPreferences -import com.twidere.twiderex.preferences.NotificationPreferences +import com.twidere.twiderex.preferences.PreferencesHolder import com.twidere.twiderex.preferences.serializer.AppearancePreferencesSerializer import com.twidere.twiderex.preferences.serializer.DisplayPreferencesSerializer import com.twidere.twiderex.preferences.serializer.MiscPreferencesSerializer import com.twidere.twiderex.preferences.serializer.NotificationPreferencesSerializer -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.firstOrNull import org.koin.core.scope.Scope import org.koin.dsl.module import java.io.File -internal fun preferencesModule() = module { +internal val preferencesModule = module { single { PreferencesHolder( - appearancePreferences = createDataStore("appearances.pb", AppearancePreferencesSerializer), + appearancePreferences = createDataStore( + "appearances.pb", + AppearancePreferencesSerializer + ), displayPreferences = createDataStore("display.pb", DisplayPreferencesSerializer), miscPreferences = createDataStore("misc.pb", MiscPreferencesSerializer), - notificationPreferences = createDataStore("notification.pb", NotificationPreferencesSerializer), - ) - } -} - -internal data class PreferencesHolder( - val appearancePreferences: DataStore, - val displayPreferences: DataStore, - val miscPreferences: DataStore, - val notificationPreferences: DataStore, -) { - suspend fun warmup() = coroutineScope { - awaitAll( - async { appearancePreferences.data.firstOrNull() }, - async { displayPreferences.data.firstOrNull() }, - async { miscPreferences.data.firstOrNull() }, - async { notificationPreferences.data.firstOrNull() }, + notificationPreferences = createDataStore( + "notification.pb", + NotificationPreferencesSerializer + ), ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index 0248014d5..1de30c247 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.di -fun setupModules() { - preferencesModule() +import org.koin.core.KoinApplication + +fun KoinApplication.setupModules() { + modules(preferencesModule) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt new file mode 100644 index 000000000..8d4b6fc78 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt @@ -0,0 +1,47 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.preferences + +import androidx.datastore.core.DataStore +import com.twidere.twiderex.preferences.model.AppearancePreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences +import com.twidere.twiderex.preferences.model.NotificationPreferences +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.firstOrNull + +data class PreferencesHolder( + val appearancePreferences: DataStore, + val displayPreferences: DataStore, + val miscPreferences: DataStore, + val notificationPreferences: DataStore, +) { + suspend fun warmup() = coroutineScope { + awaitAll( + async { appearancePreferences.data.firstOrNull() }, + async { displayPreferences.data.firstOrNull() }, + async { miscPreferences.data.firstOrNull() }, + async { notificationPreferences.data.firstOrNull() }, + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt index 0bc44cf71..a452b0e50 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt @@ -28,17 +28,19 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import com.twidere.services.http.config.HttpConfig import com.twidere.services.proxy.ProxyConfig -import com.twidere.twiderex.di.PreferencesHolder +import com.twidere.twiderex.preferences.model.AppearancePreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.map -internal val LocalAppearancePreferences = +val LocalAppearancePreferences = compositionLocalOf { error("No AppearancePreferences") } -internal val LocalDisplayPreferences = +val LocalDisplayPreferences = compositionLocalOf { error("No DisplayPreferences") } -internal val LocalHttpConfig = compositionLocalOf { error("No Http config preferences") } +val LocalHttpConfig = compositionLocalOf { error("No Http config preferences") } @Composable -internal fun ProvidePreferences( +fun ProvidePreferences( holder: PreferencesHolder, content: @Composable () -> Unit, ) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt similarity index 92% rename from common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt index ffb3004c2..b83a7352c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/AppearancePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt @@ -18,14 +18,14 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.preferences +package com.twidere.twiderex.preferences.model import kotlinx.serialization.Serializable @Serializable data class AppearancePreferences( val primaryColorIndex: Int = 0, - val tapPosition: TabPosition = TabPosition.Bottom, + val tabPosition: TabPosition = TabPosition.Bottom, val theme: Theme = Theme.Auto, val hideTabBarWhenScroll: Boolean = false, val hideFabWhenScroll: Boolean = false, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt similarity index 96% rename from common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt index 646f33f24..61d106fbf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/DisplayPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.preferences +package com.twidere.twiderex.preferences.model import kotlinx.serialization.Serializable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt similarity index 96% rename from common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt index d684c40b8..f1c0881d9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/MiscPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.preferences +package com.twidere.twiderex.preferences.model import kotlinx.serialization.Serializable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt similarity index 95% rename from common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt index 7e41b2ba4..bdcc7ded2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/NotificationPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.preferences +package com.twidere.twiderex.preferences.model import kotlinx.serialization.Serializable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt index 00c1f0641..8d1224768 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.preferences.serializer import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.AppearancePreferences +import com.twidere.twiderex.preferences.model.AppearancePreferences import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt index 9ff845249..9b6056198 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.preferences.serializer import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.DisplayPreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt index 5dd4606e7..b388e4aec 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.preferences.serializer import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.MiscPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt index 7bc321e4e..cc94b6654 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.preferences.serializer import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.NotificationPreferences +import com.twidere.twiderex.preferences.model.NotificationPreferences import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray From 8d598c6baf29d041aae2d5a657b338442543ba7e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 16:24:20 +0800 Subject: [PATCH 037/615] fix koin --- .../kotlin/com/twidere/twiderex/TwidereApp.kt | 3 +- .../com/twidere/twiderex/di/KoinModule.kt | 50 +++++++++++++++++++ .../twidere/twiderex/TwidereApplication.kt | 3 +- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt index 86f6abfd4..9fefbffda 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex +import android.app.Application import android.content.Context import androidx.hilt.work.HiltWorkerFactory import androidx.startup.AppInitializer @@ -31,7 +32,7 @@ import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @HiltAndroidApp -class TwidereApp : TwidereApplication(), Configuration.Provider { +class TwidereApp : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt new file mode 100644 index 000000000..34ff7fed0 --- /dev/null +++ b/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt @@ -0,0 +1,50 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.Koin +import org.koin.core.context.startKoin +import org.koin.mp.KoinPlatformTools +import javax.inject.Singleton + +@Deprecated("Use koin directly") +@Module +@InstallIn(SingletonComponent::class) +object KoinModule { + @Singleton + @Provides + fun getKoin(@ApplicationContext context: Context): Koin { + startKoin { + androidLogger() + androidContext(context) + setupModules() + } + return KoinPlatformTools.defaultContext().get() + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt index fef66e17f..7c68d129d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -24,7 +24,6 @@ import android.app.Application import com.twidere.twiderex.di.setupModules import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger -import org.koin.androidx.workmanager.koin.workManagerFactory import org.koin.core.KoinExperimentalAPI import org.koin.core.context.startKoin @@ -35,7 +34,7 @@ abstract class TwidereApplication : Application() { startKoin { androidLogger() androidContext(this@TwidereApplication) - workManagerFactory() + // workManagerFactory() setupModules() } } From d59baa12a215eba487e84ed270e42d84399d9b82 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 16:24:43 +0800 Subject: [PATCH 038/615] fix preferences module --- .../twidere/twiderex/di/PreferenceModule.kt | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt index e672c26a3..e508c5cbf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt @@ -20,57 +20,40 @@ */ package com.twidere.twiderex.di -import android.content.Context import androidx.datastore.core.DataStore -import androidx.datastore.core.DataStoreFactory -import androidx.datastore.core.Serializer -import com.twidere.twiderex.preferences.proto.AppearancePreferences -import com.twidere.twiderex.preferences.proto.DisplayPreferences -import com.twidere.twiderex.preferences.proto.MiscPreferences -import com.twidere.twiderex.preferences.proto.NotificationPreferences -import com.twidere.twiderex.preferences.serializer.AppearancePreferencesSerializer -import com.twidere.twiderex.preferences.serializer.DisplayPreferencesSerializer -import com.twidere.twiderex.preferences.serializer.MiscPreferencesSerializer -import com.twidere.twiderex.preferences.serializer.NotificationPreferencesSerializer +import com.twidere.twiderex.preferences.PreferencesHolder +import com.twidere.twiderex.preferences.model.AppearancePreferences +import com.twidere.twiderex.preferences.model.DisplayPreferences +import com.twidere.twiderex.preferences.model.MiscPreferences +import com.twidere.twiderex.preferences.model.NotificationPreferences import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import java.io.File +import org.koin.core.Koin import javax.inject.Singleton +@Deprecated("Use koin directly") @Module @InstallIn(SingletonComponent::class) object PreferenceModule { @Singleton @Provides - fun provideAppearances(@ApplicationContext context: Context): DataStore = - context.createDataStore("appearances.pb", AppearancePreferencesSerializer) + fun provideAppearances(koin: Koin): DataStore = + koin.get().appearancePreferences + @Singleton @Provides - fun provideDisplay(@ApplicationContext context: Context): DataStore = - context.createDataStore("display.pb", DisplayPreferencesSerializer) + fun provideDisplay(koin: Koin): DataStore = + koin.get().displayPreferences + @Singleton @Provides - fun provideMisc(@ApplicationContext context: Context): DataStore = - context.createDataStore("misc.pb", MiscPreferencesSerializer) + fun provideMisc(koin: Koin): DataStore = + koin.get().miscPreferences @Singleton @Provides - fun provideNotification(@ApplicationContext context: Context): DataStore = - context.createDataStore("notification.pb", NotificationPreferencesSerializer) + fun provideNotification(koin: Koin): DataStore = + koin.get().notificationPreferences } - -inline fun Context.createDataStore( - name: String, - serializer: Serializer, -) = DataStoreFactory.create( - serializer, - produceFile = { - File( - applicationContext.filesDir, - "datastore/$name" - ) - }, -) From e0f4348ea7a7cccff6aa1a232505fccd3eb83c01 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 16:27:35 +0800 Subject: [PATCH 039/615] Fix desktop datastore path --- .../kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt index 42aad38c9..4e5ced08a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt @@ -26,6 +26,6 @@ import java.io.File internal actual fun Scope.createDataStoreFile(name: String): File { return File( File(System.getProperty("user.home")), - "datastore/$name" + "TwidereX/datastore/$name" ) } From 0cd4e19cf4ea6029df6ada6b21492b21c7157042 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 17:14:56 +0800 Subject: [PATCH 040/615] update github actions --- .github/workflows/{build.yml => android.yml} | 10 ++-- .github/workflows/desktop.yml | 53 ++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) rename .github/workflows/{build.yml => android.yml} (94%) create mode 100644 .github/workflows/desktop.yml diff --git a/.github/workflows/build.yml b/.github/workflows/android.yml similarity index 94% rename from .github/workflows/build.yml rename to .github/workflows/android.yml index cf759989b..9bd60c1cc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/android.yml @@ -25,7 +25,7 @@ jobs: run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses - name: Build with Gradle - run: ./gradlew spotlessCheck lint build + run: ./gradlew :android:spotlessCheck :android:lint :android:build - name: Upload build reports uses: actions/upload-artifact@v2 @@ -49,7 +49,7 @@ jobs: run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses - name: Build with Gradle - run: ./gradlew test + run: ./gradlew :android:test - name: Upload test results if: always() @@ -91,7 +91,7 @@ jobs: run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses - name: Build tests - run: ./gradlew assembleAndroidTest + run: ./gradlew :android:assembleAndroidTest - name: Run tests uses: reactivecircus/android-emulator-runner@v2 @@ -102,7 +102,7 @@ jobs: target: ${{ matrix.target }} script: | adb logcat > logcat.txt & - ./gradlew connectedCheck + ./gradlew :android:connectedCheck - name: Upload logs if: always() @@ -145,7 +145,7 @@ jobs: run: ./.github/apply_signing.sh - name: Build with Gradle - run: ./gradlew assembleRelease bundleRelease + run: ./gradlew :android:assembleRelease :android:bundleRelease - name: Check if is prelease if: startsWith(github.ref, 'refs/tags/') diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml new file mode 100644 index 000000000..b0804e146 --- /dev/null +++ b/.github/workflows/desktop.yml @@ -0,0 +1,53 @@ +name: Desktop CI + +on: + push: + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Build with Gradle + run: ./gradlew :desktop:spotlessCheck :desktop:lint :desktop:build + + - name: Upload build reports + uses: actions/upload-artifact@v2 + with: + name: build-reports + path: '**/build/reports' + + unit-test: + runs-on: ubuntu-latest + needs: build + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Build with Gradle + run: ./gradlew :desktop:test + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: unit-test-result + path: "**/build/test-results/**/*.xml" From e8c4c1ff4e02c6c3e6b754ed06c02dbfcf92d0c6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 17:18:31 +0800 Subject: [PATCH 041/615] update github actions --- .github/workflows/desktop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index b0804e146..691d1508e 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -22,7 +22,7 @@ jobs: java-version: 11 - name: Build with Gradle - run: ./gradlew :desktop:spotlessCheck :desktop:lint :desktop:build + run: ./gradlew :desktop:spotlessCheck :desktop:build - name: Upload build reports uses: actions/upload-artifact@v2 From d3723aa5bdd0f7351b1eecf5d7e8983ce8fce09a Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 9 Aug 2021 17:31:12 +0800 Subject: [PATCH 042/615] update github actions --- .github/workflows/desktop.yml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 691d1508e..eb2ea8fac 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -30,24 +30,3 @@ jobs: name: build-reports path: '**/build/reports' - unit-test: - runs-on: ubuntu-latest - needs: build - timeout-minutes: 30 - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK - uses: actions/setup-java@v1 - with: - java-version: 11 - - - name: Build with Gradle - run: ./gradlew :desktop:test - - - name: Upload test results - if: always() - uses: actions/upload-artifact@v2 - with: - name: unit-test-result - path: "**/build/test-results/**/*.xml" From 16a6f94fe604b2df464a2a9359c4c5bfe243da6a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 9 Aug 2021 17:48:14 +0800 Subject: [PATCH 043/615] add test for media repository --- .../model/transform/MediaTransform.kt | 1 + common/build.gradle.kts | 1 + .../twiderex/dataprovider/DataTransform.kt | 51 +++++++++++++++++ .../twidere/twiderex/db/AndroidAppDatabase.kt | 4 ++ .../twiderex/db/AndroidCacheDatabase.kt | 4 ++ .../twiderex/dataprovider/DataTransform.kt | 4 ++ .../db/dao/DirectMessageConversationDao.kt | 15 +++++ .../twiderex/db/dao/DirectMessageEventDao.kt | 10 ++++ .../com/twidere/twiderex/db/dao/StatusDao.kt | 2 +- .../com/twidere/twiderex/db/dao/UserDao.kt | 9 ++- .../twidere/twiderex/model/ui/UiDMEvent.kt | 3 + .../com/twidere/twiderex/model/ui/UiMedia.kt | 21 +++++-- .../twiderex/repository/CacheRepository.kt | 2 +- .../repository/DirectMessageRepository.kt | 56 ++++++++----------- .../twiderex/repository/MediaRepository.kt | 6 +- .../twidere/twiderex/mock/db/MockMediaDao.kt | 34 +++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 39 +++++++++++++ .../repository/MediaRepositoryTest.kt | 41 ++++++++++++++ .../twiderex/dataprovider/DataTransform.kt | 51 +++++++++++++++++ .../twiderex/db/DesktopAppDatabaseImpl.kt | 4 ++ .../twiderex/db/DesktopCacheDatabaseImpl.kt | 4 ++ 21 files changed, 317 insertions(+), 45 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt (80%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt index acae8388e..95b655228 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.model.ui.UiMedia fun List.toUi() = sortedBy { it.order }.map { UiMedia( url = it.url, + belongToKey = it.belongToKey, mediaUrl = it.mediaUrl, previewUrl = it.previewUrl, type = it.type, diff --git a/common/build.gradle.kts b/common/build.gradle.kts index efb8c1e41..715662b56 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -43,6 +43,7 @@ kotlin { dependencies { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") } } val androidMain by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt new file mode 100644 index 000000000..edd6c7873 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.services.microblog.model.INotification +import com.twidere.services.microblog.model.IStatus +import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.DirectMessageEvent +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiUser + +actual fun IUser.toUi(accountKey: MicroBlogKey): UiUser { + TODO("Not yet implemented") +} + +actual fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus { + TODO("Not yet implemented") +} + +actual fun INotification.toUi(accountKey: MicroBlogKey): UiStatus { + TODO("Not yet implemented") +} + +actual fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus { + TODO("Not yet implemented") +} + +actual fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent { + TODO("Not yet implemented") +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt index 40922351e..0d105af15 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt @@ -28,6 +28,10 @@ internal class AndroidAppDatabase : AppDatabase { TODO("Not yet implemented") } + override fun withTransaction(block: suspend () -> R): R { + TODO("Not yet implemented") + } + override fun draftDao(): DraftDao { TODO("Not yet implemented") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt index 81342c490..2daaf8cdb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt @@ -35,4 +35,8 @@ internal class AndroidCacheDatabase : AppDatabase { override suspend fun clearAllTables() { TODO("Not yet implemented") } + + override fun withTransaction(block: suspend () -> R): R { + TODO("Not yet implemented") + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt index 901a1077d..94059ef21 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -23,8 +23,10 @@ package com.twidere.twiderex.dataprovider import com.twidere.services.microblog.model.INotification import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser @@ -35,3 +37,5 @@ expect fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus expect fun INotification.toUi(accountKey: MicroBlogKey): UiStatus expect fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus + +expect fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt index 19d349944..ac1f6cf66 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt @@ -22,8 +22,23 @@ package com.twidere.twiderex.db.dao import androidx.paging.PagingSource import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversation import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import kotlinx.coroutines.flow.Flow interface DirectMessageConversationDao { fun getPagingSource(accountKey: MicroBlogKey): PagingSource + fun findWithConversationKeyFlow( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Flow + + suspend fun findWithConversationKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): UiDMConversation? + + suspend fun insertAll(listOf: List) + suspend fun find(accountKey: MicroBlogKey): List + suspend fun delete(it: UiDMConversation) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt index 266075481..a36dd5da6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt @@ -30,4 +30,14 @@ interface DirectMessageEventDao { accountKey: MicroBlogKey, conversationKey: MicroBlogKey ): PagingSource + + suspend fun findWithMessageKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + messageKey: MicroBlogKey + ): UiDMEvent? + + suspend fun delete(message: UiDMEvent) + suspend fun getMessageCount(accountKey: MicroBlogKey, conversationKey: MicroBlogKey): Long + suspend fun insertAll(events: List) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 60d9f903e..78cfa0cdf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -25,6 +25,6 @@ import com.twidere.twiderex.model.ui.UiStatus // TODO OPERATION interface StatusDao { - suspend fun findWithStatusKey(it: MicroBlogKey): UiStatus + suspend fun findWithStatusKey(it: MicroBlogKey): UiStatus? suspend fun insertAll(it: List) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt index 787fbc600..5f6d73546 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt @@ -19,5 +19,12 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser + // TODO OPERATION -interface UserDao +interface UserDao { + suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? + suspend fun insertAll(listOf: List) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt index 29536a905..640abbc4a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt @@ -44,6 +44,9 @@ data class UiDMEvent( val isInCome: Boolean get() = recipientAccountKey == accountKey + val conversationUserKey: MicroBlogKey + get() = if (accountKey == senderAccountKey) recipientAccountKey else senderAccountKey + enum class SendStatus { PENDING, SUCCESS, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 1d1e34252..6b108a19f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -20,14 +20,16 @@ */ package com.twidere.twiderex.model.ui -import android.net.Uri import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType +import java.net.URI @Immutable data class UiMedia( val url: String?, + val belongToKey: MicroBlogKey, val mediaUrl: String?, val previewUrl: Any?, val type: MediaType, @@ -44,23 +46,32 @@ data class UiMedia( val start = it.indexOf("?format=") val end = it.indexOf("&name=") val ext = it.substring(start + "?format=".length, end) - Uri.parse(it).lastPathSegment + ".$ext" + it.lastPathSegment() + ".$ext" } } private fun String.takeIfFileName(): String? { - return let { - Uri.parse(it) - }?.lastPathSegment?.takeIf { + return lastPathSegment().takeIf { it.contains(".") } } + private fun String.lastPathSegment(): String { + return try { + URI.create(this).path.let { + it.substring(it.lastIndexOf("/") + 1) + } + } catch (e: Throwable) { + "" + } + } + companion object { @Composable fun sample() = listOf( UiMedia( url = null, + belongToKey = MicroBlogKey.Empty, mediaUrl = null, previewUrl = "", // painterResource(id = R.drawable.featured_graphics), type = MediaType.photo, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt index 3aa8af310..bfdbb07bf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -42,7 +42,7 @@ class CacheRepository( suspend fun clearImageCache() = coroutineScope { mediaCache.clear() - cache.directory().deleteRecursively() + cache.directory.deleteRecursively() } suspend fun clearCacheDir() = coroutineScope { diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt similarity index 80% rename from android/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt index d46743908..df179ade1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt @@ -21,22 +21,15 @@ package com.twidere.twiderex.repository import androidx.paging.PagingData -import androidx.room.withTransaction import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.model.IDirectMessage import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.exceptions.TwitterApiException +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbDirectMessage -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDMConversation.Companion.saveToDb -import com.twidere.twiderex.db.model.DbDMEventWithAttachments.Companion.saveToDb -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiDMConversation import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage import com.twidere.twiderex.model.ui.UiDMEvent @@ -46,8 +39,6 @@ import com.twidere.twiderex.paging.mediator.dm.DMConversationMediator.Companion. import com.twidere.twiderex.paging.mediator.dm.DMEventMediator import com.twidere.twiderex.paging.mediator.dm.DMEventMediator.Companion.toUi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import java.util.UUID class DirectMessageRepository( private val database: CacheDatabase @@ -60,7 +51,7 @@ class DirectMessageRepository( .findWithConversationKeyFlow( accountKey = accountKey, conversationKey = conversationKey - ).map { it?.toUi() } + ) } fun dmConversationListSource( @@ -109,15 +100,14 @@ class DirectMessageRepository( ?: let { database.directMessageConversationDao().insertAll( listOf( - DbDMConversation( - _id = UUID.randomUUID().toString(), + UiDMConversation( accountKey = accountKey, conversationId = conversationId, conversationKey = conversationKey, conversationAvatar = receiver.profileImage.toString(), conversationName = receiver.displayName, conversationSubName = receiver.screenName, - conversationType = DbDMConversation.Type.ONE_TO_ONE, + conversationType = UiDMConversation.Type.ONE_TO_ONE, recipientKey = receiver.userKey ) ) @@ -136,13 +126,11 @@ class DirectMessageRepository( val needDrop = con.latestMessage.sender.userKey == accountKey || oldConversation.find { // self send message or same received message - it.latestMessage.message.messageKey == con.latestMessage.message.messageKey + it.latestMessage.messageKey == con.latestMessage.messageKey }?.let { true } ?: false needDrop } newConversation - }.map { - it.toUi() } } @@ -158,7 +146,7 @@ class DirectMessageRepository( conversationKey, messageKey )?.let { - database.directMessageDao().delete(it.message) + database.directMessageDao().delete(it) try { service.destroyDirectMessage(messageId) } catch (e: TwitterApiException) { @@ -179,38 +167,38 @@ class DirectMessageRepository( val result = service.getDirectMessages(key, 50) val events = result.map { if (it is DirectMessageEvent) { - it.toDbDirectMessage(accountKey, lookupUser(accountKey, MicroBlogKey.twitter(it.messageCreate?.senderId ?: ""), lookupService)) + it.toUi(accountKey, lookupUser(accountKey, MicroBlogKey.twitter(it.messageCreate?.senderId ?: ""), lookupService)) } else throw NotImplementedError() } // save message, media database.withTransaction { - events.saveToDb(database) - events.groupBy { it.message.conversationKey } + database.directMessageDao().insertAll(events) + events.groupBy { it.conversationKey } .map { entry -> val msgWithData = entry.value.first() - val chatUser = msgWithData.message.conversationUserKey.let { - lookupUser(accountKey, it, lookupService) - } - DbDMConversation( - _id = UUID.randomUUID().toString(), + val chatUser = + lookupUser(accountKey, msgWithData.conversationUserKey, lookupService) + UiDMConversation( accountKey = accountKey, - conversationId = msgWithData.message.conversationKey.id, - conversationKey = msgWithData.message.conversationKey, - conversationAvatar = chatUser.profileImage, + conversationId = msgWithData.conversationKey.id, + conversationKey = msgWithData.conversationKey, + conversationAvatar = chatUser.profileImage.toString(), conversationName = chatUser.name, conversationSubName = chatUser.screenName, - conversationType = DbDMConversation.Type.ONE_TO_ONE, - recipientKey = msgWithData.message.conversationUserKey + conversationType = UiDMConversation.Type.ONE_TO_ONE, + recipientKey = msgWithData.conversationUserKey ) - }.saveToDb(database) + }.let { + database.directMessageConversationDao().insertAll(it) + } } return result } - private suspend fun lookupUser(accountKey: MicroBlogKey, userKey: MicroBlogKey, service: LookupService): DbUser { + private suspend fun lookupUser(accountKey: MicroBlogKey, userKey: MicroBlogKey, service: LookupService): UiUser { return database.userDao().findWithUserKey(userKey) ?: let { val user = service.lookupUser(userKey.id) - .toDbUser(accountKey) + .toUi(accountKey) database.userDao().insertAll(listOf(user)) user } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt index 637a01740..2a566dfe8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt @@ -19,11 +19,11 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.repository -import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.db.dao.MediaDao import com.twidere.twiderex.model.MicroBlogKey -class MediaRepository(private val cacheDatabase: CacheDatabase) { +class MediaRepository(private val mediaDao: MediaDao) { suspend fun findMediaByBelongToKey( belongToKey: MicroBlogKey - ) = cacheDatabase.mediaDao().findMediaByBelongToKey(belongToKey) + ) = mediaDao.findMediaByBelongToKey(belongToKey) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt new file mode 100644 index 000000000..34662a009 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt @@ -0,0 +1,34 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db + +import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiMedia +import org.jetbrains.annotations.TestOnly + +internal class MockMediaDao @TestOnly constructor(private val initData: List = emptyList()) : MediaDao { + override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { + return initData.filter { + it.belongToKey == belongToKey + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt new file mode 100644 index 000000000..d6f04d2fa --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.model + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiMedia +import org.jetbrains.annotations.TestOnly + +@TestOnly +internal fun UiMedia.Companion.mock(url: String, belongToKey: MicroBlogKey) = UiMedia( + url = url, + belongToKey = belongToKey, + mediaUrl = url, + previewUrl = url, + type = MediaType.photo, + width = 100, + height = 100, + pageUrl = "", + altText = "" +) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt new file mode 100644 index 000000000..fc9d0ce6f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockMediaDao +import com.twidere.twiderex.mock.model.mock +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiMedia +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class MediaRepositoryTest { + @Test + fun findMediasWithBelongToKey() = runBlocking { + val repository = MediaRepository( + MockMediaDao( + listOf(UiMedia.mock("test", MicroBlogKey.twitter("account"))) + ) + ) + val result = repository.findMediaByBelongToKey(MicroBlogKey.twitter("account")) + assert(result.isNotEmpty()) + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt new file mode 100644 index 000000000..edd6c7873 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider + +import com.twidere.services.microblog.model.INotification +import com.twidere.services.microblog.model.IStatus +import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.DirectMessageEvent +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiUser + +actual fun IUser.toUi(accountKey: MicroBlogKey): UiUser { + TODO("Not yet implemented") +} + +actual fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus { + TODO("Not yet implemented") +} + +actual fun INotification.toUi(accountKey: MicroBlogKey): UiStatus { + TODO("Not yet implemented") +} + +actual fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus { + TODO("Not yet implemented") +} + +actual fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent { + TODO("Not yet implemented") +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt index c72c050dc..0c794c8a3 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt @@ -35,4 +35,8 @@ internal class DesktopAppDatabaseImpl : AppDatabase { override suspend fun clearAllTables() { TODO("Not yet implemented") } + + override fun withTransaction(block: suspend () -> R): R { + TODO("Not yet implemented") + } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt index 94e93da80..4a8d30ae4 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt @@ -24,4 +24,8 @@ internal class DesktopCacheDatabaseImpl : Database { override suspend fun clearAllTables() { TODO("Not yet implemented") } + + override fun withTransaction(block: suspend () -> R): R { + TODO("Not yet implemented") + } } From fd2e99d83c1a445c8f86eebd3223d8d0f146549a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 10 Aug 2021 14:48:09 +0800 Subject: [PATCH 044/615] add test for CacheRepository --- .../twiderex/dataprovider/DataProvider.kt | 3 ++ .../twiderex/cache/AppCacheHandler.kt} | 12 ++++--- .../twiderex/dataprovider/DataProvider.kt | 3 ++ .../com/twidere/twiderex/db/CacheDatabase.kt | 26 ++++++++-------- .../kotlin/com/twidere/twiderex/di/Setup.kt | 17 ++++++++++ .../twiderex/repository/CacheRepository.kt | 31 +++++++++---------- .../mock/cache/MockAppCacheHandler.kt} | 29 +++++++++++++++-- .../repository/CacheRepositoryTest.kt} | 24 +++++++++++--- .../twiderex/dataprovider/DataProvider.kt | 3 ++ 9 files changed, 107 insertions(+), 41 deletions(-) rename common/src/{androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt => commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt} (84%) rename common/src/{commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt => commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt} (50%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt => commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt} (51%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index e6d004adb..f336a0030 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.dataprovider +import com.twidere.twiderex.cache.AppCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -36,4 +37,6 @@ actual class DataProvider { actual val cacheDatabase: CacheDatabase get() = TODO("Not yet implemented") + actual val appCacheHandler: AppCacheHandler + get() = TODO("Not yet implemented") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt similarity index 84% rename from common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt index 1c785cfbd..0d52b6e21 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt @@ -20,8 +20,12 @@ */ package com.twidere.twiderex.cache -actual class MediaCache { - actual fun clear() { - TODO("Not yet implemented") - } +interface AppCacheHandler { + fun clearMediaCaches() + + fun clearFileCaches() + + fun clearDatabaseCaches() + + fun clearSearchHistories() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index 878567572..34d05c271 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.dataprovider +import com.twidere.twiderex.cache.AppCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -32,4 +33,6 @@ expect class DataProvider { val appDatabase: AppDatabase val cacheDatabase: CacheDatabase + + val appCacheHandler: AppCacheHandler } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt index 25c0985e9..59ef900a1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt @@ -35,17 +35,17 @@ import com.twidere.twiderex.db.dao.UrlEntityDao import com.twidere.twiderex.db.dao.UserDao interface CacheDatabase : Database { - abstract fun statusDao(): StatusDao - abstract fun mediaDao(): MediaDao - abstract fun userDao(): UserDao - abstract fun reactionDao(): ReactionDao - abstract fun pagingTimelineDao(): PagingTimelineDao - abstract fun urlEntityDao(): UrlEntityDao - abstract fun statusReferenceDao(): StatusReferenceDao - abstract fun listsDao(): ListsDao - abstract fun notificationCursorDao(): NotificationCursorDao - abstract fun trendDao(): TrendDao - abstract fun trendHistoryDao(): TrendHistoryDao - abstract fun directMessageConversationDao(): DirectMessageConversationDao - abstract fun directMessageDao(): DirectMessageEventDao + fun statusDao(): StatusDao + fun mediaDao(): MediaDao + fun userDao(): UserDao + fun reactionDao(): ReactionDao + fun pagingTimelineDao(): PagingTimelineDao + fun urlEntityDao(): UrlEntityDao + fun statusReferenceDao(): StatusReferenceDao + fun listsDao(): ListsDao + fun notificationCursorDao(): NotificationCursorDao + fun trendDao(): TrendDao + fun trendHistoryDao(): TrendHistoryDao + fun directMessageConversationDao(): DirectMessageConversationDao + fun directMessageDao(): DirectMessageEventDao } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index a94e01659..84dad0712 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -20,5 +20,22 @@ */ package com.twidere.twiderex.di +import com.twidere.twiderex.dataprovider.DataProvider +import com.twidere.twiderex.repository.CacheRepository +import com.twidere.twiderex.repository.MediaRepository +import org.koin.dsl.module + +val twidereModules = module { + single { DataProvider.create() } + single { get().appCacheHandler } + single { get().cacheDatabase } + single { get().appDatabase } +} + +val repositoryModules = module { + factory { MediaRepository(get().cacheDatabase.mediaDao()) } + factory { CacheRepository(get()) } +} + fun setupModules() { } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt index bfdbb07bf..7ebcfb40e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -20,44 +20,41 @@ */ package com.twidere.twiderex.repository -import com.twidere.twiderex.cache.MediaCache -import com.twidere.twiderex.dataprovider.DataProvider +import com.twidere.twiderex.cache.AppCacheHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch -import okhttp3.Cache -import java.io.File class CacheRepository( - private val dataProvider: DataProvider = DataProvider.create(), - private val cache: Cache, - private val mediaCache: MediaCache, - private val cacheDirs: List, + private val appCache: AppCacheHandler, ) { suspend fun clearDatabaseCache() = coroutineScope { launch(Dispatchers.IO) { - dataProvider.cacheDatabase.clearAllTables() + appCache.clearDatabaseCaches() + // cacheDatabase.clearAllTables() } } suspend fun clearImageCache() = coroutineScope { - mediaCache.clear() - cache.directory.deleteRecursively() + appCache.clearMediaCaches() + // cache.directory.deleteRecursively() } suspend fun clearCacheDir() = coroutineScope { launch(Dispatchers.IO) { - cacheDirs.forEach { - it.listFiles()?.forEach { file -> - file.deleteRecursively() - } - } + appCache.clearFileCaches() + // cacheDirs.forEach { + // it.listFiles()?.forEach { file -> + // file.deleteRecursively() + // } + // } } } suspend fun clearSearchHistory() = coroutineScope { launch(Dispatchers.IO) { - dataProvider.appDatabase.searchDao().clear() + appCache.clearSearchHistories() + // appDatabase.searchDao().clear() } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt similarity index 50% rename from common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt index ffa7fc130..e75e29930 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt @@ -18,8 +18,31 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.cache +package com.twidere.twiderex.mock.cache -expect class MediaCache { - fun clear() +import com.twidere.twiderex.cache.AppCacheHandler + +internal class MockAppCacheHandler( + private val mediaCache: MutableList, + private val fileCache: MutableList, + private val database: MutableList, + private val searchHistories: MutableList +) : AppCacheHandler { + override fun clearMediaCaches() { + mediaCache.clear() + } + + override fun clearFileCaches() { + fileCache.clear() + } + + override fun clearDatabaseCaches() { + database.clear() + } + + override fun clearSearchHistories() { + searchHistories.clear() + } + + fun isCacheCleared() = mediaCache.isEmpty() && fileCache.isEmpty() && database.isEmpty() && searchHistories.isEmpty() } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt similarity index 51% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt index 1c785cfbd..44f1b34e3 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/cache/MediaCache.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt @@ -18,10 +18,26 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.cache +package com.twidere.twiderex.repository -actual class MediaCache { - actual fun clear() { - TODO("Not yet implemented") +import com.twidere.twiderex.mock.cache.MockAppCacheHandler +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class CacheRepositoryTest { + @Test + fun clearAllCachesSuccess() = runBlocking { + val handler = MockAppCacheHandler( + mediaCache = mutableListOf("media"), + fileCache = mutableListOf("file"), + database = mutableListOf("database"), + searchHistories = mutableListOf("search"), + ) + val repository = CacheRepository(handler) + repository.clearCacheDir() + repository.clearDatabaseCache() + repository.clearImageCache() + repository.clearSearchHistory() + assert(handler.isCacheCleared()) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index e6d004adb..f336a0030 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.dataprovider +import com.twidere.twiderex.cache.AppCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -36,4 +37,6 @@ actual class DataProvider { actual val cacheDatabase: CacheDatabase get() = TODO("Not yet implemented") + actual val appCacheHandler: AppCacheHandler + get() = TODO("Not yet implemented") } From 42265c56d4937d3ad09acbb1b28f36d518de60c3 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 10 Aug 2021 15:59:44 +0800 Subject: [PATCH 045/615] add koin viewmodel extension --- .../twidere/twiderex/di/ext/ViewModelExt.kt | 76 +++++++++++++++++++ .../precompose/ui/ComposeCompositionLocal.kt | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt new file mode 100644 index 000000000..4f0888f6a --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt @@ -0,0 +1,76 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.ext + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner +import org.koin.core.parameter.ParametersDefinition +import org.koin.core.qualifier.Qualifier +import org.koin.mp.KoinPlatformTools +import kotlin.reflect.KClass + +@Composable +inline fun getViewModel( + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null, +): T { + val owner = LocalViewModelStoreOwner.current + return remember(qualifier, parameters) { + owner.getViewModel(qualifier, parameters) + } +} + +inline fun ViewModelStoreOwner.viewModel( + qualifier: Qualifier? = null, + mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED, + noinline parameters: ParametersDefinition? = null, +): Lazy { + return lazy(mode) { + getViewModel(qualifier, parameters) + } +} + +fun ViewModelStoreOwner.viewModel( + qualifier: Qualifier? = null, + clazz: KClass, + mode: LazyThreadSafetyMode = LazyThreadSafetyMode.SYNCHRONIZED, + parameters: ParametersDefinition? = null, +): Lazy { + return lazy(mode) { getViewModel(qualifier, clazz, parameters) } +} + +inline fun ViewModelStoreOwner.getViewModel( + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null, +): T { + return getViewModel(qualifier, T::class, parameters) +} + +fun ViewModelStoreOwner.getViewModel( + qualifier: Qualifier? = null, + clazz: KClass, + parameters: ParametersDefinition? = null, +): T { + return KoinPlatformTools.defaultContext().get().get(clazz, qualifier, parameters) +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt index 0f49066b7..9216697c3 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt @@ -26,7 +26,7 @@ import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner val LocalLifecycleOwner = compositionLocalOf { null } -val LocalViewModelStoreOwner = compositionLocalOf { null } +val LocalViewModelStoreOwner = compositionLocalOf { noLocalProvidedFor("ViewModelStoreOwner") } private fun noLocalProvidedFor(name: String): Nothing { error("CompositionLocal $name not present") From b5aa6b62dfcc8521a4053695ddbacaa0feb9d771 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 10 Aug 2021 18:06:27 +0800 Subject: [PATCH 046/615] migrate all repositories except AccountRepository to commonMain --- .../twiderex/db/AppDatabaseMigrationTest.kt | 2 +- .../component/navigation/Navigator.kt | 2 +- .../component/status/StatusActions.kt | 2 +- .../twidere/twiderex/db/dao/ReactionDao.kt | 6 ++ .../com/twidere/twiderex/db/model/DbDraft.kt | 2 +- .../model/converter/ComposeTypeConverter.kt | 2 +- .../twiderex/jobs/compose/ComposeJob.kt | 2 +- .../jobs/compose/MastodonComposeJob.kt | 2 +- .../jobs/compose/TwitterComposeJob.kt | 2 +- .../twidere/twiderex/model/job/ComposeData.kt | 2 +- .../model/transform/WorkDataTransform.kt | 2 +- .../com/twidere/twiderex/navigation/Root.kt | 2 +- .../twiderex/navigation/RootDeepLinks.kt | 2 +- .../com/twidere/twiderex/navigation/Route.kt | 2 +- .../twiderex/scenes/compose/ComposeScene.kt | 2 +- .../twiderex/scenes/home/HomeTimelineItem.kt | 2 +- .../viewmodel/compose/ComposeViewModel.kt | 8 +-- .../worker/status/UpdateStatusWorker.kt | 9 +-- .../twiderex/dataprovider/DataTransform.kt | 4 ++ .../com/twidere/twiderex/db/CacheDatabase.kt | 6 -- .../com/twidere/twiderex/db/dao/DraftDao.kt | 11 ++- .../com/twidere/twiderex/db/dao/ListsDao.kt | 6 ++ .../twiderex/db/dao/PagingTimelineDao.kt | 1 + .../twidere/twiderex/db/dao/ReactionDao.kt | 11 ++- .../com/twidere/twiderex/db/dao/SearchDao.kt | 19 +++++- .../com/twidere/twiderex/db/dao/StatusDao.kt | 8 +++ .../com/twidere/twiderex/db/dao/UserDao.kt | 2 + .../twidere/twiderex/di/RepositoryModule.kt | 22 +++++- .../com/twidere/twiderex/di/TwidereModule.kt | 22 +++++- .../http/TwidereHttpConfigProvider.kt | 3 +- .../twiderex/http/TwidereServiceFactory.kt | 0 .../twiderex/model/cred/BasicCredentials.kt | 0 .../twiderex/model/cred/Credentials.kt | 0 .../twiderex/model/cred/CredentialsType.kt | 0 .../twiderex/model/cred/EmptyCredentials.kt | 0 .../twiderex/model/cred/OAuth2Credentials.kt | 0 .../twiderex/model/cred/OAuthCredentials.kt | 0 .../enums/ComposeType.kt} | 10 ++- .../UrlEntityDao.kt => model/ui/UiDraft.kt} | 16 ++++- .../ui/UiSearch.kt} | 12 +++- .../paging/PagingTimelineMediatorBase.kt | 12 ++++ .../repository/AccountUpdateRepository.kt | 35 ++++++++++ .../twiderex/repository/DraftRepository.kt | 16 ++--- .../twiderex/repository/ListsRepository.kt | 67 +++++++++---------- .../repository/ListsUsersRepository.kt | 30 ++++++--- .../twiderex/repository/ReactionRepository.kt | 35 +++++----- .../twiderex/repository/SearchRepository.kt | 10 ++- .../twiderex/repository/StatusRepository.kt | 51 +++++++------- .../twiderex/repository/TimelineRepository.kt | 43 ++++++------ .../twiderex/repository/TrendRepository.kt | 9 +-- .../twiderex/repository/UserRepository.kt | 35 +++------- .../kotlin/com/twidere/twiderex/utils/Json.kt | 39 +++++++++++ 52 files changed, 385 insertions(+), 203 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/Credentials.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt (100%) rename common/src/commonMain/kotlin/com/twidere/twiderex/{db/dao/StatusReferenceDao.kt => model/enums/ComposeType.kt} (87%) rename common/src/commonMain/kotlin/com/twidere/twiderex/{db/dao/UrlEntityDao.kt => model/ui/UiDraft.kt} (67%) rename common/src/commonMain/kotlin/com/twidere/twiderex/{db/dao/TrendHistoryDao.kt => model/ui/UiSearch.kt} (78%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/DraftRepository.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/ListsRepository.kt (71%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt (82%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt (60%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/SearchRepository.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/StatusRepository.kt (71%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt (72%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/TrendRepository.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/repository/UserRepository.kt (65%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt index cd033859b..e8d735570 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt @@ -24,7 +24,7 @@ import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.SimpleSQLiteQuery import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt index cde7e3520..3ddbe99f2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt @@ -27,6 +27,7 @@ import android.net.Uri import android.webkit.CookieManager import androidx.compose.runtime.staticCompositionLocalOf import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType @@ -34,7 +35,6 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.navigation.twidereXSchema -import com.twidere.twiderex.viewmodel.compose.ComposeType import moe.tlaster.precompose.navigation.NavController import moe.tlaster.precompose.navigation.NavOptions diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 752fb1c79..8ecc5297c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -64,10 +64,10 @@ import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.extensions.humanizedCount import com.twidere.twiderex.extensions.shareText +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.ui.LocalActiveAccount -import com.twidere.twiderex.viewmodel.compose.ComposeType private val rippleSize = 24.dp diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt index 624f1a9c7..cae78059b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt @@ -34,4 +34,10 @@ interface ReactionDao { @Query("SELECT * FROM status_reactions WHERE statusKey == :statusKey AND accountKey == :accountKey") suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): DbStatusReaction? + suspend fun updateReaction( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + liked: Boolean?, + retweeted: Boolean? + ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt index cfcd3e270..c75936919 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.db.model import androidx.room.Entity import androidx.room.PrimaryKey import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType @Entity( tableName = "draft", diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt index b99a896da..bd912c702 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db.model.converter import androidx.room.TypeConverter -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType class ComposeTypeConverter { @TypeConverter diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 4fe75382b..07f50021a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.R import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.navigation.RootDeepLinksRoute @@ -33,7 +34,6 @@ import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.viewmodel.compose.ComposeType import kotlin.math.roundToInt abstract class ComposeJob( diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index 65b49d11b..e7be78ff7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -32,13 +32,13 @@ import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.viewmodel.compose.ComposeType import java.io.File import java.net.URI diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index b629de39e..5f43c5a01 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -29,13 +29,13 @@ import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.viewmodel.compose.ComposeType class TwitterComposeJob constructor( context: Context, diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt b/android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt index 9b5fd745a..020ce8c4a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.model.job import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility -import com.twidere.twiderex.viewmodel.compose.ComposeType import com.twidere.twiderex.viewmodel.compose.VoteExpired import java.util.UUID diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt index 1694ade18..14fbdc607 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt @@ -25,12 +25,12 @@ import androidx.work.workDataOf import com.twidere.twiderex.extensions.getNullableBoolean import com.twidere.twiderex.extensions.getNullableDouble import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.job.DirectMessageDeleteData import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.viewmodel.compose.ComposeType import com.twidere.twiderex.viewmodel.compose.VoteExpired fun StatusResult.toWorkData() = workDataOf( diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt index 5a7161fd8..91c3319dc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.navigation import com.twidere.route.processor.AppRoute import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType @AppRoute interface Root { diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt index 80ffc2f47..a8e3e9301 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.navigation import com.twidere.route.processor.AppRoute import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType /** * if deeplink has the same parameters with route in Root.kt, diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt index bc2293903..972776418 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.unit.Constraints import com.twidere.twiderex.component.RequireAuthorization import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.scenes.DraftListScene import com.twidere.twiderex.scenes.HomeScene @@ -91,7 +92,6 @@ import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.utils.LocalPlatformResolver -import com.twidere.twiderex.viewmodel.compose.ComposeType import moe.tlaster.precompose.navigation.BackStackEntry import moe.tlaster.precompose.navigation.RouteBuilder import moe.tlaster.precompose.navigation.path diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d4ac9bf70..74d594780 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -125,6 +125,7 @@ import com.twidere.twiderex.extensions.stringName import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory @@ -133,7 +134,6 @@ import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.Orange import com.twidere.twiderex.ui.TwidereScene -import com.twidere.twiderex.viewmodel.compose.ComposeType import com.twidere.twiderex.viewmodel.compose.ComposeViewModel import com.twidere.twiderex.viewmodel.compose.DraftComposeViewModel import com.twidere.twiderex.viewmodel.compose.DraftItemViewModel diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 205f05b2f..7898a0bc3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -37,10 +37,10 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene -import com.twidere.twiderex.viewmodel.compose.ComposeType import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel class HomeTimelineItem : HomeNavigationItem() { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 19f593f9c..007b8c09b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -44,6 +44,7 @@ import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData @@ -68,13 +69,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import java.util.UUID -enum class ComposeType { - New, - Reply, - Quote, - Thread, -} - class DraftItemViewModel @AssistedInject constructor( private val repository: DraftRepository, @Assisted private val draftId: String, diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index 83b497750..29309efcb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -66,14 +66,7 @@ class UpdateStatusWorker @AssistedInject constructor( val retweeted = inputData.getNullableBoolean("retweeted") val retweetCount = inputData.getNullableLong("retweetCount") val likeCount = inputData.getNullableLong("likeCount") - repository.updateReaction(accountKey = accountKey, statusKey = statusKey) { - if (liked != null) { - it.liked = liked - } - if (retweeted != null) { - it.retweeted = retweeted - } - } + repository.updateReaction(accountKey = accountKey, statusKey = statusKey, liked = liked, retweeted = retweeted) statusRepository.updateStatus(statusKey = statusKey) { if (retweetCount != null) { it.retweetCount = retweetCount diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt index 94059ef21..9a532b9c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.dataprovider +import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.INotification import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.IUser @@ -27,6 +28,7 @@ import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser @@ -38,4 +40,6 @@ expect fun INotification.toUi(accountKey: MicroBlogKey): UiStatus expect fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus +expect fun IListModel.toUi(accountKey: MicroBlogKey): UiList + expect fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt index 59ef900a1..d15d4cfdb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt @@ -28,10 +28,7 @@ import com.twidere.twiderex.db.dao.NotificationCursorDao import com.twidere.twiderex.db.dao.PagingTimelineDao import com.twidere.twiderex.db.dao.ReactionDao import com.twidere.twiderex.db.dao.StatusDao -import com.twidere.twiderex.db.dao.StatusReferenceDao import com.twidere.twiderex.db.dao.TrendDao -import com.twidere.twiderex.db.dao.TrendHistoryDao -import com.twidere.twiderex.db.dao.UrlEntityDao import com.twidere.twiderex.db.dao.UserDao interface CacheDatabase : Database { @@ -40,12 +37,9 @@ interface CacheDatabase : Database { fun userDao(): UserDao fun reactionDao(): ReactionDao fun pagingTimelineDao(): PagingTimelineDao - fun urlEntityDao(): UrlEntityDao - fun statusReferenceDao(): StatusReferenceDao fun listsDao(): ListsDao fun notificationCursorDao(): NotificationCursorDao fun trendDao(): TrendDao - fun trendHistoryDao(): TrendHistoryDao fun directMessageConversationDao(): DirectMessageConversationDao fun directMessageDao(): DirectMessageEventDao } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt index 38064b907..e1f4867fe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt @@ -20,5 +20,14 @@ */ package com.twidere.twiderex.db.dao +import com.twidere.twiderex.model.ui.UiDraft +import kotlinx.coroutines.flow.Flow + // TODO OPERATION -interface DraftDao +interface DraftDao { + fun getAll(): Flow> + fun getDraftCount(): Flow + suspend fun insert(it: UiDraft) + suspend fun get(draftId: String): UiDraft? + suspend fun remove(draft: UiDraft) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt index 03cf9ba64..84bbbcdaa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt @@ -24,6 +24,7 @@ import androidx.paging.PagingSource import com.twidere.services.microblog.model.IListModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList +import kotlinx.coroutines.flow.Flow // TODO OPERATION interface ListsDao { @@ -33,4 +34,9 @@ interface ListsDao { fun saveLists(accountKey: MicroBlogKey, lists: List) fun getPagingSource(accountKey: MicroBlogKey): PagingSource + fun findWithListKeyWithFlow(listKey: MicroBlogKey, accountKey: MicroBlogKey): Flow + suspend fun insertAll(listOf: List) + suspend fun findWithListKey(listKey: MicroBlogKey, accountKey: MicroBlogKey): UiList? + suspend fun update(listOf: List) + suspend fun delete(listOf: List) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt index be0aa93b8..105b826c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt @@ -35,4 +35,5 @@ interface PagingTimelineDao { suspend fun getLatest(pagingKey: String, accountKey: MicroBlogKey): PagingTimeLineWithStatus? suspend fun findWithStatusKey(maxStatusKey: MicroBlogKey, accountKey: MicroBlogKey): PagingTimeLine? suspend fun insertAll(listOf: List) + suspend fun delete(statusKey: MicroBlogKey) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt index 032dcd125..1a396c280 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt @@ -20,4 +20,13 @@ */ package com.twidere.twiderex.db.dao -interface ReactionDao +import com.twidere.twiderex.model.MicroBlogKey + +interface ReactionDao { + suspend fun updateAction( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + liked: Boolean?, + retweet: Boolean? + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt index 2b0a7fe0d..568839239 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt @@ -20,7 +20,22 @@ */ package com.twidere.twiderex.db.dao -// TODO OPERATION +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import kotlinx.coroutines.flow.Flow + interface SearchDao { - fun clear() + suspend fun insertAll(search: List) + + fun getAll(accountKey: MicroBlogKey): Flow> + + fun getAllHistory(accountKey: MicroBlogKey): Flow> + + fun getAllSaved(accountKey: MicroBlogKey): Flow> + + suspend fun get(content: String, accountKey: MicroBlogKey): UiSearch? + + suspend fun remove(search: UiSearch) + + suspend fun clear() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 78cfa0cdf..75621dc1c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -22,9 +22,17 @@ package com.twidere.twiderex.db.dao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiStatus +import kotlinx.coroutines.flow.Flow // TODO OPERATION interface StatusDao { suspend fun findWithStatusKey(it: MicroBlogKey): UiStatus? suspend fun insertAll(it: List) + fun findWithStatusKeyWithReferenceFlow( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey + ): Flow + + suspend fun findWithStatusKeyWithReference(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? + suspend fun delete(statusKey: MicroBlogKey) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt index 5f6d73546..c19d6d47c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt @@ -22,9 +22,11 @@ package com.twidere.twiderex.db.dao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser +import kotlinx.coroutines.flow.Flow // TODO OPERATION interface UserDao { suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? suspend fun insertAll(listOf: List) + fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index 5d77206e1..c5f16aa6a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.di import com.twidere.twiderex.dataprovider.DataProvider @@ -8,4 +28,4 @@ import org.koin.dsl.module internal val repositoryModules = module { factory { MediaRepository(get().cacheDatabase.mediaDao()) } factory { CacheRepository(get()) } -} \ No newline at end of file +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt index 0431ce94a..c0a414e90 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.di import com.twidere.twiderex.dataprovider.DataProvider @@ -8,4 +28,4 @@ internal val twidereModules = module { single { get().appCacheHandler } single { get().cacheDatabase } single { get().appDatabase } -} \ No newline at end of file +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt index dea862cbd..368034859 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt @@ -28,9 +28,8 @@ import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.runBlocking -import javax.inject.Inject -class TwidereHttpConfigProvider @Inject constructor( +class TwidereHttpConfigProvider( private val miscPreferences: DataStore ) : HttpConfigProvider { override fun provideConfig(): HttpConfig { diff --git a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/Credentials.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt similarity index 87% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt index 8bdd77dff..ed367903f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt @@ -18,7 +18,11 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.model.enums -// TODO OPERATION -interface StatusReferenceDao +enum class ComposeType { + New, + Reply, + Quote, + Thread, +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt similarity index 67% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt index 9cc28ac4b..07273e605 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt @@ -18,7 +18,17 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.model.ui -// TODO OPERATION -interface UrlEntityDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType + +data class UiDraft( + val draftId: String, + val content: String, + val media: List, + val createdAt: Long, + val composeType: ComposeType, + val statusKey: MicroBlogKey?, + val excludedReplyUserIds: List?, +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt similarity index 78% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt index 4549562f3..8af0db8b9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt @@ -18,7 +18,13 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.model.ui -// TODO OPERATION -interface TrendHistoryDao +import com.twidere.twiderex.model.MicroBlogKey + +data class UiSearch( + val content: String, + val lastActive: Long, + val saved: Boolean, + val accountKey: MicroBlogKey +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index c48940257..5e8cb1269 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -22,15 +22,21 @@ package com.twidere.twiderex.paging.mediator.paging import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType +import androidx.paging.Pager +import androidx.paging.PagingData import androidx.paging.PagingState +import androidx.paging.map import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.dataprovider.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.model.paging.saveToDb +import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.IPagingList +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) abstract class PagingTimelineMediatorBase( @@ -116,3 +122,9 @@ abstract class PagingTimelineMediatorBase( paging: T? ): List } + +fun Pager.toUi(accountKey: MicroBlogKey): Flow> { + return flow.map { pagingData -> + pagingData.map { it.status } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt new file mode 100644 index 000000000..8029947d0 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.model.ui.UiUser + +expect class AccountUpdateRepository { + fun updateAccount(user: UiUser) + // accountRepository.findByAccountKey(user.userKey)?.let { + // accountRepository.getAccountDetails(it) + // }?.let { details -> + // user.let { + // details.user = it.toAmUser() + // accountRepository.updateAccount(details) + // } + // } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt index 1d634e9ff..450466257 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/DraftRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt @@ -21,13 +21,11 @@ package com.twidere.twiderex.repository import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.model.DbDraft import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.viewmodel.compose.ComposeType +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.model.ui.UiDraft import java.util.UUID -import javax.inject.Singleton -@Singleton class DraftRepository( private val database: AppDatabase ) { @@ -47,8 +45,8 @@ class DraftRepository( draftId: String = UUID.randomUUID().toString(), excludedReplyUserIds: List? = null, ) { - DbDraft( - _id = draftId, + UiDraft( + draftId = draftId, content = content, composeType = composeType, media = media, @@ -56,11 +54,11 @@ class DraftRepository( createdAt = System.currentTimeMillis(), excludedReplyUserIds = excludedReplyUserIds ).let { - database.draftDao().insertAll(it) + database.draftDao().insert(it) } } - suspend fun get(draftId: String): DbDraft? { + suspend fun get(draftId: String): UiDraft? { return database.draftDao().get(draftId) } @@ -70,7 +68,7 @@ class DraftRepository( } } - suspend fun remove(draft: DbDraft) { + suspend fun remove(draft: UiDraft) { database.draftDao().remove(draft) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt similarity index 71% rename from android/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt index 902cb62fd..1cabd4889 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/ListsRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt @@ -22,76 +22,72 @@ package com.twidere.twiderex.repository import androidx.paging.PagingData import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbList -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.paging.mediator.list.ListsMediator import com.twidere.twiderex.paging.mediator.list.ListsMediator.Companion.toUi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map class ListsRepository(private val database: CacheDatabase) { - fun findListWithListKey(account: AccountDetails, listKey: MicroBlogKey): Flow { - return database.listsDao().findWithListKeyWithFlow(listKey = listKey, accountKey = account.accountKey) - .map { - it?.toUi() - } + fun findListWithListKey(accountKey: MicroBlogKey, listKey: MicroBlogKey): Flow { + return database.listsDao().findWithListKeyWithFlow(listKey = listKey, accountKey = accountKey) } - fun fetchLists(account: AccountDetails): Flow> { + fun fetchLists(accountKey: MicroBlogKey, service: ListsService): Flow> { val mediator = ListsMediator( database = database, - accountKey = account.accountKey, - service = account.service as ListsService, + accountKey = accountKey, + service = service, ) return mediator.pager().toUi() } suspend fun createLists( - account: AccountDetails, + accountKey: MicroBlogKey, + service: ListsService, title: String, description: String? = null, mode: String? = null, replyPolicy: String? = null ): UiList { - val result = (account.service as ListsService).createList( + val result = service.createList( name = title, description = description, mode = mode, repliesPolicy = replyPolicy - ).toDbList(account.accountKey) + ).toUi(accountKey) // save to db database.listsDao().insertAll(listOf(result)) - return result.toUi() + return result } suspend fun updateLists( - account: AccountDetails, + accountKey: MicroBlogKey, + service: ListsService, listId: String, title: String? = null, description: String? = null, mode: String? = null, replyPolicy: String? = null ): UiList { - val result = (account.service as ListsService).updateList( + val result = service.updateList( listId = listId, name = title, description = description, mode = mode, repliesPolicy = replyPolicy - ).toDbList(account.accountKey) + ).toUi(accountKey) val originSource = database.listsDao().findWithListKey(result.listKey, result.accountKey) originSource?.let { database.listsDao().update( listOf( it.copy( title = result.title, - description = result.description, + descriptions = result.descriptions, mode = result.mode, isFollowed = result.isFollowed, replyPolicy = result.replyPolicy, @@ -100,45 +96,48 @@ class ListsRepository(private val database: CacheDatabase) { ) ) } - return result.toUi() + return result } suspend fun deleteLists( - account: AccountDetails, + accountKey: MicroBlogKey, + service: ListsService, listKey: MicroBlogKey, listId: String ): UiList? { - (account.service as ListsService).destroyList(listId) - val deleteItem = database.listsDao().findWithListKey(listKey, account.accountKey) + service.destroyList(listId) + val deleteItem = database.listsDao().findWithListKey(listKey, accountKey) deleteItem?.let { database.listsDao().delete(listOf(deleteItem)) } - return deleteItem?.toUi() + return deleteItem } suspend fun unsubscribeLists( - account: AccountDetails, + accountKey: MicroBlogKey, + service: ListsService, listKey: MicroBlogKey ): UiList? { - (account.service as ListsService).unsubscribeList(listId = listKey.id) - val updateItem = database.listsDao().findWithListKey(listKey, account.accountKey) + service.unsubscribeList(listId = listKey.id) + val updateItem = database.listsDao().findWithListKey(listKey, accountKey) updateItem?.let { database.listsDao().update(listOf(updateItem.copy(isFollowed = false))) } - return updateItem?.toUi() + return updateItem } suspend fun subscribeLists( - account: AccountDetails, + accountKey: MicroBlogKey, + service: ListsService, listKey: MicroBlogKey ): UiList { - val result = (account.service as ListsService) + val result = service .subscribeList(listId = listKey.id) - .toDbList(accountKey = account.accountKey) - val updateItem = database.listsDao().findWithListKey(listKey, account.accountKey) + .toUi(accountKey = accountKey) + val updateItem = database.listsDao().findWithListKey(listKey, accountKey) updateItem?.let { database.listsDao().update(listOf(updateItem.copy(isFollowed = true))) } ?: database.listsDao().insertAll(listOf(result)) - return result.toUi() + return result } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt similarity index 82% rename from android/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt index 576420ff9..f6e5f593a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt @@ -26,7 +26,7 @@ import androidx.paging.PagingConfig import androidx.paging.PagingData import com.twidere.services.microblog.ListsService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.MemoryCachePagingSource import com.twidere.twiderex.paging.crud.PagingMemoryCache @@ -37,7 +37,11 @@ import kotlinx.coroutines.flow.Flow class ListsUsersRepository(private val membersCaches: MutableMap> = mutableMapOf()) { @OptIn(ExperimentalPagingApi::class) - fun fetchMembers(account: AccountDetails, listId: String): Flow> { + fun fetchMembers( + accountKey: MicroBlogKey, + service: ListsService, + listId: String + ): Flow> { val cache = membersCaches[listId] ?: PagingMemoryCache() membersCaches[listId] = cache return Pager( @@ -47,8 +51,8 @@ class ListsUsersRepository(private val membersCaches: MutableMap> { + fun fetchSubscribers( + accountKey: MicroBlogKey, + service: ListsService, + listId: String + ): Flow> { return Pager( config = PagingConfig( pageSize = defaultLoadCount, @@ -64,19 +72,19 @@ class ListsUsersRepository(private val membersCaches: MutableMap Unit, + liked: Boolean? = null, + retweet: Boolean? = null, ) { - database.reactionDao().findWithStatusKey(statusKey, accountKey).let { - it ?: DbStatusReaction( - _id = UUID.randomUUID().toString(), - statusKey = statusKey, - accountKey = accountKey, - liked = false, - retweeted = false, - ) - }.let { - action.invoke(it) - database.reactionDao().insertAll(listOf(it)) - } + database.reactionDao().updateAction( + statusKey = statusKey, + accountKey = accountKey, + liked = liked, + retweet = retweet + ) + // database.reactionDao().findWithStatusKey(statusKey, accountKey).let { + // it ?: DbStatusReaction( + // _id = UUID.randomUUID().toString(), + // statusKey = statusKey, + // accountKey = accountKey, + // liked = false, + // retweeted = false, + // ) + // }.let { + // action.invoke(it) + // database.reactionDao().insertAll(listOf(it)) + // } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt index 8d79eb348..ca08d8b15 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/SearchRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt @@ -21,9 +21,8 @@ package com.twidere.twiderex.repository import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.model.DbSearch import com.twidere.twiderex.model.MicroBlogKey -import java.util.UUID +import com.twidere.twiderex.model.ui.UiSearch class SearchRepository( private val database: AppDatabase @@ -42,8 +41,7 @@ class SearchRepository( lastActive = System.currentTimeMillis(), saved = if (it.saved) it.saved else saved ) - } ?: DbSearch( - _id = UUID.randomUUID().toString(), + } ?: UiSearch( content = content, lastActive = System.currentTimeMillis(), saved = false, @@ -54,11 +52,11 @@ class SearchRepository( ) } - suspend fun remove(item: DbSearch) { + suspend fun remove(item: UiSearch) { database.searchDao().remove(item) } - suspend fun get(content: String, accountKey: MicroBlogKey): DbSearch? { + suspend fun get(content: String, accountKey: MicroBlogKey): UiSearch? { return database.searchDao().get(content, accountKey) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt similarity index 71% rename from android/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index c2db0bb16..972ddfb7d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -22,26 +22,21 @@ package com.twidere.twiderex.repository import androidx.paging.ExperimentalPagingApi import androidx.paging.PagingData -import androidx.room.withTransaction import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.LookupService +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.nitter.NitterService import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbStatusWithReference -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.saveToDb -import com.twidere.twiderex.extensions.toUi -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.pager +import com.twidere.twiderex.paging.mediator.paging.toUi import com.twidere.twiderex.paging.mediator.status.MastodonStatusContextMediator import com.twidere.twiderex.paging.mediator.status.TwitterConversationMediator import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map class StatusRepository( private val database: CacheDatabase, @@ -51,21 +46,16 @@ class StatusRepository( statusKey: MicroBlogKey, accountKey: MicroBlogKey ): Flow { - return database.statusDao().findWithStatusKeyWithReferenceFlow(statusKey).map { - it?.toUi(accountKey) - } + return database.statusDao().findWithStatusKeyWithReferenceFlow(statusKey, accountKey) } suspend fun loadFromCache(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? { - return database.statusDao().findWithStatusKeyWithReference(statusKey).let { - it?.toUi(accountKey) - } + return database.statusDao().findWithStatusKeyWithReference(statusKey, accountKey) } - suspend fun updateStatus(statusKey: MicroBlogKey, action: (DbStatusV2) -> Unit) { + suspend fun updateStatus(statusKey: MicroBlogKey, action: (UiStatus) -> UiStatus) { database.statusDao().findWithStatusKey(statusKey)?.let { - action.invoke(it) - database.statusDao().insertAll(listOf(it)) + database.statusDao().insertAll(listOf(action.invoke(it))) } } @@ -73,7 +63,8 @@ class StatusRepository( database.withTransaction { database.statusDao().delete(statusKey) database.pagingTimelineDao().delete(statusKey) - database.statusReferenceDao().delete(statusKey) + // TODO hide by implementation of statusDao + // database.statusReferenceDao().delete(statusKey) } } @@ -82,34 +73,38 @@ class StatusRepository( accountKey: MicroBlogKey, lookupService: LookupService ) { - listOf( - lookupService.lookupStatus(id).toDbStatusWithReference(accountKey = accountKey) - ).saveToDb(database) + database.statusDao().insertAll( + listOf( + lookupService.lookupStatus(id).toUi(accountKey = accountKey) + ) + ) } @OptIn(ExperimentalPagingApi::class) fun conversation( statusKey: MicroBlogKey, - account: AccountDetails, + platformType: PlatformType, + service: MicroBlogService, + accountKey: MicroBlogKey ): Flow> { // TODO: remove usage of `when` - val remoteMediator = when (account.type) { + val remoteMediator = when (platformType) { PlatformType.Twitter -> TwitterConversationMediator( - service = account.service as TwitterService, + service = service as TwitterService, nitterService = nitterService, statusKey = statusKey, - accountKey = account.accountKey, + accountKey = accountKey, database = database, ) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> MastodonStatusContextMediator( - service = account.service as MastodonService, + service = service as MastodonService, statusKey = statusKey, - accountKey = account.accountKey, + accountKey = accountKey, database = database, ) } - return remoteMediator.pager().toUi(accountKey = account.accountKey) + return remoteMediator.pager().toUi(accountKey = accountKey) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt similarity index 72% rename from android/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt index f36a03f3a..97fd18483 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt @@ -25,12 +25,12 @@ import androidx.paging.PagingData import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.toUi -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.list.ListsTimelineMediator import com.twidere.twiderex.paging.mediator.paging.pager +import com.twidere.twiderex.paging.mediator.paging.toUi import com.twidere.twiderex.paging.mediator.timeline.MastodonHashtagTimelineMediator import com.twidere.twiderex.paging.mediator.user.UserFavouriteMediator import com.twidere.twiderex.paging.mediator.user.UserStatusMediator @@ -42,54 +42,59 @@ class TimelineRepository( ) { fun favouriteTimeline( userKey: MicroBlogKey, - account: AccountDetails, + accountKey: MicroBlogKey, + platformType: PlatformType, + service: TimelineService, ): Flow> { val mediator = UserFavouriteMediator( userKey = userKey, - platformType = account.type, + platformType = platformType, database = database, - accountKey = account.accountKey, - service = account.service as TimelineService, + accountKey = accountKey, + service = service, ) - return mediator.pager().toUi(accountKey = account.accountKey) + return mediator.pager().toUi(accountKey = accountKey) } fun userTimeline( userKey: MicroBlogKey, - account: AccountDetails, + accountKey: MicroBlogKey, + service: TimelineService, exclude_replies: Boolean, ): Flow> { return UserStatusMediator( userKey = userKey, database = database, - accountKey = account.accountKey, - service = account.service as TimelineService, + accountKey = accountKey, + service = service, exclude_replies = exclude_replies, - ).pager().toUi(accountKey = account.accountKey) + ).pager().toUi(accountKey = accountKey) } fun listTimeline( - account: AccountDetails, + accountKey: MicroBlogKey, + service: TimelineService, listKey: MicroBlogKey, ): Flow> { return ListsTimelineMediator( - accountKey = account.accountKey, + accountKey = accountKey, database = database, listKey = listKey, - service = account.service as TimelineService - ).pager().toUi(accountKey = account.accountKey) + service = service + ).pager().toUi(accountKey = accountKey) } fun mastodonHashtagTimeline( keyword: String, - account: AccountDetails + accountKey: MicroBlogKey, + service: MastodonService, ): Flow> { val mediator = MastodonHashtagTimelineMediator( keyword = keyword, - service = account.service as MastodonService, - accountKey = account.accountKey, + service = service, + accountKey = accountKey, database = database ) - return mediator.pager().toUi(accountKey = account.accountKey) + return mediator.pager().toUi(accountKey = accountKey) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt index fe0f0f09c..83edd8a5e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/TrendRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.repository import androidx.paging.PagingData import com.twidere.services.microblog.TrendService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiTrend import com.twidere.twiderex.paging.mediator.trend.TrendMediator import com.twidere.twiderex.paging.mediator.trend.TrendMediator.Companion.toUi @@ -33,13 +33,14 @@ class TrendRepository(private val database: CacheDatabase) { private val worldWideId = "1" fun trendsSource( - account: AccountDetails, + accountKey: MicroBlogKey, + service: TrendService, locationId: String = worldWideId ): Flow> { return TrendMediator( database = database, - service = account.service as TrendService, - accountKey = account.accountKey, + service = service, + accountKey = accountKey, locationId = locationId ).pager().toUi() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt similarity index 65% rename from android/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt index 246a218f4..9b0540519 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/UserRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt @@ -21,51 +21,38 @@ package com.twidere.twiderex.repository import com.twidere.services.microblog.LookupService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toAmUser -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map class UserRepository( private val database: CacheDatabase, - private val accountRepository: AccountRepository, + private val accountRepository: AccountUpdateRepository, ) { suspend fun lookupUserByName(name: String, accountKey: MicroBlogKey, lookupService: LookupService): UiUser { - val user = lookupService.lookupUserByName(name).toDbUser(accountKey) - saveUser(user) - return user.toUi() + return lookupService.lookupUserByName(name).toUi(accountKey).also { + saveUser(it) + } } suspend fun lookupUserById(id: String, accountKey: MicroBlogKey, lookupService: LookupService): UiUser { - val user = lookupService.lookupUser(id).toDbUser(accountKey) + val user = lookupService.lookupUser(id).toUi(accountKey) saveUser(user) - return user.toUi() + return user } suspend fun lookupUsersByName(name: List, accountKey: MicroBlogKey, lookupService: LookupService): List { - return lookupService.lookupUsersByName(name = name).map { it.toDbUser(accountKey).toUi() } + return lookupService.lookupUsersByName(name = name).map { it.toUi(accountKey) } } fun getUserFlow(userKey: MicroBlogKey): Flow { - return database.userDao().findWithUserKeyFlow(userKey = userKey).map { - it?.toUi() - } + return database.userDao().findWithUserKeyFlow(userKey = userKey) } - private suspend fun saveUser(user: DbUser) { + private suspend fun saveUser(user: UiUser) { database.userDao().insertAll(listOf(user)) - accountRepository.findByAccountKey(user.userKey)?.let { - accountRepository.getAccountDetails(it) - }?.let { details -> - user.let { - details.user = it.toAmUser() - accountRepository.updateAccount(details) - } - } + accountRepository.updateAccount(user) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt new file mode 100644 index 000000000..3539babb5 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils + +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +private val JSON by lazy { + Json { + ignoreUnknownKeys = true + isLenient = true + coerceInputValues = true + } +} + +internal inline fun T.json(): String = + JSON.encodeToString(this) + +internal inline fun String.fromJson() = + JSON.decodeFromString(this) From e1a0fcc3d4d8a0e6783101b059119a1b4b24ef0d Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 11 Aug 2021 15:22:28 +0800 Subject: [PATCH 047/615] clean code in daos --- .../com/twidere/twiderex/dataprovider/DataTransform.kt | 4 ++++ .../kotlin/com/twidere/twiderex/db/dao/ListsDao.kt | 7 +------ .../kotlin/com/twidere/twiderex/db/dao/TrendDao.kt | 5 ++--- .../kotlin/com/twidere/twiderex/db/dao/UserDao.kt | 1 - .../twidere/twiderex/paging/mediator/list/ListsMediator.kt | 5 +++-- .../twiderex/paging/mediator/trend/TrendMediator.kt | 6 +++++- .../com/twidere/twiderex/repository/UserRepository.kt | 6 +++--- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt index 9a532b9c3..9381f1f24 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.dataprovider import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.INotification import com.twidere.services.microblog.model.IStatus +import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.twiderex.model.MicroBlogKey @@ -30,6 +31,7 @@ import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiTrend import com.twidere.twiderex.model.ui.UiUser expect fun IUser.toUi(accountKey: MicroBlogKey): UiUser @@ -42,4 +44,6 @@ expect fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String) expect fun IListModel.toUi(accountKey: MicroBlogKey): UiList +expect fun ITrend.toUi(accountKey: MicroBlogKey): UiTrend + expect fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt index 84bbbcdaa..4643d011c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt @@ -21,22 +21,17 @@ package com.twidere.twiderex.db.dao import androidx.paging.PagingSource -import com.twidere.services.microblog.model.IListModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList import kotlinx.coroutines.flow.Flow // TODO OPERATION interface ListsDao { - // Todo implement - // database.listsDao().clearAll(accountKey) - // database.listsDao().insertAll(lists) - fun saveLists(accountKey: MicroBlogKey, lists: List) - fun getPagingSource(accountKey: MicroBlogKey): PagingSource fun findWithListKeyWithFlow(listKey: MicroBlogKey, accountKey: MicroBlogKey): Flow suspend fun insertAll(listOf: List) suspend fun findWithListKey(listKey: MicroBlogKey, accountKey: MicroBlogKey): UiList? suspend fun update(listOf: List) suspend fun delete(listOf: List) + suspend fun clearAll(accountKey: MicroBlogKey) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt index bf8a83844..7e056a586 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt @@ -21,13 +21,12 @@ package com.twidere.twiderex.db.dao import androidx.paging.PagingSource -import com.twidere.services.microblog.model.ITrend import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiTrend // TODO OPERATION interface TrendDao { - // Todo clear/add , just like listDao - fun saveTrend(accountKey: MicroBlogKey, trends: List) + fun insertAll(trends: List) fun getPagingSource(accountKey: MicroBlogKey): PagingSource + suspend fun clear() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt index c19d6d47c..0608eacf4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt @@ -24,7 +24,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import kotlinx.coroutines.flow.Flow -// TODO OPERATION interface UserDao { suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? suspend fun insertAll(listOf: List) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt index 035382f71..3faf4862d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt @@ -29,6 +29,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey @@ -45,8 +46,8 @@ class ListsMediator( override suspend fun load(loadType: LoadType, state: PagingState): MediatorResult { return try { if (loadType == LoadType.REFRESH) { - val lists = service.lists() - database.listsDao().saveLists(accountKey = accountKey, lists = lists) + val lists = service.lists().map { it.toUi(accountKey) } + database.listsDao().insertAll(lists) } MediatorResult.Success(endOfPaginationReached = true) } catch (e: Throwable) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt index 99fa9a586..05844482f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt @@ -29,6 +29,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator import com.twidere.services.microblog.TrendService +import com.twidere.twiderex.dataprovider.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey @@ -50,7 +51,10 @@ class TrendMediator( return try { if (loadType == LoadType.REFRESH) { val lists = service.trends(locationId) - database.trendDao().saveTrend(accountKey = accountKey, trends = lists) + database.withTransaction { + database.trendDao().clear() + database.trendDao().insertAll(lists.map { it.toUi(accountKey) }) + } } MediatorResult.Success(endOfPaginationReached = true) } catch (e: Throwable) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt index 9b0540519..f03380c11 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt @@ -38,9 +38,9 @@ class UserRepository( } suspend fun lookupUserById(id: String, accountKey: MicroBlogKey, lookupService: LookupService): UiUser { - val user = lookupService.lookupUser(id).toUi(accountKey) - saveUser(user) - return user + return lookupService.lookupUser(id).toUi(accountKey).also { + saveUser(it) + } } suspend fun lookupUsersByName(name: List, accountKey: MicroBlogKey, lookupService: LookupService): List { From d4e8230a62897145912f0eed55604351549961df Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 11 Aug 2021 18:02:21 +0800 Subject: [PATCH 048/615] migrate model mapper to commonMain --- .../dm/DirectMessageRepositoryTest.kt | 1 - .../transform/AccountTransform.kt | 2 +- .../transform/DmConversationTransform.kt | 2 +- .../{model => db}/transform/EmojiTransform.kt | 2 +- .../{model => db}/transform/ListTransform.kt | 2 +- .../{model => db}/transform/MediaTransform.kt | 3 +- .../transform/StatusTransform.kt | 2 +- .../{model => db}/transform/TrendTransform.kt | 2 +- .../transform/UrlEntityTransform.kt | 2 +- .../{model => db}/transform/UserTransform.kt | 2 +- .../transform/WorkDataTransform.kt | 2 +- .../twiderex/extensions/PagingExtensions.kt | 2 +- .../jobs/compose/MastodonComposeJob.kt | 2 - .../jobs/compose/TwitterComposeJob.kt | 2 - .../twiderex/jobs/status/LikeStatusJob.kt | 1 - .../twiderex/jobs/status/RetweetStatusJob.kt | 1 - .../jobs/status/UnRetweetStatusJob.kt | 1 - .../twiderex/jobs/status/UnlikeStatusJob.kt | 1 - .../twiderex/repository/AccountRepository.kt | 4 +- .../twiderex/utils/MastodonEmojiCache.kt | 2 +- .../mastodon/MastodonSignInViewModel.kt | 1 - .../viewmodel/search/SearchTweetsViewModel.kt | 1 - .../twitter/TwitterSignInViewModel.kt | 1 - .../search/TwitterSearchMediaViewModel.kt | 1 - .../user/UserMediaTimelineViewModel.kt | 1 - .../twiderex/worker/compose/ComposeWorker.kt | 2 +- .../worker/compose/MastodonComposeWorker.kt | 2 +- .../worker/compose/TwitterComposeWorker.kt | 2 +- .../worker/dm/DirectMessageDeleteWorker.kt | 4 +- .../worker/dm/DirectMessageSendWorker.kt | 2 +- .../dm/TwitterDirectMessageSendWorker.kt | 2 +- .../twiderex/worker/draft/SaveDraftWorker.kt | 4 +- .../twiderex/worker/status/StatusWorker.kt | 2 +- .../worker/status/UpdateStatusWorker.kt | 2 +- .../repository/ListsUsersRepositoryTest.kt | 1 - common/build.gradle.kts | 2 + .../twiderex/dataprovider/DataTransform.kt | 49 -- .../dataprovider/mapper/DataMapper.kt | 99 ++-- .../twiderex/dataprovider}/mapper/Mastodon.kt | 337 +++++++------ .../twiderex/dataprovider}/mapper/Twitter.kt | 467 +++++++----------- .../twiderex/model/paging/PagingTimeLine.kt | 1 - .../com/twidere/twiderex/model/ui/UiMedia.kt | 2 + .../paging/mediator/list/ListsMediator.kt | 2 +- .../mediator/list/ListsUserPagingMediator.kt | 2 +- .../paging/PagingTimelineMediatorBase.kt | 3 +- .../mediator/paging/PagingWithGapMediator.kt | 2 +- .../paging/mediator/trend/TrendMediator.kt | 2 +- .../paging/source/FollowersPagingSource.kt | 2 +- .../paging/source/FollowingPagingSource.kt | 2 +- .../paging/source/SearchUserPagingSource.kt | 2 +- .../paging/source/UserPagingSource.kt | 2 +- .../repository/DirectMessageRepository.kt | 2 +- .../twiderex/repository/ListsRepository.kt | 2 +- .../repository/NotificationRepository.kt | 2 +- .../twiderex/repository/StatusRepository.kt | 2 +- .../twiderex/repository/UserRepository.kt | 2 +- .../twidere/twiderex/mock/model/MockModels.kt | 3 +- 57 files changed, 450 insertions(+), 605 deletions(-) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/AccountTransform.kt (95%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/DmConversationTransform.kt (98%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/EmojiTransform.kt (96%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/ListTransform.kt (96%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/MediaTransform.kt (94%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/StatusTransform.kt (99%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/TrendTransform.kt (96%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/UrlEntityTransform.kt (95%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/UserTransform.kt (98%) rename android/src/main/kotlin/com/twidere/twiderex/{model => db}/transform/WorkDataTransform.kt (99%) delete mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt rename android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt => common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt (58%) rename {android/src/main/kotlin/com/twidere/twiderex/db => common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider}/mapper/Mastodon.kt (60%) rename {android/src/main/kotlin/com/twidere/twiderex/db => common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider}/mapper/Twitter.kt (57%) diff --git a/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt b/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt index 8fb3d77a7..a360798ad 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt @@ -31,7 +31,6 @@ import com.twidere.twiderex.mock.MockDirectMessageService import com.twidere.twiderex.mock.MockLookUpService import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.runBlocking import org.junit.After diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/AccountTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/AccountTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt index 8977eef5e..b80e5a337 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/AccountTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import android.accounts.Account import com.twidere.twiderex.model.TwidereAccount diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt index 65b3844c8..7b1490125 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/DmConversationTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbDMConversation import com.twidere.twiderex.db.model.DbDMEvent diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/EmojiTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/EmojiTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt index c886ebcf1..055a25046 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/EmojiTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.services.mastodon.model.Emoji import com.twidere.twiderex.model.ui.UiEmoji diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/ListTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/ListTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt index 7ff1f131f..475c8bd01 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/ListTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbList import com.twidere.twiderex.model.ui.UiList diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt index 95b655228..60ec6624b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/MediaTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbMedia import com.twidere.twiderex.model.ui.UiMedia @@ -34,5 +34,6 @@ fun List.toUi() = sortedBy { it.order }.map { height = it.height, pageUrl = it.pageUrl, altText = it.altText, + order = it.order, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt similarity index 99% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt index e1cfc0691..b88852a07 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.services.mastodon.model.Mention import com.twidere.services.mastodon.model.Poll diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/TrendTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/TrendTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt index 485c4ad42..b3880e5da 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/TrendTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbTrendHistory import com.twidere.twiderex.db.model.DbTrendWithHistory diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/UrlEntityTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/UrlEntityTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt index 3850f8324..74b07733d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/UrlEntityTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbUrlEntity import com.twidere.twiderex.model.ui.UiUrlEntity diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt index 760b6e5a8..6501b8597 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import com.twidere.twiderex.db.model.DbMastodonUserExtra import com.twidere.twiderex.db.model.DbTwitterUserExtra diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt similarity index 99% rename from android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt index 14fbdc607..89a5d929f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/transform/WorkDataTransform.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.model.transform +package com.twidere.twiderex.db.transform import androidx.work.Data import androidx.work.workDataOf diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt index ec14e0e8a..fb8988852 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt @@ -23,8 +23,8 @@ package com.twidere.twiderex.extensions import androidx.paging.Pager import androidx.paging.map import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus +import com.twidere.twiderex.db.transform.toUi import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import kotlinx.coroutines.flow.map fun Pager.toUi(accountKey: MicroBlogKey) = diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index e7be78ff7..ff33403cd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -27,7 +27,6 @@ import com.twidere.services.mastodon.model.PostStatus import com.twidere.services.mastodon.model.Visibility import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.db.mapper.toDbStatusWithReference -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator @@ -35,7 +34,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.job.ComposeData -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 5f43c5a01..7d867c5d0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -24,14 +24,12 @@ import android.content.Context import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.db.mapper.toDbStatusWithReference -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt index 0adb1dfa4..f5336cff9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt @@ -24,7 +24,6 @@ import com.twidere.services.microblog.StatusService import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt index fcc25f2dc..2d2a0480e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt @@ -24,7 +24,6 @@ import com.twidere.services.microblog.StatusService import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt index d8f5e35e1..7585633df 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt @@ -24,7 +24,6 @@ import com.twidere.services.microblog.StatusService import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt index 18835bd3e..4e53ca55d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt @@ -24,7 +24,6 @@ import com.twidere.services.microblog.StatusService import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 65f41a2e1..234ef04f4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.repository import android.accounts.Account import android.accounts.AccountManager import android.os.Build +import com.twidere.twiderex.db.transform.toAndroid +import com.twidere.twiderex.db.transform.toTwidere import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toAndroid -import com.twidere.twiderex.model.transform.toTwidere import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import kotlinx.coroutines.flow.MutableStateFlow diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt index 32e13af4e..5e12660e8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.utils import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.db.transform.toUi import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiEmojiCategory import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index f087a6657..af3b47656 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -31,7 +31,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.cred.OAuth2Credentials import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toAmUser import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.ACCOUNT_TYPE diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index d21019bfb..b1051b9c3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -27,7 +27,6 @@ import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 9b061b024..fe693921e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -33,7 +33,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.cred.OAuthCredentials import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.transform.toAmUser import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.ACCOUNT_TYPE diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 31913339d..e399d99d5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -28,7 +28,6 @@ import androidx.paging.map import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 38f68d0bb..cc90fb8eb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -31,7 +31,6 @@ import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.PagingMediator diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt index 87e2df8ac..7f450b3e8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt @@ -24,9 +24,9 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.db.transform.toComposeData import com.twidere.twiderex.jobs.compose.ComposeJob import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toComposeData abstract class ComposeWorker( protected val context: Context, diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt index 121c47144..dc03ba136 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt @@ -26,10 +26,10 @@ import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.compose.MastodonComposeJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.ComposeData -import com.twidere.twiderex.model.transform.toWorkData import dagger.assisted.Assisted import dagger.assisted.AssistedInject diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt index 7672c6e42..a1f94bda2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt @@ -26,10 +26,10 @@ import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.compose.TwitterComposeJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.ComposeData -import com.twidere.twiderex.model.transform.toWorkData import dagger.assisted.Assisted import dagger.assisted.AssistedInject diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt index 6871ce915..f70c7cabe 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt @@ -25,11 +25,11 @@ import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters +import com.twidere.twiderex.db.transform.toDirectMessageDeleteData +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.dm.DirectMessageDeleteJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.DirectMessageDeleteData -import com.twidere.twiderex.model.transform.toDirectMessageDeleteData -import com.twidere.twiderex.model.transform.toWorkData import dagger.assisted.Assisted import dagger.assisted.AssistedInject diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt index 81f88e3ed..598d1fc6a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt @@ -23,9 +23,9 @@ package com.twidere.twiderex.worker.dm import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters +import com.twidere.twiderex.db.transform.toDirectMessageSendData import com.twidere.twiderex.jobs.dm.DirectMessageSendJob import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toDirectMessageSendData abstract class DirectMessageSendWorker( context: Context, diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt index b8f72dd07..c23656cce 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt @@ -25,9 +25,9 @@ import androidx.hilt.work.HiltWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.dm.TwitterDirectMessageSendJob import com.twidere.twiderex.model.job.DirectMessageSendData -import com.twidere.twiderex.model.transform.toWorkData import dagger.assisted.Assisted import dagger.assisted.AssistedInject diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt index 3903e5fcc..84851b876 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt @@ -25,10 +25,10 @@ import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters +import com.twidere.twiderex.db.transform.toComposeData +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.draft.SaveDraftJob import com.twidere.twiderex.model.job.ComposeData -import com.twidere.twiderex.model.transform.toComposeData -import com.twidere.twiderex.model.transform.toWorkData import dagger.assisted.Assisted import dagger.assisted.AssistedInject diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt index 457e4d059..987e43370 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt @@ -25,9 +25,9 @@ import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import androidx.work.workDataOf +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.status.StatusJob import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toWorkData import com.twidere.twiderex.model.ui.UiStatus abstract class StatusWorker( diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index 29309efcb..13403f537 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -27,11 +27,11 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OverwritingInputMerger import androidx.work.WorkerParameters import androidx.work.setInputMerger +import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.extensions.getNullableBoolean import com.twidere.twiderex.extensions.getNullableLong import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.model.transform.toWorkData import com.twidere.twiderex.repository.ReactionRepository import com.twidere.twiderex.repository.StatusRepository import dagger.assisted.Assisted diff --git a/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt index dc345f02a..d8fcb05e3 100644 --- a/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt +++ b/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt @@ -25,7 +25,6 @@ import com.twidere.twiderex.db.mapper.toDbUser import com.twidere.twiderex.mock.MockCenter import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.PagingMemoryCache import kotlinx.coroutines.runBlocking diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 715662b56..73dc4875e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -37,6 +37,8 @@ kotlin { api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}") api("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${Versions.Kotlin.serialization}") api("io.insert-koin:koin-core:${Versions.koin}") + implementation("com.twitter.twittertext:twitter-text:3.1.0") + implementation("org.jsoup:jsoup:1.13.1") } } val commonTest by getting { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt deleted file mode 100644 index 9381f1f24..000000000 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.dataprovider - -import com.twidere.services.microblog.model.IListModel -import com.twidere.services.microblog.model.INotification -import com.twidere.services.microblog.model.IStatus -import com.twidere.services.microblog.model.ITrend -import com.twidere.services.microblog.model.IUser -import com.twidere.services.twitter.model.DirectMessageEvent -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import com.twidere.twiderex.model.ui.UiDMEvent -import com.twidere.twiderex.model.ui.UiList -import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.model.ui.UiTrend -import com.twidere.twiderex.model.ui.UiUser - -expect fun IUser.toUi(accountKey: MicroBlogKey): UiUser - -expect fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus - -expect fun INotification.toUi(accountKey: MicroBlogKey): UiStatus - -expect fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus - -expect fun IListModel.toUi(accountKey: MicroBlogKey): UiList - -expect fun ITrend.toUi(accountKey: MicroBlogKey): UiTrend - -expect fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt similarity index 58% rename from android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt index 70ea5ece5..ae129d237 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/IStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt @@ -18,8 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.mapper +package com.twidere.twiderex.dataprovider.mapper +import com.twidere.services.mastodon.model.Emoji import com.twidere.services.mastodon.model.MastodonList import com.twidere.services.microblog.model.IDirectMessage import com.twidere.services.microblog.model.IListModel @@ -28,9 +29,10 @@ import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.TwitterList -import com.twidere.twiderex.db.model.DbStatusWithReference -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiEmoji +import com.twidere.twiderex.model.ui.UiEmojiCategory +import com.twidere.twiderex.model.ui.UiUser private typealias TwitterUser = com.twidere.services.twitter.model.User private typealias TwitterUserV2 = com.twidere.services.twitter.model.UserV2 @@ -42,78 +44,83 @@ private typealias MastodonNotification = com.twidere.services.mastodon.model.Not private typealias MastodonUser = com.twidere.services.mastodon.model.Account private typealias MastodonTrend = com.twidere.services.mastodon.model.Trend -fun IStatus.toDbPagingTimeline( - accountKey: MicroBlogKey, - pagingKey: String, -) = when (this) { - is TwitterStatus -> this.toDbPagingTimeline( +fun IUser.toUi(accountKey: MicroBlogKey) = when (this) { + is TwitterUser -> this.toUiUser() + is TwitterUserV2 -> this.toUiUser() + is MastodonUser -> this.toUiUser( + accountKey = accountKey + ) + else -> throw NotImplementedError() +} + +fun IStatus.toUi(accountKey: MicroBlogKey, isGap: Boolean = false) = when (this) { + is TwitterStatus -> this.toUiStatus( accountKey = accountKey, - pagingKey = pagingKey, + isGap = isGap, ) - is TwitterStatusV2 -> this.toDbPagingTimeline( + is TwitterStatusV2 -> this.toUiStatus( accountKey = accountKey, - pagingKey = pagingKey, + isGap = isGap, ) - is MastodonStatus -> this.toDbPagingTimeline( + is MastodonStatus -> this.toUiStatus( accountKey = accountKey, - pagingKey = pagingKey, + isGap = isGap ) - is MastodonNotification -> this.toDbPagingTimeline( + is MastodonNotification -> this.toUiStatus( accountKey = accountKey, - pagingKey = pagingKey, + isGap = isGap ) else -> throw NotImplementedError() } -fun IStatus.toDbStatusWithReference( - accountKey: MicroBlogKey, -): DbStatusWithReference = when (this) { - is TwitterStatus -> this.toDbStatusWithReference( +fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String) = when (this) { + is TwitterStatus -> this.toPagingTimeline( accountKey = accountKey, + pagingKey = pagingKey, ) - is TwitterStatusV2 -> this.toDbStatusWithReference( + is TwitterStatusV2 -> this.toPagingTimeline( accountKey = accountKey, + pagingKey = pagingKey, ) - is MastodonStatus -> this.toDbStatusWithReference( + is MastodonStatus -> this.toPagingTimeline( accountKey = accountKey, + pagingKey = pagingKey, ) - is MastodonNotification -> this.toDbStatusWithReference( + is MastodonNotification -> this.toPagingTimeline( accountKey = accountKey, + pagingKey = pagingKey, ) else -> throw NotImplementedError() } -fun IUser.toDbUser( - accountKey: MicroBlogKey -) = when (this) { - is TwitterUser -> this.toDbUser() - is TwitterUserV2 -> this.toDbUser() - is MastodonUser -> this.toDbUser( - accountKey = accountKey - ) +fun IListModel.toUi(accountKey: MicroBlogKey) = when (this) { + is TwitterList -> this.toUiList(accountKey) + is MastodonList -> this.toUiList(accountKey) else -> throw NotImplementedError() } -fun IListModel.toDbList( - accountKey: MicroBlogKey -) = when (this) { - is TwitterList -> this.toDbList(accountKey) - is MastodonList -> this.toDbList(accountKey) +fun ITrend.toUi(accountKey: MicroBlogKey) = when (this) { + is TwitterTrend -> this.toUiTrend() + is MastodonTrend -> this.toUiTrend(accountKey) else -> throw NotImplementedError() } -fun ITrend.toDbTrend( - accountKey: MicroBlogKey -) = when (this) { - is TwitterTrend -> this.toDbTrend(accountKey) - is MastodonTrend -> this.toDbTrend(accountKey) +fun IDirectMessage.toUi(accountKey: MicroBlogKey, sender: UiUser) = when (this) { + is DirectMessageEvent -> this.toUiDMEvent(accountKey, sender) else -> throw NotImplementedError() } -fun IDirectMessage.toDbDirectMessage( - accountKey: MicroBlogKey, - sender: DbUser -) = when (this) { - is DirectMessageEvent -> this.toDbDirectMessage(accountKey, sender) - else -> throw NotImplementedError() +fun List.toUi(): List = groupBy({ it.category }, { it }).map { + UiEmojiCategory( + if (it.key.isNullOrEmpty()) null else it.key, + it.value.map { emoji -> + UiEmoji( + shortcode = emoji.shortcode, + url = emoji.url, + staticURL = emoji.staticURL, + visibleInPicker = emoji.visibleInPicker, + category = emoji.category + ) + } + ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt similarity index 60% rename from android/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index 0ee6fe2a7..e105fc7a1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.mapper +package com.twidere.twiderex.dataprovider.mapper import com.twidere.services.mastodon.model.Account import com.twidere.services.mastodon.model.Emoji @@ -29,109 +29,104 @@ import com.twidere.services.mastodon.model.NotificationTypes import com.twidere.services.mastodon.model.Status import com.twidere.services.mastodon.model.Trend import com.twidere.services.mastodon.model.Visibility -import com.twidere.twiderex.db.model.DbList -import com.twidere.twiderex.db.model.DbMastodonStatusExtra -import com.twidere.twiderex.db.model.DbMastodonUserExtra -import com.twidere.twiderex.db.model.DbMedia -import com.twidere.twiderex.db.model.DbPagingTimeline -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.DbPreviewCard -import com.twidere.twiderex.db.model.DbStatusReaction -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.DbStatusWithMediaAndUser -import com.twidere.twiderex.db.model.DbStatusWithReference -import com.twidere.twiderex.db.model.DbTrend -import com.twidere.twiderex.db.model.DbTrendHistory -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.db.model.DbUser -import com.twidere.twiderex.db.model.toDbStatusReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.Option +import com.twidere.twiderex.model.ui.StatusMetrics +import com.twidere.twiderex.model.ui.UiCard +import com.twidere.twiderex.model.ui.UiGeo +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.model.ui.UiPoll +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.model.ui.UiTrendHistory +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.model.ui.UserMetrics +import com.twidere.twiderex.model.ui.mastodon.Field +import com.twidere.twiderex.model.ui.mastodon.MastodonMention +import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra +import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra import com.twidere.twiderex.navigation.RootDeepLinksRoute -import com.twidere.twiderex.utils.json import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node -import java.util.UUID import java.util.regex.Pattern -fun Notification.toDbPagingTimeline( +fun Notification.toPagingTimeline( accountKey: MicroBlogKey, pagingKey: String, -): DbPagingTimelineWithStatus { - val status = this.toDbStatusWithReference(accountKey = accountKey) - return DbPagingTimelineWithStatus( - timeline = DbPagingTimeline( - _id = UUID.randomUUID().toString(), +): PagingTimeLineWithStatus { + val status = this.toUiStatus(accountKey = accountKey) + return PagingTimeLineWithStatus( + timeline = PagingTimeLine( accountKey = accountKey, timestamp = createdAt?.time ?: 0, isGap = false, - statusKey = status.status.data.statusKey, + statusKey = status.statusKey, pagingKey = pagingKey, - sortId = status.status.data.timestamp + sortId = status.timestamp ), status = status, ) } -fun Notification.toDbStatusWithReference( +fun Notification.toUiStatus( accountKey: MicroBlogKey, -): DbStatusWithReference { - val user = this.account?.toDbUser(accountKey = accountKey) + isGap: Boolean = false +): UiStatus { + val user = this.account?.toUiUser(accountKey = accountKey) ?: throw IllegalArgumentException("mastodon Notification.user should not be null") - val relatedStatus = this.status?.toDbStatusWithMediaAndUser(accountKey = accountKey) - val status = DbStatusV2( - _id = UUID.randomUUID().toString(), + val relatedStatus = this.status?.toUiStatus(accountKey = accountKey) + val statusKey = accountKey.copy( + id = id + ?: throw IllegalArgumentException("mastodon Notification.id should not be null"), + ) + return UiStatus( statusId = id ?: throw IllegalArgumentException("mastodon Notification.id should not be null"), - statusKey = accountKey.copy( - id = id - ?: throw IllegalArgumentException("mastodon Notification.id should not be null"), - ), + statusKey = statusKey, htmlText = "", rawText = "", timestamp = this.createdAt?.time ?: 0, - retweetCount = 0, - likeCount = 0, - replyCount = 0, - placeString = null, + metrics = StatusMetrics( + retweet = 0, + like = 0, + reply = 0 + ), + geo = UiGeo( + name = "" + ), source = "", hasMedia = false, - userKey = user.userKey, - lang = null, - is_possibly_sensitive = false, + user = user, + sensitive = false, platformType = PlatformType.Mastodon, - extra = DbMastodonStatusExtra( + extra = MastodonStatusExtra( type = this.type.toDbType(), emoji = emptyList(), visibility = MastodonVisibility.Public, - sensitive = false, - spoilerText = null, - poll = null, - card = null, mentions = null, - ).json(), + ), + spoilerText = null, + poll = null, + card = null, inReplyToStatusId = null, inReplyToUserId = null, - ) - return DbStatusWithReference( - status = DbStatusWithMediaAndUser( - data = status, - media = emptyList(), - user = user, - reactions = emptyList(), - url = emptyList(), - ), - references = listOfNotNull( - relatedStatus.toDbStatusReference( - status.statusKey, - ReferenceType.MastodonNotification - ), - ), + media = emptyList(), + url = emptyList(), + liked = false, + retweeted = false, + referenceStatus = mutableMapOf().apply { + relatedStatus?.let { this[ReferenceType.MastodonNotification] = it } + }, + isGap = isGap ) } @@ -148,49 +143,39 @@ private fun NotificationTypes?.toDbType(): MastodonStatusType { } } -fun Status.toDbPagingTimeline( +fun Status.toPagingTimeline( accountKey: MicroBlogKey, pagingKey: String, -): DbPagingTimelineWithStatus { - val status = this.toDbStatusWithReference(accountKey = accountKey) +): PagingTimeLineWithStatus { + val status = this.toUiStatus(accountKey = accountKey, false) - return DbPagingTimelineWithStatus( - timeline = DbPagingTimeline( - _id = UUID.randomUUID().toString(), + return PagingTimeLineWithStatus( + timeline = PagingTimeLine( accountKey = accountKey, - timestamp = status.status.data.timestamp, + timestamp = status.timestamp, isGap = false, - statusKey = status.status.data.statusKey, + statusKey = status.statusKey, pagingKey = pagingKey, - sortId = status.status.data.timestamp + sortId = status.timestamp ), status = status, ) } -fun Status.toDbStatusWithReference( +internal fun Status.toUiStatus( accountKey: MicroBlogKey, -): DbStatusWithReference { - val status = this.toDbStatusWithMediaAndUser(accountKey) - val retweet = this.reblog?.toDbStatusWithMediaAndUser( + isGap: Boolean = false +): UiStatus { + val retweet = this.reblog?.toUiStatus( accountKey ) - - return DbStatusWithReference( - status = status, - references = listOfNotNull( - retweet.toDbStatusReference(status.data.statusKey, ReferenceType.Retweet), - ), - ) -} - -private fun Status.toDbStatusWithMediaAndUser( - accountKey: MicroBlogKey -): DbStatusWithMediaAndUser { - val user = account?.toDbUser(accountKey = accountKey) + val user = account?.toUiUser(accountKey = accountKey) ?: throw IllegalArgumentException("mastodon Status.user should not be null") - val status = DbStatusV2( - _id = UUID.randomUUID().toString(), + val statusKey = MicroBlogKey( + id ?: throw IllegalArgumentException("mastodon Status.idStr should not be null"), + host = user.userKey.host, + ) + return UiStatus( statusId = id ?: throw IllegalArgumentException("mastodon Status.idStr should not be null"), rawText = content?.let { Jsoup.parse(it).wholeText() } ?: "", htmlText = content?.let { @@ -208,48 +193,66 @@ private fun Status.toDbStatusWithMediaAndUser( generateWithHashtag(content = it) } ?: "", timestamp = createdAt?.time ?: 0, - retweetCount = reblogsCount ?: 0, - likeCount = favouritesCount ?: 0, - replyCount = repliesCount ?: 0, - placeString = "", + metrics = StatusMetrics( + retweet = reblogsCount ?: 0, + like = favouritesCount ?: 0, + reply = repliesCount ?: 0, + ), + geo = UiGeo( + name = "" + ), hasMedia = !mediaAttachments.isNullOrEmpty(), source = application?.name ?: "", - userKey = user.userKey, - lang = null, - statusKey = MicroBlogKey( - id ?: throw IllegalArgumentException("mastodon Status.idStr should not be null"), - host = user.userKey.host, - ), - is_possibly_sensitive = sensitive ?: false, + user = user, + statusKey = statusKey, + sensitive = sensitive ?: false, platformType = PlatformType.Mastodon, - extra = DbMastodonStatusExtra( + spoilerText = spoilerText?.takeIf { it.isNotEmpty() }, + poll = poll?.let { + UiPoll( + id = it.id ?: "", + options = it.options?.map { option -> + Option( + text = option.title ?: "", + count = option.votesCount ?: 0 + ) + } ?: emptyList(), + expiresAt = it.expiresAt?.time, + expired = it.expired ?: false, + multiple = it.multiple ?: false, + voted = it.voted ?: false, + votesCount = it.votesCount ?: 0, + votersCount = it.votersCount ?: 0, + ownVotes = it.ownVotes + ) + }, + extra = MastodonStatusExtra( type = MastodonStatusType.Status, - emoji = emojis ?: emptyList(), - visibility = visibility.toDbEnums(), - sensitive = sensitive ?: false, - spoilerText = spoilerText?.takeIf { it.isNotEmpty() }, - poll = poll, - card = card, - mentions = mentions, - ).json(), - previewCard = card?.url?.let { url -> - DbPreviewCard( + emoji = emojis?.toUi() ?: emptyList(), + visibility = visibility.toMastodonVisibility(), + mentions = mentions?.map { + MastodonMention( + id = it.id, + username = it.username, + url = it.url, + acct = it.acct + ) + }, + ), + card = card?.url?.let { url -> + UiCard( link = url, displayLink = card?.url, title = card?.title, - desc = card?.description?.takeIf { it.isNotEmpty() && it.isNotBlank() }, + description = card?.description?.takeIf { it.isNotEmpty() && it.isNotBlank() }, image = card?.image, ) }, inReplyToUserId = inReplyToAccountID, - inReplyToStatusId = inReplyToID - ) - return DbStatusWithMediaAndUser( - data = status, + inReplyToStatusId = inReplyToID, media = (mediaAttachments ?: emptyList()).mapIndexed { index, it -> - DbMedia( - _id = UUID.randomUUID().toString(), - belongToKey = status.statusKey, + UiMedia( + belongToKey = statusKey, previewUrl = it.previewURL, mediaUrl = it.url, width = it.meta?.original?.width ?: 0, @@ -269,30 +272,21 @@ private fun Status.toDbStatusWithMediaAndUser( order = index, ) }, - user = user, - reactions = if (favourited == true || reblogged == true) { - listOf( - DbStatusReaction( - _id = UUID.randomUUID().toString(), - statusKey = status.statusKey, - accountKey = accountKey, - liked = favourited == true, - retweeted = reblogged == true, - ), - ) - } else { - emptyList() + liked = favourited == true, + retweeted = reblogged == true, + referenceStatus = mutableMapOf().apply { + retweet?.let { this[ReferenceType.Retweet] = it } }, - url = emptyList(), + isGap = isGap, + url = emptyList() ) } -fun Account.toDbUser( +internal fun Account.toUiUser( accountKey: MicroBlogKey -): DbUser { - return DbUser( - _id = UUID.randomUUID().toString(), - userId = this.id ?: throw IllegalArgumentException("mastodon user.id should not be null"), +): UiUser { + return UiUser( + id = this.id ?: throw IllegalArgumentException("mastodon user.id should not be null"), name = displayName?.let { generateHtmlContentWithEmoji(it, emojis ?: emptyList()) } ?: throw IllegalArgumentException("mastodon user.displayName should not be null"), @@ -304,9 +298,13 @@ fun Account.toDbUser( ), profileImage = avatar ?: avatarStatic ?: "", profileBackgroundImage = header ?: headerStatic ?: "", - followersCount = followersCount ?: 0, - friendsCount = followingCount ?: 0, - listedCount = 0, + metrics = UserMetrics( + fans = followersCount ?: 0, + follow = followingCount ?: 0, + listed = 0, + status = statusesCount ?: 0L, + ), + rawDesc = note ?: "", htmlDesc = note?.let { generateHtmlContentWithEmoji( @@ -319,7 +317,7 @@ fun Account.toDbUser( website = null, location = null, verified = false, - isProtected = false, + protected = false, acct = acct?.let { MicroBlogKey.valueOf(it) }?.let { if (it.host.isEmpty()) { it.copy(host = accountKey.host) @@ -328,23 +326,26 @@ fun Account.toDbUser( } } ?: throw IllegalArgumentException("mastodon user.acct should not be null"), platformType = PlatformType.Mastodon, - statusesCount = statusesCount ?: 0L, - extra = DbMastodonUserExtra( - fields = fields ?: emptyList(), + extra = MastodonUserExtra( + fields = fields?.map { field -> + Field( + field.name, + field.value + ) + } ?: emptyList(), bot = bot ?: false, locked = locked ?: false, - emoji = emojis ?: emptyList(), - ).json() + emoji = emojis?.toUi() ?: emptyList(), + ) ) } -fun MastodonList.toDbList(accountKey: MicroBlogKey): DbList { - return DbList( - _id = UUID.randomUUID().toString(), +fun MastodonList.toUiList(accountKey: MicroBlogKey): UiList { + return UiList( ownerId = accountKey.id, - listId = id ?: throw IllegalArgumentException("list.idStr should not be null"), + id = id ?: throw IllegalArgumentException("list.idStr should not be null"), title = title ?: "", - description = "", + descriptions = "", mode = "", replyPolicy = repliesPolicy ?: "", accountKey = accountKey, @@ -357,25 +358,19 @@ fun MastodonList.toDbList(accountKey: MicroBlogKey): DbList { ) } -fun Trend.toDbTrend(accountKey: MicroBlogKey): DbTrendWithHistory { - return DbTrendWithHistory( - trend = DbTrend( - _id = UUID.randomUUID().toString(), - trendKey = MicroBlogKey("$name:$url", accountKey.host), - accountKey = accountKey, - displayName = name ?: "", - query = name ?: "", - url = url ?: "", - volume = 0 - ), +fun Trend.toUiTrend(accountKey: MicroBlogKey): UiTrend { + return UiTrend( + trendKey = MicroBlogKey("$name:$url", accountKey.host), + displayName = name ?: "", + query = name ?: "", + url = url ?: "", + volume = 0, history = history?.map { - DbTrendHistory( - _id = UUID.randomUUID().toString(), + UiTrendHistory( trendKey = MicroBlogKey("$name:$url", accountKey.host), day = it.day?.toLong() ?: 0L, uses = it.uses?.toLong() ?: 0L, accounts = it.accounts?.toLong() ?: 0L, - accountKey = accountKey ) } ?: emptyList() ) @@ -441,7 +436,7 @@ private fun replaceHashTag(node: Node) { } } -private fun Visibility?.toDbEnums() = when (this) { +private fun Visibility?.toMastodonVisibility() = when (this) { Visibility.Unlisted -> MastodonVisibility.Unlisted Visibility.Private -> MastodonVisibility.Private Visibility.Direct -> MastodonVisibility.Direct diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt similarity index 57% rename from android/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index 8e1056e1a..a27717503 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.mapper +package com.twidere.twiderex.dataprovider.mapper import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.ReferencedTweetType @@ -29,35 +29,29 @@ import com.twidere.services.twitter.model.Trend import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.User import com.twidere.services.twitter.model.UserV2 -import com.twidere.twiderex.db.model.DbDMEvent -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbList -import com.twidere.twiderex.db.model.DbMedia -import com.twidere.twiderex.db.model.DbPagingTimeline -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.DbPreviewCard -import com.twidere.twiderex.db.model.DbStatusReaction -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.DbStatusWithMediaAndUser -import com.twidere.twiderex.db.model.DbStatusWithReference -import com.twidere.twiderex.db.model.DbTrend -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.db.model.DbTwitterStatusExtra -import com.twidere.twiderex.db.model.DbTwitterUserExtra -import com.twidere.twiderex.db.model.DbUrlEntity -import com.twidere.twiderex.db.model.DbUser -import com.twidere.twiderex.db.model.TwitterUrlEntity -import com.twidere.twiderex.db.model.toDbStatusReference import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType import com.twidere.twiderex.model.enums.TwitterReplySettings +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.model.ui.ListsMode +import com.twidere.twiderex.model.ui.StatusMetrics +import com.twidere.twiderex.model.ui.UiCard +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiGeo +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.model.ui.UiUrlEntity +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.model.ui.UserMetrics +import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra +import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition -import com.twidere.twiderex.utils.json import com.twitter.twittertext.Autolink -import java.util.UUID val autolink by lazy { Autolink().apply { @@ -75,163 +69,95 @@ private fun generateDeepLinkBase(deeplink: String): String { ) } -fun StatusV2.toDbPagingTimeline( +fun StatusV2.toPagingTimeline( accountKey: MicroBlogKey, pagingKey: String, -): DbPagingTimelineWithStatus { - val status = toDbStatusWithReference(accountKey = accountKey) - return DbPagingTimelineWithStatus( - timeline = DbPagingTimeline( - _id = UUID.randomUUID().toString(), +): PagingTimeLineWithStatus { + val status = toUiStatus(accountKey, isGap = false) + return PagingTimeLineWithStatus( + timeline = PagingTimeLine( accountKey = accountKey, - timestamp = status.status.data.timestamp, + timestamp = status.timestamp, isGap = false, - statusKey = status.status.data.statusKey, + statusKey = status.statusKey, pagingKey = pagingKey, - sortId = status.status.data.timestamp + sortId = status.timestamp ), status = status, ) } -fun StatusV2.toDbStatusWithReference( +fun Status.toPagingTimeline( accountKey: MicroBlogKey, -): DbStatusWithReference { - val status = this.toDbStatusWithMediaAndUser(accountKey) + pagingKey: String, +): PagingTimeLineWithStatus { + val status = toUiStatus(accountKey = accountKey) + + return PagingTimeLineWithStatus( + timeline = PagingTimeLine( + accountKey = accountKey, + timestamp = status.timestamp, + isGap = false, + statusKey = status.statusKey, + pagingKey = pagingKey, + sortId = status.timestamp + ), + status = status, + ) +} + +internal fun StatusV2.toUiStatus( + @Suppress("UNUSED_PARAMETER") + accountKey: MicroBlogKey, + isGap: Boolean = false, +): UiStatus { val retweet = this.referencedTweets - ?.firstOrNull { it.type == ReferencedTweetType.retweeted }?.status?.toDbStatusWithMediaAndUser( + ?.firstOrNull { it.type == ReferencedTweetType.retweeted }?.status?.toUiStatus( accountKey ) val replyTo = this.let { it.referencedTweets ?.firstOrNull { it.type == ReferencedTweetType.retweeted }?.status ?: it }.referencedTweets - ?.firstOrNull { it.type == ReferencedTweetType.replied_to }?.status?.toDbStatusWithMediaAndUser( + ?.firstOrNull { it.type == ReferencedTweetType.replied_to }?.status?.toUiStatus( accountKey ) val quote = this.let { it.referencedTweets ?.firstOrNull { it.type == ReferencedTweetType.retweeted }?.status ?: it }.referencedTweets - ?.firstOrNull { it.type == ReferencedTweetType.quoted }?.status?.toDbStatusWithMediaAndUser( + ?.firstOrNull { it.type == ReferencedTweetType.quoted }?.status?.toUiStatus( accountKey ) - return DbStatusWithReference( - status = status, - references = listOfNotNull( - replyTo.toDbStatusReference(status.data.statusKey, ReferenceType.Reply), - quote.toDbStatusReference(status.data.statusKey, ReferenceType.Quote), - retweet.toDbStatusReference(status.data.statusKey, ReferenceType.Retweet), - ), + val user = user?.toUiUser() ?: throw IllegalArgumentException("Status.user should not be null") + val statusKey = MicroBlogKey.twitter( + id ?: throw IllegalArgumentException("Status.idStr should not be null") ) -} - -fun Status.toDbPagingTimeline( - accountKey: MicroBlogKey, - pagingKey: String, -): DbPagingTimelineWithStatus { - val status = toDbStatusWithReference(accountKey = accountKey) - - return DbPagingTimelineWithStatus( - timeline = DbPagingTimeline( - _id = UUID.randomUUID().toString(), - accountKey = accountKey, - timestamp = status.status.data.timestamp, - isGap = false, - statusKey = status.status.data.statusKey, - pagingKey = pagingKey, - sortId = status.status.data.timestamp - ), - status = status, - ) -} - -fun Status.toDbStatusWithReference( - accountKey: MicroBlogKey, -): DbStatusWithReference { - val status = this.toDbStatusWithMediaAndUser(accountKey) - val retweet = retweetedStatus?.toDbStatusWithMediaAndUser(accountKey) - val quote = - (retweetedStatus?.quotedStatus ?: quotedStatus)?.toDbStatusWithMediaAndUser(accountKey) - - return DbStatusWithReference( - status = status, - references = listOfNotNull( - quote.toDbStatusReference(status.data.statusKey, ReferenceType.Quote), - retweet.toDbStatusReference(status.data.statusKey, ReferenceType.Retweet), - ), - ) -} - -private fun getImage(uri: String?, type: String): String? { - if (uri == null) { - return null - } - if (uri.contains(".")) { - val index = uri.lastIndexOf(".") - val extension = uri.substring(index) - return "${uri.removeSuffix(extension)}?format=${extension.removePrefix(".")}&name=$type" - } - return uri -} - -private fun StatusV2.toDbStatusWithMediaAndUser( - @Suppress("UNUSED_PARAMETER") - accountKey: MicroBlogKey -): DbStatusWithMediaAndUser { - val user = user?.toDbUser() ?: throw IllegalArgumentException("Status.user should not be null") - val status = DbStatusV2( - _id = UUID.randomUUID().toString(), + return UiStatus( statusId = id ?: throw IllegalArgumentException("Status.idStr should not be null"), - is_possibly_sensitive = possiblySensitive ?: false, + sensitive = possiblySensitive ?: false, rawText = text ?: "", htmlText = autolink.autoLink(text ?: ""), -// htmlText = autolink.autoLinkEntities(text ?: "", entities?.let { -// (it.mentions?.map { -// Extractor.Entity( -// it.start?.toInt() ?: 0, -// it.end?.toInt() ?: 0, -// it.username ?: "", -// Extractor.Entity.Type.MENTION -// ) -// } ?: emptyList()) + (it.hashtags?.map { -// Extractor.Entity( -// it.start?.toInt() ?: 0, -// it.end?.toInt() ?: 0, -// it.tag ?: "", -// Extractor.Entity.Type.HASHTAG -// ) -// } ?: emptyList()) + (it.urls?.map { -// Extractor.Entity( -// it.start?.toInt() ?: 0, -// it.end?.toInt() ?: 0, -// it.url ?: "", -// Extractor.Entity.Type.URL -// ).apply { -// displayURL = it.displayURL -// expandedURL = it.expandedURL -// } -// } ?: emptyList()) -// }?.distinctBy { it.start }?.sortedBy { it.start } ?: emptyList()), timestamp = createdAt?.time ?: 0, - retweetCount = publicMetrics?.retweetCount ?: 0, - likeCount = publicMetrics?.likeCount ?: 0, - replyCount = publicMetrics?.replyCount ?: 0, - placeString = place?.fullName, + metrics = StatusMetrics( + retweet = publicMetrics?.retweetCount ?: 0, + like = publicMetrics?.likeCount ?: 0, + reply = publicMetrics?.replyCount ?: 0, + ), + geo = UiGeo( + name = place?.fullName ?: "" + ), hasMedia = !attachments?.media.isNullOrEmpty(), source = source ?: "", - userKey = user.userKey, - lang = lang, - statusKey = MicroBlogKey.twitter( - id ?: throw IllegalArgumentException("Status.idStr should not be null") - ), + user = user, + statusKey = statusKey, platformType = PlatformType.Twitter, - extra = DbTwitterStatusExtra( + extra = TwitterStatusExtra( reply_settings = replySettings.toDbEnums(), quoteCount = publicMetrics?.quoteCount - ).json(), - previewCard = entities?.urls?.firstOrNull() + ), + card = entities?.urls?.firstOrNull() ?.takeUnless { url -> referencedTweets?.firstOrNull { it.type == ReferencedTweetType.quoted } ?.id?.let { id -> url.expandedURL?.endsWith(id) == true } == true @@ -239,10 +165,10 @@ private fun StatusV2.toDbStatusWithMediaAndUser( ?.takeUnless { url -> url.displayURL?.contains("pic.twitter.com") == true } ?.let { it.expandedURL?.let { url -> - DbPreviewCard( + UiCard( link = url, title = it.title, - desc = it.description, + description = it.description, image = it.images?.firstOrNull()?.url, displayLink = it.displayURL, ) @@ -250,16 +176,14 @@ private fun StatusV2.toDbStatusWithMediaAndUser( }, inReplyToStatusId = referencedTweets?.find { it.type == ReferencedTweetType.replied_to }?.id, inReplyToUserId = inReplyToUserId, - ) - return DbStatusWithMediaAndUser( - data = status, + retweeted = false, + liked = false, media = (attachments?.media ?: emptyList()).filter { it.type == MediaType.photo.name // TODO: video and gif }.mapIndexed { index, it -> val type = it.type?.let { MediaType.valueOf(it) } ?: MediaType.photo - DbMedia( - _id = UUID.randomUUID().toString(), - belongToKey = status.statusKey, + UiMedia( + belongToKey = statusKey, previewUrl = getImage(it.url ?: it.previewImageURL, "small"), mediaUrl = getImage(it.url ?: it.previewImageURL, "orig"), width = it.width ?: 0, @@ -271,12 +195,9 @@ private fun StatusV2.toDbStatusWithMediaAndUser( order = index, ) }, - user = user, - reactions = emptyList(), // TODO: twitter v2 api does not return this + isGap = isGap, url = entities?.urls?.map { - DbUrlEntity( - _id = UUID.randomUUID().toString(), - statusKey = status.statusKey, + UiUrlEntity( url = it.url ?: "", expandedUrl = it.expandedURL ?: "", displayUrl = it.displayURL ?: "", @@ -284,90 +205,84 @@ private fun StatusV2.toDbStatusWithMediaAndUser( description = it.description, image = it.images?.maxByOrNull { it.width ?: it.height ?: 0 }?.url ) - } ?: emptyList() + } ?: emptyList(), + referenceStatus = mutableMapOf().apply { + replyTo?.let { this[ReferenceType.Reply] = it } + quote?.let { this[ReferenceType.Quote] = it } + retweet?.let { this[ReferenceType.Retweet] = it } + } ) } -private fun Status.toDbStatusWithMediaAndUser( - accountKey: MicroBlogKey -): DbStatusWithMediaAndUser { - val user = user?.toDbUser() ?: throw IllegalArgumentException("Status.user should not be null") - val status = DbStatusV2( - _id = UUID.randomUUID().toString(), +private fun getImage(uri: String?, type: String): String? { + if (uri == null) { + return null + } + if (uri.contains(".")) { + val index = uri.lastIndexOf(".") + val extension = uri.substring(index) + return "${uri.removeSuffix(extension)}?format=${extension.removePrefix(".")}&name=$type" + } + return uri +} + +internal fun Status.toUiStatus( + accountKey: MicroBlogKey, + isGap: Boolean = false, +): UiStatus { + val retweet = retweetedStatus?.toUiStatus(accountKey) + val quote = + (retweetedStatus?.quotedStatus ?: quotedStatus)?.toUiStatus(accountKey) + + val user = user?.toUiUser() ?: throw IllegalArgumentException("Status.user should not be null") + val statusKey = MicroBlogKey.twitter( + idStr ?: throw IllegalArgumentException("Status.idStr should not be null") + ) + return UiStatus( statusId = idStr ?: throw IllegalArgumentException("Status.idStr should not be null"), - is_possibly_sensitive = possiblySensitive ?: false, + sensitive = possiblySensitive ?: false, rawText = fullText ?: text ?: "", htmlText = autolink.autoLink(fullText ?: text ?: ""), -// htmlText = autolink.autoLinkEntities(fullText ?: text ?: "", entities?.let { -// (it.userMentions?.map { -// Extractor.Entity( -// it.indices?.elementAtOrNull(0)?.toInt() ?: 0, -// it.indices?.elementAtOrNull(1)?.toInt() ?: 0, -// it.screenName ?: "", -// Extractor.Entity.Type.MENTION -// ) -// } ?: emptyList()) + (it.hashtags?.map { -// Extractor.Entity( -// it.indices?.elementAtOrNull(0)?.toInt() ?: 0, -// it.indices?.elementAtOrNull(1)?.toInt() ?: 0, -// it.text ?: "", -// Extractor.Entity.Type.HASHTAG -// ) -// } ?: emptyList()) + (it.urls?.map { -// Extractor.Entity( -// it.indices?.elementAtOrNull(0)?.toInt() ?: 0, -// it.indices?.elementAtOrNull(1)?.toInt() ?: 0, -// it.url ?: "", -// Extractor.Entity.Type.URL -// ).apply { -// displayURL = it.displayURL -// expandedURL = it.expandedURL -// } -// } ?: emptyList()) -// }?.sortedBy { it.start } ?: emptyList()), timestamp = createdAt?.time ?: 0, - retweetCount = retweetCount ?: 0, - likeCount = favoriteCount ?: 0, - replyCount = 0, - placeString = place?.fullName, + metrics = StatusMetrics( + retweet = retweetCount ?: 0, + like = favoriteCount ?: 0, + reply = 0, + ), + geo = UiGeo( + name = place?.fullName ?: "" + ), hasMedia = extendedEntities?.media != null || entities?.media != null, source = source ?: "", - userKey = user.userKey, - lang = lang, - statusKey = MicroBlogKey.twitter( - idStr ?: throw IllegalArgumentException("Status.idStr should not be null") - ), + user = user, + statusKey = statusKey, platformType = PlatformType.Twitter, - extra = DbTwitterStatusExtra( + extra = TwitterStatusExtra( reply_settings = TwitterReplySettings.Everyone, - ).json(), - previewCard = entities?.urls?.firstOrNull() + ), + card = entities?.urls?.firstOrNull() ?.takeUnless { url -> quotedStatus?.idStr?.let { id -> url.expandedURL?.endsWith(id) == true } == true } ?.takeUnless { url -> url.expandedURL?.contains("pic.twitter.com") == true } ?.let { it.url?.let { url -> - DbPreviewCard( + UiCard( link = it.expandedURL ?: url, displayLink = it.displayURL, image = null, title = null, - desc = null, + description = null, ) } }, inReplyToUserId = inReplyToUserIDStr, - inReplyToStatusId = inReplyToStatusIDStr - ) - return DbStatusWithMediaAndUser( - data = status, + inReplyToStatusId = inReplyToStatusIDStr, media = ( extendedEntities?.media ?: entities?.media ?: emptyList() ).mapIndexed { index, it -> val type = it.type?.let { MediaType.valueOf(it) } ?: MediaType.photo - DbMedia( - _id = UUID.randomUUID().toString(), - belongToKey = status.statusKey, + UiMedia( + belongToKey = statusKey, previewUrl = getImage(it.mediaURLHTTPS, "small"), mediaUrl = when (type) { MediaType.photo -> getImage(it.mediaURLHTTPS, "orig") @@ -386,24 +301,11 @@ private fun Status.toDbStatusWithMediaAndUser( order = index, ) }, - user = user, - reactions = if (favorited == true || retweeted == true) { - listOf( - DbStatusReaction( - _id = UUID.randomUUID().toString(), - statusKey = status.statusKey, - accountKey = accountKey, - liked = favorited == true, - retweeted = retweeted == true, - ), - ) - } else { - emptyList() - }, + liked = favorited == true, + retweeted = retweeted == true, + isGap = isGap, url = entities?.urls?.map { - DbUrlEntity( - _id = UUID.randomUUID().toString(), - statusKey = status.statusKey, + UiUrlEntity( url = it.url ?: "", expandedUrl = it.expandedURL ?: "", displayUrl = it.displayURL ?: "", @@ -411,82 +313,94 @@ private fun Status.toDbStatusWithMediaAndUser( description = null, image = null, ) - } ?: emptyList() + } ?: emptyList(), + referenceStatus = mutableMapOf().apply { + quote?.let { this[ReferenceType.Quote] = it } + retweet?.let { this[ReferenceType.Retweet] = it } + } ) } -fun User.toDbUser(): DbUser { - return DbUser( - _id = UUID.randomUUID().toString(), - userId = this.idStr ?: throw IllegalArgumentException("user.idStr should not be null"), +internal fun User.toUiUser(): UiUser { + return UiUser( + id = this.idStr ?: throw IllegalArgumentException("user.idStr should not be null"), name = this.name ?: "", screenName = this.screenName ?: "", profileImage = (profileImageURLHTTPS ?: profileImageURL)?.let { updateProfileImagePath(it) } ?: "", profileBackgroundImage = profileBannerURL, - followersCount = this.followersCount ?: 0, - friendsCount = this.friendsCount ?: 0, - listedCount = this.listedCount ?: 0, + metrics = UserMetrics( + fans = this.followersCount ?: 0, + follow = this.friendsCount ?: 0, + listed = this.listedCount ?: 0, + status = statusesCount ?: 0, + ), rawDesc = this.description ?: "", htmlDesc = autolink.autoLink(this.description ?: ""), location = this.location, website = this.entities?.url?.urls?.firstOrNull { it.url == this.url }?.expandedURL, verified = this.verified ?: false, - isProtected = this.protected ?: false, + protected = this.protected ?: false, userKey = MicroBlogKey.twitter( idStr ?: throw IllegalArgumentException("user.idStr should not be null") ), platformType = PlatformType.Twitter, acct = MicroBlogKey.twitter(screenName ?: ""), - statusesCount = statusesCount ?: 0, - extra = DbTwitterUserExtra( + extra = TwitterUserExtra( pinned_tweet_id = null, url = entities?.description?.urls?.map { - TwitterUrlEntity( + UiUrlEntity( url = it.url ?: "", expandedUrl = it.expandedURL ?: "", displayUrl = it.displayURL ?: "", + title = "", + description = "", + image = null ) } ?: emptyList() - ).json() + ) ) } -fun UserV2.toDbUser(): DbUser { - return DbUser( - _id = UUID.randomUUID().toString(), - userId = id ?: throw IllegalArgumentException("user.idStr should not be null"), +internal fun UserV2.toUiUser(): UiUser { + return UiUser( + id = id ?: throw IllegalArgumentException("user.idStr should not be null"), name = name ?: "", screenName = username ?: "", profileImage = profileImageURL?.let { updateProfileImagePath(it) } ?: "", profileBackgroundImage = profileBanner?.sizes?.let { it.getOrElse("mobile_retina", { null }) ?: it.values.firstOrNull() }?.url, - followersCount = publicMetrics?.followersCount ?: 0, - friendsCount = publicMetrics?.followingCount ?: 0, - listedCount = publicMetrics?.listedCount ?: 0, + metrics = UserMetrics( + fans = publicMetrics?.followersCount ?: 0, + follow = publicMetrics?.followingCount ?: 0, + listed = publicMetrics?.listedCount ?: 0, + status = publicMetrics?.tweetCount ?: 0, + ), rawDesc = description ?: "", htmlDesc = autolink.autoLink(description ?: ""), location = location, website = entities?.url?.urls?.firstOrNull { it.url == url }?.expandedURL, verified = verified ?: false, - isProtected = protected ?: false, + protected = protected ?: false, userKey = MicroBlogKey.twitter( id ?: throw IllegalArgumentException("user.idStr should not be null") ), acct = MicroBlogKey.twitter(username ?: ""), platformType = PlatformType.Twitter, - statusesCount = publicMetrics?.tweetCount ?: 0, - extra = DbTwitterUserExtra( + extra = TwitterUserExtra( pinned_tweet_id = pinnedTweetID, url = entities?.description?.urls?.map { - TwitterUrlEntity( + UiUrlEntity( url = it.url ?: "", expandedUrl = it.expandedURL ?: "", displayUrl = it.displayURL ?: "", + title = null, + description = null, + image = null ) } ?: emptyList() - ).json() + ) ) } @@ -506,7 +420,7 @@ private fun updateProfileImagePath( } } -enum class ProfileImageSize { +private enum class ProfileImageSize { original, reasonably_small, bigger, @@ -514,12 +428,11 @@ enum class ProfileImageSize { mini, } -fun TwitterList.toDbList(accountKey: MicroBlogKey) = DbList( - _id = UUID.randomUUID().toString(), +internal fun TwitterList.toUiList(accountKey: MicroBlogKey) = UiList( ownerId = user?.idStr ?: "", - listId = idStr ?: throw IllegalArgumentException("list.idStr should not be null"), + id = idStr ?: throw IllegalArgumentException("list.idStr should not be null"), title = name ?: "", - description = description ?: "", + descriptions = description ?: "", mode = mode ?: "", replyPolicy = "", accountKey = accountKey, @@ -528,16 +441,12 @@ fun TwitterList.toDbList(accountKey: MicroBlogKey) = DbList( allowToSubscribe = mode != ListsMode.PRIVATE.value ) -fun Trend.toDbTrend(accountKey: MicroBlogKey) = DbTrendWithHistory( - trend = DbTrend( - _id = UUID.randomUUID().toString(), - trendKey = MicroBlogKey.twitter("$name:$url"), - accountKey = accountKey, - displayName = name ?: "", - query = name ?: "", - url = url ?: "", - volume = tweetVolume ?: 0, - ), +internal fun Trend.toUiTrend() = UiTrend( + trendKey = MicroBlogKey.twitter("$name:$url"), + displayName = name ?: "", + query = name ?: "", + url = url ?: "", + volume = tweetVolume ?: 0, history = emptyList() ) @@ -549,30 +458,26 @@ fun DirectMessageEvent.generateConversationId(accountKey: MicroBlogKey): String } } -fun DirectMessageEvent.toDbDirectMessage(accountKey: MicroBlogKey, sender: DbUser): DbDMEventWithAttachments { - val message = DbDMEvent( - _id = UUID.randomUUID().toString(), +fun DirectMessageEvent.toUiDMEvent(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent { + val messageKey = MicroBlogKey.twitter("dm-${id ?: throw IllegalArgumentException("message id should not be null")}") + return UiDMEvent( accountKey = accountKey, sortId = createdTimestamp?.toLong() ?: 0L, conversationKey = MicroBlogKey.twitter(generateConversationId(accountKey)), messageId = id ?: throw IllegalArgumentException("message id should not be null"), - messageKey = MicroBlogKey.twitter("dm-${id ?: throw IllegalArgumentException("message id should not be null")}"), + messageKey = messageKey, htmlText = autolink.autoLink(messageCreate?.messageData?.text ?: ""), originText = messageCreate?.messageData?.text ?: "", createdTimestamp = createdTimestamp?.toLong() ?: 0L, messageType = type ?: throw IllegalArgumentException("message type should not be null"), senderAccountKey = MicroBlogKey.twitter(messageCreate?.senderId ?: throw IllegalArgumentException("message sender id should not be null")), recipientAccountKey = MicroBlogKey.twitter(messageCreate?.target?.recipientId ?: throw IllegalArgumentException("message recipientId id should not be null")), - sendStatus = DbDMEvent.SendStatus.SUCCESS - ) - return DbDMEventWithAttachments( - message = message, + sendStatus = UiDMEvent.SendStatus.SUCCESS, media = messageCreate?.messageData?.attachment?.media?.let { media -> val type = media.type?.let { MediaType.valueOf(it) } ?: MediaType.photo listOf( - DbMedia( - _id = UUID.randomUUID().toString(), - belongToKey = message.messageKey, + UiMedia( + belongToKey = messageKey, url = media.url ?: "", previewUrl = media.mediaURLHTTPS, type = type, @@ -593,9 +498,7 @@ fun DirectMessageEvent.toDbDirectMessage(accountKey: MicroBlogKey, sender: DbUse ) } ?: emptyList(), urlEntity = messageCreate?.messageData?.entities?.urls?.map { - DbUrlEntity( - _id = UUID.randomUUID().toString(), - statusKey = message.messageKey, + UiUrlEntity( url = it.url ?: "", expandedUrl = it.expanded_url ?: "", displayUrl = it.display_url ?: "", diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt index 370b2f045..e97696009 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt @@ -31,7 +31,6 @@ data class PagingTimeLine( val timestamp: Long, val sortId: Long, var isGap: Boolean, - var status: UiStatus ) data class PagingTimeLineWithStatus( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 6b108a19f..7946f5a2b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -37,6 +37,7 @@ data class UiMedia( val height: Long, val pageUrl: String?, val altText: String, + val order: Int, ) { val fileName: String? get() = mediaUrl?.takeIfFileName() ?: url?.takeIfFileName() ?: findFileName() @@ -79,6 +80,7 @@ data class UiMedia( height = 0, // painterResource(id = R.drawable.featured_graphics).intrinsicSize.height.toLong(), pageUrl = null, altText = "", + order = 0, ), ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt index 3faf4862d..e81dd4b4d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt @@ -29,7 +29,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt index ce23e5f57..d2f886418 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt @@ -26,7 +26,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.model.IPaging import com.twidere.services.microblog.model.IUser -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.MemoryCachePagingMediator diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index 5e8cb1269..8fa365c2f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -27,11 +27,10 @@ import androidx.paging.PagingData import androidx.paging.PagingState import androidx.paging.map import com.twidere.services.microblog.model.IStatus -import com.twidere.twiderex.dataprovider.toPagingTimeline +import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import com.twidere.twiderex.model.paging.saveToDb import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.IPagingList diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index a1ede3d2e..c724912fb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -24,7 +24,7 @@ import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingState import com.twidere.services.microblog.model.IStatus -import com.twidere.twiderex.dataprovider.toPagingTimeline +import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt index 05844482f..bb51465e3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt @@ -29,7 +29,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import androidx.paging.RemoteMediator import com.twidere.services.microblog.TrendService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt index 90a221137..07d43c5ca 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt index 913e2d9af..7cd1debfa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt index 4208a1859..6b01b48fd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.paging.source import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt index d1781d589..d795bd280 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.services.microblog.model.IPaging import com.twidere.services.microblog.model.IUser -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt index df179ade1..2afa51e3b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt @@ -26,7 +26,7 @@ import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.model.IDirectMessage import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.exceptions.TwitterApiException -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt index 1cabd4889..6dd02f1e8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.repository import androidx.paging.PagingData import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt index b0eb6cb4d..9f6fc15f2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.repository import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.NotificationCursorType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 972ddfb7d..f34febe8c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -27,7 +27,7 @@ import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.MicroBlogService import com.twidere.services.nitter.NitterService import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt index f03380c11..03d159cc3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.repository import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.dataprovider.toUi +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index d6f04d2fa..008d84482 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -35,5 +35,6 @@ internal fun UiMedia.Companion.mock(url: String, belongToKey: MicroBlogKey) = Ui width = 100, height = 100, pageUrl = "", - altText = "" + altText = "", + order = index ) From ded64b863f6ee85db90e59a5311540ab948ec439 Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 11 Aug 2021 21:23:37 +0800 Subject: [PATCH 049/615] only play video when visible in screen --- .../component/foundation/VideoPlayer.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 3ac33fa92..dea0d2c21 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -41,6 +41,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource @@ -92,7 +94,6 @@ fun VideoPlayer( val context = LocalContext.current val lifecycle = LocalLifecycleOwner.current.lifecycle val httpConfig = LocalHttpConfig.current - Box { if (playInitial) { val player = remember(url) { @@ -177,8 +178,23 @@ fun VideoPlayer( } } + var isInScreen by remember { + mutableStateOf(false) + } + AndroidView( - modifier = modifier, + modifier = modifier.onGloballyPositioned { coordinates -> + coordinates.boundsInWindow().run { + if (isInScreen && top <= 0 && bottom <= 0) { + isInScreen = false + updateState() + player.playWhenReady = false + } else if (!isInScreen && top > 0 && bottom > 0) { + isInScreen = true + player.playWhenReady = autoPlay + } + } + }, factory = { context -> StyledPlayerView(context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) @@ -188,7 +204,7 @@ fun VideoPlayer( } ) { it.player = player - if (isResume) { + if (isResume && isInScreen) { it.onResume() } else { it.onPause() From f387c6fb3cdccea192b884ab3c0e4ae5bf2edd4b Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 12 Aug 2021 14:10:28 +0800 Subject: [PATCH 050/615] fixed test for MediaRepository and CacheRepository --- .../twiderex/dataprovider/DataProvider.kt | 4 +- .../twiderex/dataprovider/DataTransform.kt | 51 ----------- .../twiderex/db/AndroidCacheDatabase.kt | 42 --------- .../repository/AccountUpdateRepository.kt} | 15 ++-- ...AppCacheHandler.kt => FileCacheHandler.kt} | 6 +- .../twiderex/dataprovider/DataProvider.kt | 4 +- .../dataprovider/mapper/DataMapper.kt | 2 +- .../twiderex/dataprovider/mapper/Mastodon.kt | 9 +- .../twiderex/dataprovider/mapper/Twitter.kt | 12 +-- .../com/twidere/twiderex/db/CacheDatabase.kt | 2 - .../com/twidere/twiderex/db/Database.kt | 2 +- .../com/twidere/twiderex/db/dao/StatusDao.kt | 6 ++ .../twidere/twiderex/di/RepositoryModule.kt | 2 +- .../com/twidere/twiderex/di/TwidereModule.kt | 2 +- .../com/twidere/twiderex/model/ui/UiTrend.kt | 1 + .../paging/PagingTimelineMediatorBase.kt | 5 +- .../twiderex/repository/CacheRepository.kt | 18 ++-- .../twiderex/repository/ReactionRepository.kt | 2 +- ...acheHandler.kt => MockFileCacheHandler.kt} | 18 +--- .../twiderex/mock/db/MockAppDatabase.kt} | 27 ++++-- .../twiderex/mock/db/MockCacheDatabase.kt | 85 +++++++++++++++++++ .../mock/db/{ => dao}/MockMediaDao.kt | 8 +- .../twiderex/mock/db/dao/MockSearchDao.kt | 76 +++++++++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 13 ++- .../repository/CacheRepositoryTest.kt | 25 ++++-- .../repository/MediaRepositoryTest.kt | 11 ++- .../twiderex/dataprovider/DataProvider.kt | 5 +- .../twiderex/dataprovider/DataTransform.kt | 51 ----------- .../twiderex/db/DesktopAppDatabaseImpl.kt | 42 --------- .../AccountUpdateRepository.kt} | 12 ++- 30 files changed, 283 insertions(+), 275 deletions(-) delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt rename common/src/{commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt => androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt} (74%) rename common/src/commonMain/kotlin/com/twidere/twiderex/cache/{AppCacheHandler.kt => FileCacheHandler.kt} (90%) rename common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/{MockAppCacheHandler.kt => MockFileCacheHandler.kt} (72%) rename common/src/{androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt => commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt} (65%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/{ => dao}/MockMediaDao.kt (82%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt delete mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt delete mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt rename common/src/desktopMain/kotlin/com/twidere/twiderex/{db/DesktopCacheDatabaseImpl.kt => repository/AccountUpdateRepository.kt} (74%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index f336a0030..311f2bc87 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.dataprovider -import com.twidere.twiderex.cache.AppCacheHandler +import com.twidere.twiderex.cache.FileCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -37,6 +37,6 @@ actual class DataProvider { actual val cacheDatabase: CacheDatabase get() = TODO("Not yet implemented") - actual val appCacheHandler: AppCacheHandler + actual val fileCacheHandler: FileCacheHandler get() = TODO("Not yet implemented") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt deleted file mode 100644 index edd6c7873..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.dataprovider - -import com.twidere.services.microblog.model.INotification -import com.twidere.services.microblog.model.IStatus -import com.twidere.services.microblog.model.IUser -import com.twidere.services.twitter.model.DirectMessageEvent -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import com.twidere.twiderex.model.ui.UiDMEvent -import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.model.ui.UiUser - -actual fun IUser.toUi(accountKey: MicroBlogKey): UiUser { - TODO("Not yet implemented") -} - -actual fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus { - TODO("Not yet implemented") -} - -actual fun INotification.toUi(accountKey: MicroBlogKey): UiStatus { - TODO("Not yet implemented") -} - -actual fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus { - TODO("Not yet implemented") -} - -actual fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent { - TODO("Not yet implemented") -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt deleted file mode 100644 index 2daaf8cdb..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidCacheDatabase.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import com.twidere.twiderex.db.dao.DraftDao -import com.twidere.twiderex.db.dao.SearchDao - -internal class AndroidCacheDatabase : AppDatabase { - override fun draftDao(): DraftDao { - TODO("Not yet implemented") - } - - override fun searchDao(): SearchDao { - TODO("Not yet implemented") - } - - override suspend fun clearAllTables() { - TODO("Not yet implemented") - } - - override fun withTransaction(block: suspend () -> R): R { - TODO("Not yet implemented") - } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt similarity index 74% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt index 1a396c280..3cd7a6705 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt @@ -18,15 +18,12 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.repository -import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser -interface ReactionDao { - suspend fun updateAction( - statusKey: MicroBlogKey, - accountKey: MicroBlogKey, - liked: Boolean?, - retweet: Boolean? - ) +actual class AccountUpdateRepository { + actual fun updateAccount(user: UiUser) { + TODO("NOT IMPLEMENT YET") + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt similarity index 90% rename from common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt index 0d52b6e21..4e8ee69cb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/AppCacheHandler.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt @@ -20,12 +20,8 @@ */ package com.twidere.twiderex.cache -interface AppCacheHandler { +interface FileCacheHandler { fun clearMediaCaches() fun clearFileCaches() - - fun clearDatabaseCaches() - - fun clearSearchHistories() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index 34d05c271..8edb48ae3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.dataprovider -import com.twidere.twiderex.cache.AppCacheHandler +import com.twidere.twiderex.cache.FileCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -34,5 +34,5 @@ expect class DataProvider { val cacheDatabase: CacheDatabase - val appCacheHandler: AppCacheHandler + val fileCacheHandler: FileCacheHandler } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt index ae129d237..b62504b5d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt @@ -100,7 +100,7 @@ fun IListModel.toUi(accountKey: MicroBlogKey) = when (this) { } fun ITrend.toUi(accountKey: MicroBlogKey) = when (this) { - is TwitterTrend -> this.toUiTrend() + is TwitterTrend -> this.toUiTrend(accountKey) is MastodonTrend -> this.toUiTrend(accountKey) else -> throw NotImplementedError() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index e105fc7a1..77d6dccc7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -53,7 +53,7 @@ import com.twidere.twiderex.model.ui.mastodon.Field import com.twidere.twiderex.model.ui.mastodon.MastodonMention import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra -import com.twidere.twiderex.navigation.RootDeepLinksRoute +// import com.twidere.twiderex.navigation.RootDeepLinksRoute import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -360,6 +360,7 @@ fun MastodonList.toUiList(accountKey: MicroBlogKey): UiList { fun Trend.toUiTrend(accountKey: MicroBlogKey): UiTrend { return UiTrend( + accountKey = accountKey, trendKey = MicroBlogKey("$name:$url", accountKey.host), displayName = name ?: "", query = name ?: "", @@ -409,7 +410,8 @@ private fun replaceMention(mentions: List, node: Node, accountKey: Micr if (id != null) { node.attr( "href", - RootDeepLinksRoute.User(MicroBlogKey(id, accountKey.host)) + // TODO migrate Route + // RootDeepLinksRoute.User(MicroBlogKey(id, accountKey.host)) ) } } else { @@ -429,7 +431,8 @@ private fun replaceHashTag(node: Node) { ) { node.attr( "href", - RootDeepLinksRoute.Mastodon.Hashtag(node.text().trimStart('#')) + // TODO migrate Route + // RootDeepLinksRoute.Mastodon.Hashtag(node.text().trimStart('#')) ) } else { node.childNodes().forEach { replaceHashTag(it) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index a27717503..6882bb29d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -50,15 +50,16 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.model.ui.UserMetrics import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra -import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition +// import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition import com.twitter.twittertext.Autolink val autolink by lazy { Autolink().apply { setUsernameIncludeSymbol(true) - hashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" - cashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" - usernameUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" + // TODO migrate Route + hashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" + cashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" + usernameUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" } } @@ -441,7 +442,8 @@ internal fun TwitterList.toUiList(accountKey: MicroBlogKey) = UiList( allowToSubscribe = mode != ListsMode.PRIVATE.value ) -internal fun Trend.toUiTrend() = UiTrend( +internal fun Trend.toUiTrend(accountKey: MicroBlogKey) = UiTrend( + accountKey = accountKey, trendKey = MicroBlogKey.twitter("$name:$url"), displayName = name ?: "", query = name ?: "", diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt index d15d4cfdb..4e633219c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt @@ -26,7 +26,6 @@ import com.twidere.twiderex.db.dao.ListsDao import com.twidere.twiderex.db.dao.MediaDao import com.twidere.twiderex.db.dao.NotificationCursorDao import com.twidere.twiderex.db.dao.PagingTimelineDao -import com.twidere.twiderex.db.dao.ReactionDao import com.twidere.twiderex.db.dao.StatusDao import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.UserDao @@ -35,7 +34,6 @@ interface CacheDatabase : Database { fun statusDao(): StatusDao fun mediaDao(): MediaDao fun userDao(): UserDao - fun reactionDao(): ReactionDao fun pagingTimelineDao(): PagingTimelineDao fun listsDao(): ListsDao fun notificationCursorDao(): NotificationCursorDao diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt index f9517ec59..cbd37ad62 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt @@ -22,5 +22,5 @@ package com.twidere.twiderex.db interface Database { suspend fun clearAllTables() - fun withTransaction(block: suspend () -> R): R + suspend fun withTransaction(block: suspend () -> R): R } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 75621dc1c..7fe74cdc5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -35,4 +35,10 @@ interface StatusDao { suspend fun findWithStatusKeyWithReference(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? suspend fun delete(statusKey: MicroBlogKey) + suspend fun updateAction( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + liked: Boolean?, + retweet: Boolean? + ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index c5f16aa6a..c793321e8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -27,5 +27,5 @@ import org.koin.dsl.module internal val repositoryModules = module { factory { MediaRepository(get().cacheDatabase.mediaDao()) } - factory { CacheRepository(get()) } + factory { CacheRepository(get(), get(), get()) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt index c0a414e90..71134c694 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -25,7 +25,7 @@ import org.koin.dsl.module internal val twidereModules = module { single { DataProvider.create() } - single { get().appCacheHandler } + single { get().fileCacheHandler } single { get().cacheDatabase } single { get().appDatabase } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt index 9e96f4cb5..d15bf3b4e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.model.ui import com.twidere.twiderex.model.MicroBlogKey data class UiTrend( + val accountKey: MicroBlogKey, val trendKey: MicroBlogKey, val displayName: String, val url: String, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index 8fa365c2f..83a84da75 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -25,12 +25,14 @@ import androidx.paging.LoadType import androidx.paging.Pager import androidx.paging.PagingData import androidx.paging.PagingState +import androidx.paging.filter import androidx.paging.map import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.saveToDb import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.IPagingList @@ -124,6 +126,7 @@ abstract class PagingTimelineMediatorBase( fun Pager.toUi(accountKey: MicroBlogKey): Flow> { return flow.map { pagingData -> - pagingData.map { it.status } + pagingData.filter { it.timeline.accountKey == accountKey } + .map { it.status } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt index 7ebcfb40e..a6abad28b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -20,29 +20,32 @@ */ package com.twidere.twiderex.repository -import com.twidere.twiderex.cache.AppCacheHandler +import com.twidere.twiderex.cache.FileCacheHandler +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch class CacheRepository( - private val appCache: AppCacheHandler, + private val fileCache: FileCacheHandler, + private val cacheDatabase: CacheDatabase, + private val appDatabase: AppDatabase ) { suspend fun clearDatabaseCache() = coroutineScope { launch(Dispatchers.IO) { - appCache.clearDatabaseCaches() - // cacheDatabase.clearAllTables() + cacheDatabase.clearAllTables() } } suspend fun clearImageCache() = coroutineScope { - appCache.clearMediaCaches() + fileCache.clearMediaCaches() // cache.directory.deleteRecursively() } suspend fun clearCacheDir() = coroutineScope { launch(Dispatchers.IO) { - appCache.clearFileCaches() + fileCache.clearFileCaches() // cacheDirs.forEach { // it.listFiles()?.forEach { file -> // file.deleteRecursively() @@ -53,8 +56,7 @@ class CacheRepository( suspend fun clearSearchHistory() = coroutineScope { launch(Dispatchers.IO) { - appCache.clearSearchHistories() - // appDatabase.searchDao().clear() + appDatabase.searchDao().clear() } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt index a2d86df61..1d4462fdf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt @@ -32,7 +32,7 @@ class ReactionRepository( liked: Boolean? = null, retweet: Boolean? = null, ) { - database.reactionDao().updateAction( + database.statusDao().updateAction( statusKey = statusKey, accountKey = accountKey, liked = liked, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt similarity index 72% rename from common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt index e75e29930..cac90adff 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockAppCacheHandler.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt @@ -20,14 +20,12 @@ */ package com.twidere.twiderex.mock.cache -import com.twidere.twiderex.cache.AppCacheHandler +import com.twidere.twiderex.cache.FileCacheHandler -internal class MockAppCacheHandler( +internal class MockFileCacheHandler( private val mediaCache: MutableList, private val fileCache: MutableList, - private val database: MutableList, - private val searchHistories: MutableList -) : AppCacheHandler { +) : FileCacheHandler { override fun clearMediaCaches() { mediaCache.clear() } @@ -36,13 +34,5 @@ internal class MockAppCacheHandler( fileCache.clear() } - override fun clearDatabaseCaches() { - database.clear() - } - - override fun clearSearchHistories() { - searchHistories.clear() - } - - fun isCacheCleared() = mediaCache.isEmpty() && fileCache.isEmpty() && database.isEmpty() && searchHistories.isEmpty() + fun isCacheCleared() = mediaCache.isEmpty() && fileCache.isEmpty() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt similarity index 65% rename from common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt index 0d105af15..0e2b0c5ba 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/AndroidAppDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt @@ -18,25 +18,34 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db +package com.twidere.twiderex.mock.db +import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.dao.DraftDao import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.mock.db.dao.MockSearchDao +import org.jetbrains.annotations.TestOnly -internal class AndroidAppDatabase : AppDatabase { - override suspend fun clearAllTables() { +internal class MockAppDatabase @TestOnly constructor() : AppDatabase { + override fun draftDao(): DraftDao { TODO("Not yet implemented") } - override fun withTransaction(block: suspend () -> R): R { - TODO("Not yet implemented") + private val searchDao = MockSearchDao() + override fun searchDao(): SearchDao { + return searchDao } - override fun draftDao(): DraftDao { - TODO("Not yet implemented") + private var cleared = false + override suspend fun clearAllTables() { + cleared = true } - override fun searchDao(): SearchDao { - TODO("Not yet implemented") + fun isAllTablesCleared(): Boolean { + return cleared + } + + override suspend fun withTransaction(block: suspend () -> R): R { + return block.invoke() } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt new file mode 100644 index 000000000..35f23f745 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db + +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.db.dao.DirectMessageConversationDao +import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.db.dao.ListsDao +import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.db.dao.NotificationCursorDao +import com.twidere.twiderex.db.dao.PagingTimelineDao +import com.twidere.twiderex.db.dao.StatusDao +import com.twidere.twiderex.db.dao.TrendDao +import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.mock.db.dao.MockMediaDao +import org.jetbrains.annotations.TestOnly + +internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { + override fun statusDao(): StatusDao { + TODO("Not yet implemented") + } + + override fun mediaDao(): MediaDao { + return MockMediaDao() + } + + override fun userDao(): UserDao { + TODO("Not yet implemented") + } + + override fun pagingTimelineDao(): PagingTimelineDao { + TODO("Not yet implemented") + } + + override fun listsDao(): ListsDao { + TODO("Not yet implemented") + } + + override fun notificationCursorDao(): NotificationCursorDao { + TODO("Not yet implemented") + } + + override fun trendDao(): TrendDao { + TODO("Not yet implemented") + } + + override fun directMessageConversationDao(): DirectMessageConversationDao { + TODO("Not yet implemented") + } + + override fun directMessageDao(): DirectMessageEventDao { + TODO("Not yet implemented") + } + + private var cleared = false + override suspend fun clearAllTables() { + cleared = true + } + + override suspend fun withTransaction(block: suspend () -> R): R { + return block.invoke() + } + + fun isAllTablesCleared(): Boolean { + return cleared + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt similarity index 82% rename from common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt index 34662a009..9da1fa707 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockMediaDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt @@ -18,14 +18,18 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.mock.db +package com.twidere.twiderex.mock.db.dao import com.twidere.twiderex.db.dao.MediaDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import org.jetbrains.annotations.TestOnly -internal class MockMediaDao @TestOnly constructor(private val initData: List = emptyList()) : MediaDao { +internal class MockMediaDao @TestOnly constructor() : MediaDao { + private var initData: List = emptyList() + fun initData(initData: List) { + this.initData = initData + } override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { return initData.filter { it.belongToKey == belongToKey diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt new file mode 100644 index 000000000..7fd359939 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt @@ -0,0 +1,76 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import org.jetbrains.annotations.TestOnly + +class MockSearchDao @TestOnly constructor() : SearchDao { + private val fakeDb = mutableMapOf>() + override suspend fun insertAll(search: List) { + search.forEach { uiSearch -> + fakeDb[uiSearch.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[uiSearch.accountKey] = mutableListOf(uiSearch) + } else { + it.add(uiSearch) + } + } + } + } + + override fun getAll(accountKey: MicroBlogKey): Flow> { + return flow { + fakeDb[accountKey]?.toList()?.let { + emit(it) + } ?: emit(emptyList()) + } + } + + override fun getAllHistory(accountKey: MicroBlogKey): Flow> { + return getAll(accountKey) + .map { it.filter { search -> !search.saved } } + } + + override fun getAllSaved(accountKey: MicroBlogKey): Flow> { + return getAll(accountKey) + .map { it.filter { search -> search.saved } } + } + + override suspend fun get(content: String, accountKey: MicroBlogKey): UiSearch? { + return fakeDb[accountKey]?.find { + it.content == content + } + } + + override suspend fun remove(search: UiSearch) { + fakeDb[search.accountKey]?.removeAll { it.content == search.content } + } + + override suspend fun clear() { + fakeDb.clear() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 008d84482..c65382cf2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -23,10 +23,11 @@ package com.twidere.twiderex.mock.model import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.model.ui.UiSearch import org.jetbrains.annotations.TestOnly @TestOnly -internal fun UiMedia.Companion.mock(url: String, belongToKey: MicroBlogKey) = UiMedia( +internal fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty) = UiMedia( url = url, belongToKey = belongToKey, mediaUrl = url, @@ -36,5 +37,13 @@ internal fun UiMedia.Companion.mock(url: String, belongToKey: MicroBlogKey) = Ui height = 100, pageUrl = "", altText = "", - order = index + order = 0 +) + +@TestOnly +internal fun mockUiSearch(content: String = "", accountKey: MicroBlogKey = MicroBlogKey.Empty, saved: Boolean = false) = UiSearch( + content = content, + lastActive = System.currentTimeMillis(), + saved = saved, + accountKey = accountKey ) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt index 44f1b34e3..0ec8ed405 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt @@ -20,24 +20,39 @@ */ package com.twidere.twiderex.repository -import com.twidere.twiderex.mock.cache.MockAppCacheHandler +import com.twidere.twiderex.mock.cache.MockFileCacheHandler +import com.twidere.twiderex.mock.db.MockAppDatabase +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.model.mockUiSearch +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking import org.junit.Test class CacheRepositoryTest { @Test fun clearAllCachesSuccess() = runBlocking { - val handler = MockAppCacheHandler( + val handler = MockFileCacheHandler( mediaCache = mutableListOf("media"), fileCache = mutableListOf("file"), - database = mutableListOf("database"), - searchHistories = mutableListOf("search"), ) - val repository = CacheRepository(handler) + val cacheDatabase = MockCacheDatabase() + val appDatabase = MockAppDatabase() + val repository = CacheRepository(handler, cacheDatabase, appDatabase) + appDatabase.searchDao().insertAll(listOf(mockUiSearch(accountKey = MicroBlogKey.twitter("1")))) + val list = appDatabase.searchDao().getAll(MicroBlogKey.twitter("1")).first() + assert(list.isNotEmpty()) + assert(!handler.isCacheCleared()) + assert(!cacheDatabase.isAllTablesCleared()) + repository.clearCacheDir() repository.clearDatabaseCache() repository.clearImageCache() repository.clearSearchHistory() + assert(handler.isCacheCleared()) + assert(cacheDatabase.isAllTablesCleared()) + assert(appDatabase.searchDao().getAll(MicroBlogKey.twitter("1")).firstOrNull().isNullOrEmpty()) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt index fc9d0ce6f..70fbf8f46 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt @@ -20,10 +20,9 @@ */ package com.twidere.twiderex.repository -import com.twidere.twiderex.mock.db.MockMediaDao -import com.twidere.twiderex.mock.model.mock +import com.twidere.twiderex.mock.db.dao.MockMediaDao +import com.twidere.twiderex.mock.model.mockUiMedia import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiMedia import kotlinx.coroutines.runBlocking import org.junit.Test @@ -31,9 +30,9 @@ internal class MediaRepositoryTest { @Test fun findMediasWithBelongToKey() = runBlocking { val repository = MediaRepository( - MockMediaDao( - listOf(UiMedia.mock("test", MicroBlogKey.twitter("account"))) - ) + MockMediaDao().apply { + initData(listOf(mockUiMedia("test", MicroBlogKey.twitter("account")))) + } ) val result = repository.findMediaByBelongToKey(MicroBlogKey.twitter("account")) assert(result.isNotEmpty()) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index f336a0030..651f806af 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.dataprovider -import com.twidere.twiderex.cache.AppCacheHandler +import com.twidere.twiderex.cache.FileCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase @@ -37,6 +37,7 @@ actual class DataProvider { actual val cacheDatabase: CacheDatabase get() = TODO("Not yet implemented") - actual val appCacheHandler: AppCacheHandler + + actual val fileCacheHandler: FileCacheHandler get() = TODO("Not yet implemented") } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt deleted file mode 100644 index edd6c7873..000000000 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataTransform.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.dataprovider - -import com.twidere.services.microblog.model.INotification -import com.twidere.services.microblog.model.IStatus -import com.twidere.services.microblog.model.IUser -import com.twidere.services.twitter.model.DirectMessageEvent -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import com.twidere.twiderex.model.ui.UiDMEvent -import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.model.ui.UiUser - -actual fun IUser.toUi(accountKey: MicroBlogKey): UiUser { - TODO("Not yet implemented") -} - -actual fun IStatus.toUi(accountKey: MicroBlogKey): UiStatus { - TODO("Not yet implemented") -} - -actual fun INotification.toUi(accountKey: MicroBlogKey): UiStatus { - TODO("Not yet implemented") -} - -actual fun IStatus.toPagingTimeline(accountKey: MicroBlogKey, pagingKey: String): PagingTimeLineWithStatus { - TODO("Not yet implemented") -} - -actual fun DirectMessageEvent.toUi(accountKey: MicroBlogKey, sender: UiUser): UiDMEvent { - TODO("Not yet implemented") -} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt deleted file mode 100644 index 0c794c8a3..000000000 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopAppDatabaseImpl.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import com.twidere.twiderex.db.dao.DraftDao -import com.twidere.twiderex.db.dao.SearchDao - -internal class DesktopAppDatabaseImpl : AppDatabase { - override fun draftDao(): DraftDao { - TODO("Not yet implemented") - } - - override fun searchDao(): SearchDao { - TODO("Not yet implemented") - } - - override suspend fun clearAllTables() { - TODO("Not yet implemented") - } - - override fun withTransaction(block: suspend () -> R): R { - TODO("Not yet implemented") - } -} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt similarity index 74% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt index 4a8d30ae4..3cd7a6705 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/db/DesktopCacheDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt @@ -18,14 +18,12 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db +package com.twidere.twiderex.repository -internal class DesktopCacheDatabaseImpl : Database { - override suspend fun clearAllTables() { - TODO("Not yet implemented") - } +import com.twidere.twiderex.model.ui.UiUser - override fun withTransaction(block: suspend () -> R): R { - TODO("Not yet implemented") +actual class AccountUpdateRepository { + actual fun updateAccount(user: UiUser) { + TODO("NOT IMPLEMENT YET") } } From fccd06e7c1bfcb53a7027c3d6cf8b6f0e8a0c27e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 12 Aug 2021 15:05:14 +0800 Subject: [PATCH 051/615] migrate paging mediator test to commonTest --- common/build.gradle.kts | 2 + .../twidere/twiderex/MainThreadTestBase.kt | 45 ++++++ .../twiderex/mock/db/MockCacheDatabase.kt | 8 +- .../twiderex/mock/db/dao/MockListsDao.kt | 99 ++++++++++++++ .../twiderex/mock/db/dao/MockTrendDao.kt | 54 ++++++++ .../twiderex/mock/paging/MockPagingSource.kt | 98 ++++++++++++++ .../twiderex/mock/service/MockListsService.kt | 128 ++++++++++++++++++ .../twiderex/mock/service/MockTrendService.kt | 56 ++++++++ .../crud/MemoryCachePagingMediatorTest.kt | 0 .../crud/MemoryCachePagingSourceTest.kt | 0 .../paging/crud/PagingMemoryCacheTest.kt | 0 .../paging/lists/ListsMediatorTest.kt | 21 +-- .../lists/ListsUserPagingMediatorTest.kt | 0 .../paging/trend/TrendMediatorTest.kt | 34 ++--- .../repository/DirectMessageRepositoryTest.kt | 31 +++++ 15 files changed, 548 insertions(+), 28 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt rename android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt => common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt (100%) rename {android/src/test/java => common/src/commonTest/kotlin}/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt (100%) rename {android/src/test/java => common/src/commonTest/kotlin}/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt (100%) rename {android/src/test/java => common/src/commonTest/kotlin}/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt (72%) rename {android/src/test/java => common/src/commonTest/kotlin}/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt (100%) rename {android/src/test/java => common/src/commonTest/kotlin}/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt (53%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 73dc4875e..fc9685802 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -46,6 +46,8 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") + implementation("org.mockito:mockito-core:3.11.2") + implementation("org.mockito.kotlin:mockito-kotlin:3.2.0") } } val androidMain by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt new file mode 100644 index 000000000..d2ec2719f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt @@ -0,0 +1,45 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.ObsoleteCoroutinesApi +import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Before +@OptIn(ExperimentalCoroutinesApi::class) +internal open class MainThreadTestBase { + @OptIn(ObsoleteCoroutinesApi::class) + private val mainThreadSurrogate = newSingleThreadContext("UI thread") + + @Before + open fun setUp() { + Dispatchers.setMain(mainThreadSurrogate) + } + + @After + open fun tearDown() { + Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 35f23f745..4fc5ffa3a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -30,7 +30,9 @@ import com.twidere.twiderex.db.dao.PagingTimelineDao import com.twidere.twiderex.db.dao.StatusDao import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.mock.db.dao.MockListsDao import com.twidere.twiderex.mock.db.dao.MockMediaDao +import com.twidere.twiderex.mock.db.dao.MockTrendDao import org.jetbrains.annotations.TestOnly internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { @@ -50,16 +52,18 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { TODO("Not yet implemented") } + private val listsDao = MockListsDao() override fun listsDao(): ListsDao { - TODO("Not yet implemented") + return listsDao } override fun notificationCursorDao(): NotificationCursorDao { TODO("Not yet implemented") } + private val trendDao = MockTrendDao() override fun trendDao(): TrendDao { - TODO("Not yet implemented") + return trendDao } override fun directMessageConversationDao(): DirectMessageConversationDao { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt new file mode 100644 index 000000000..ec2e2e8fe --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt @@ -0,0 +1,99 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.ListsDao +import com.twidere.twiderex.mock.paging.MockPagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.jetbrains.annotations.TestOnly + +internal class MockListsDao @TestOnly constructor() : ListsDao { + private val fakeDb = mutableMapOf>() + + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return MockPagingSource(fakeDb[accountKey]?.toList() ?: emptyList()) + } + + override fun findWithListKeyWithFlow( + listKey: MicroBlogKey, + accountKey: MicroBlogKey + ): Flow { + return flow { + emit( + fakeDb[accountKey]?.let { + it.find { list -> list.listKey == listKey } + } + ) + } + } + + override suspend fun insertAll(listOf: List) { + listOf.forEach { list -> + fakeDb[list.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[list.accountKey] = mutableListOf(list) + } else { + it.add(list) + } + } + } + } + + override suspend fun findWithListKey(listKey: MicroBlogKey, accountKey: MicroBlogKey): UiList? { + return fakeDb[accountKey]?.let { + it.find { list -> list.listKey == listKey } + } + } + + override suspend fun update(listOf: List) { + listOf.forEach { list -> + fakeDb[list.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[list.accountKey] = mutableListOf(list) + } else { + it.map { origin -> + if (origin.listKey == list.listKey) list else origin + } + } + } + } + } + + override suspend fun delete(listOf: List) { + listOf.forEach { list -> + fakeDb[list.accountKey].let { + if (!it.isNullOrEmpty()) { + it.mapNotNull { origin -> + if (origin.listKey == list.listKey) null else origin + } + } + } + } + } + + override suspend fun clearAll(accountKey: MicroBlogKey) { + fakeDb.clear() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt new file mode 100644 index 000000000..6c1b0a95b --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt @@ -0,0 +1,54 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.TrendDao +import com.twidere.twiderex.mock.paging.MockPagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import org.jetbrains.annotations.TestOnly + +class MockTrendDao @TestOnly constructor() : TrendDao { + private val fakeDb = mutableMapOf>() + + override fun insertAll(trends: List) { + trends.forEach { uiTrend -> + fakeDb[uiTrend.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[uiTrend.accountKey] = mutableListOf(uiTrend) + } else { + it.add(uiTrend) + } + } + } + } + + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return MockPagingSource( + fakeDb[accountKey] ?: emptyList() + ) + } + + override suspend fun clear() { + fakeDb.clear() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt new file mode 100644 index 000000000..b6c0addb9 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt @@ -0,0 +1,98 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.paging + +import androidx.paging.CombinedLoadStates +import androidx.paging.DifferCallback +import androidx.paging.NullPaddedList +import androidx.paging.PagingData +import androidx.paging.PagingDataDiffer +import androidx.paging.PagingSource +import androidx.paging.PagingState +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import org.jetbrains.annotations.TestOnly + +internal class MockPagingSource @TestOnly constructor(val data: List) : PagingSource() { + + override fun getRefreshKey(state: PagingState): Int? { + return null + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val page = params.key ?: 0 + val count = params.loadSize + val startIndex = page * count + val endIndex = page * count + count + val result = when { + endIndex <= data.size -> { + data.subList(startIndex, endIndex) + } + data.size in (startIndex + 1) until endIndex -> { + data.subList(startIndex, data.size) + } + else -> { + emptyList() + } + } + LoadResult.Page(result, null, if (result.isEmpty()) null else page + 1) + } catch (e: Exception) { + LoadResult.Error(e) + } + } +} + +@OptIn(ExperimentalCoroutinesApi::class) +@TestOnly +internal suspend fun PagingData.collectDataForTest(): List { + val dcb = object : DifferCallback { + override fun onChanged(position: Int, count: Int) {} + override fun onInserted(position: Int, count: Int) {} + override fun onRemoved(position: Int, count: Int) {} + } + val items = mutableListOf() + val dif = object : PagingDataDiffer(dcb, TestCoroutineDispatcher()) { + override suspend fun presentNewList( + previousList: NullPaddedList, + newList: NullPaddedList, + newCombinedLoadStates: CombinedLoadStates, + lastAccessedIndex: Int, + onListPresentable: () -> Unit + ): Int? { + for (idx in 0 until newList.size) + items.add(newList.getFromStorage(idx)) + onListPresentable() + return null + } + } + dif.collectFrom(this) + return items +} + +@TestOnly +internal suspend fun PagingSource.collectDataForTest(): List { + return if (this is MockPagingSource) { + PagingData.from(this.data).collectDataForTest() + } else { + emptyList() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt new file mode 100644 index 000000000..ac28392f4 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -0,0 +1,128 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.ListsService +import com.twidere.services.microblog.MicroBlogService +import com.twidere.services.microblog.model.IListModel +import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.TwitterList +import com.twidere.services.twitter.model.exceptions.TwitterApiException + +class MockListsService : ListsService, MicroBlogService { + override suspend fun lists( + userId: String?, + screenName: String?, + reverse: Boolean + ): List { + val list = mutableListOf() + for (i in 0 until 20) { + val id = System.currentTimeMillis() + list.add( + TwitterList( + id = id, + idStr = id.toString(), + name = "list $i timestamp:${System.currentTimeMillis()}", + ) + ) + } + return list + } + + override suspend fun createList( + name: String, + mode: String?, + description: String?, + repliesPolicy: String? + ): IListModel { + if (name == "error") throw TwitterApiException(error = "throw exception intentional") + val id = System.currentTimeMillis() + return TwitterList( + id = id, + idStr = id.toString(), + name = name, + description = description, + mode = mode, + ) + } + + override suspend fun updateList( + listId: String, + name: String?, + mode: String?, + description: String?, + repliesPolicy: String? + ): IListModel { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + return TwitterList( + id = listId.toLong(), + idStr = listId, + name = name, + mode = mode, + description = description, + ) + } + + override suspend fun destroyList(listId: String) { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + // do nothing + } + + override suspend fun listMembers(listId: String, count: Int, cursor: String?): List { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + TODO("Not yet implemented") + } + + override suspend fun addMember(listId: String, userId: String, screenName: String) { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + // do nothing + } + + override suspend fun removeMember(listId: String, userId: String, screenName: String) { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + // do nothing + } + + override suspend fun listSubscribers(listId: String, count: Int, cursor: String?): List { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + TODO("Not yet implemented") + } + + override suspend fun unsubscribeList(listId: String): IListModel { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + return TwitterList( + id = listId.toLong(), + idStr = listId, + name = "", + following = false + ) + } + + override suspend fun subscribeList(listId: String): IListModel { + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + return TwitterList( + id = listId.toLong(), + idStr = listId, + name = "", + following = true + ) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt new file mode 100644 index 000000000..845b39e5a --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt @@ -0,0 +1,56 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.mastodon.model.Trend +import com.twidere.services.mastodon.model.TrendHistory +import com.twidere.services.microblog.MicroBlogService +import com.twidere.services.microblog.TrendService +import com.twidere.services.microblog.model.ITrend +import java.lang.IllegalArgumentException + +class MockTrendService : TrendService, MicroBlogService { + + override suspend fun trends(locationId: String, limit: Int?): List { + + return if (locationId == "error") + throw IllegalArgumentException("service error") + else { + val list = mutableListOf() + for (i in 0 until (limit ?: 1)) { + list.add( + Trend( + name = "trend $i timestamp:${System.currentTimeMillis()}", + url = "https://trend", + history = mutableListOf( + TrendHistory( + accounts = "1", + uses = "1", + day = System.currentTimeMillis().toString() + ) + ) + ) + ) + } + list + } + } +} diff --git a/android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt similarity index 100% rename from android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt similarity index 100% rename from android/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt similarity index 100% rename from android/src/test/java/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt similarity index 72% rename from android/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index 435fbc84b..f9529d1f9 100644 --- a/android/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -23,13 +23,13 @@ package com.twidere.twiderex.paging.lists import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState -import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.db.model.DbList -import com.twidere.twiderex.mock.MockCenter +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockListsService import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.paging.mediator.list.ListsMediator import kotlinx.coroutines.runBlocking -import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnitRunner @@ -40,19 +40,20 @@ import org.mockito.junit.MockitoJUnitRunner @RunWith(MockitoJUnitRunner::class) class ListsMediatorTest { - private var mockDataBase = MockCenter.mockCacheDatabase() + private var mockDataBase = MockCacheDatabase() - private var mockService = MockCenter.mockListsService() as ListsService + private var mockService = MockListsService() @Test fun load_saveToDatabaseWhenSuccess() { runBlocking { - Assert.assertEquals(0, mockDataBase.listsDao().findAll()?.size) + val accountKey = MicroBlogKey.twitter("123") + assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) val mediator = ListsMediator(mockDataBase, mockService, accountKey = MicroBlogKey.twitter("123")) - val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) mediator.load(LoadType.REFRESH, pagingState) - // when mediator get data from service, it store to database - assert(mockDataBase.listsDao().findAll()?.isNotEmpty() == true) + // when mediator get data from service, it store to database\ + assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isNotEmpty()) } } } diff --git a/android/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt similarity index 100% rename from android/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt similarity index 53% rename from android/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt index d68ae25b7..9fd6fe0de 100644 --- a/android/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt @@ -23,13 +23,15 @@ package com.twidere.twiderex.paging.trend import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState -import com.twidere.services.microblog.TrendService -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.mock.MockCenter +import com.twidere.twiderex.MainThreadTestBase +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockTrendService import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend import com.twidere.twiderex.paging.mediator.trend.TrendMediator +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnitRunner @@ -39,20 +41,20 @@ import org.mockito.junit.MockitoJUnitRunner */ @RunWith(MockitoJUnitRunner::class) -class TrendMediatorTest { - private var mockDataBase = MockCenter.mockCacheDatabase() +internal class TrendMediatorTest : MainThreadTestBase() { + private var mockDataBase = MockCacheDatabase() - private var mockService = MockCenter.mockTrendService() as TrendService + private var mockService = MockTrendService() @Test - fun load_saveToDatabaseWhenSuccess() { - runBlocking { - Assert.assertEquals(0, mockDataBase.listsDao().findAll()?.size) - val mediator = TrendMediator(mockDataBase, mockService, accountKey = MicroBlogKey.twitter("123"), "1") - val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) - mediator.load(LoadType.REFRESH, pagingState) - // when mediator get data from service, it store to database - assert(mockDataBase.trendDao().find(MicroBlogKey.twitter("123"), 10).isNotEmpty()) - } + fun load_saveToDatabaseWhenSuccess() = runBlocking(Dispatchers.Main) { + val accountKey = MicroBlogKey.twitter("123") + assert(mockDataBase.trendDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) + val mediator = TrendMediator(mockDataBase, mockService, accountKey = accountKey, "1") + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + mediator.load(LoadType.REFRESH, pagingState) + + // when mediator get data from service, it store to database + assert(mockDataBase.trendDao().getPagingSource(accountKey).collectDataForTest().isNotEmpty()) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt new file mode 100644 index 000000000..51be9ada4 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockCacheDatabase +import org.junit.Before + +internal class DirectMessageRepositoryTest { + private val cacheDatabase = MockCacheDatabase() + @Before + fun setUp() { + } +} From 53dff39ec751f0ba5db818d1d1ffc93800cfbfca Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 12 Aug 2021 15:58:37 +0800 Subject: [PATCH 052/615] opt test for trend and list mediators --- .../paging/source/FollowersPagingSource.kt | 32 ++------- .../paging/source/FollowingPagingSource.kt | 32 ++------- .../source/ListsSubscribersPagingSource.kt | 5 -- .../twidere/twiderex/mock/model/MockModels.kt | 54 +++++++++++++++ .../twiderex/mock/service/ErrorService.kt | 11 +++ .../twiderex/mock/service/MockListsService.kt | 55 ++++++++------- .../twiderex/mock/service/MockTrendService.kt | 26 ++----- .../paging/lists/ListMembersMediatorTest.kt | 68 +++++++++++++++++++ .../paging/lists/ListsMediatorTest.kt | 39 ++++++++--- .../paging/trend/TrendMediatorTest.kt | 22 ++++-- 10 files changed, 230 insertions(+), 114 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt index 07d43c5ca..4f106d8f6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt @@ -20,37 +20,17 @@ */ package com.twidere.twiderex.paging.source -import androidx.paging.PagingSource -import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService -import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.services.microblog.model.IUser import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiUser class FollowersPagingSource( - private val userKey: MicroBlogKey, + userKey: MicroBlogKey, private val service: RelationshipService -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val page = params.key - val result = service.followers(userKey.id, nextPage = page) - val users = result.map { - it.toUi(userKey) - } - val nextPage = if (result is IPaging) { - result.nextPage - } else { - null - } - LoadResult.Page(data = users, prevKey = null, nextKey = nextPage) - } catch (e: Exception) { - LoadResult.Error(e) - } - } +) : UserPagingSource(userKey = userKey) { - override fun getRefreshKey(state: PagingState): String? { - return null + override suspend fun loadUsers(params: LoadParams): List { + val page = params.key + return service.followers(userKey.id, nextPage = page) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt index 7cd1debfa..6e2b5fac2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt @@ -20,37 +20,17 @@ */ package com.twidere.twiderex.paging.source -import androidx.paging.PagingSource -import androidx.paging.PagingState import com.twidere.services.microblog.RelationshipService -import com.twidere.services.microblog.model.IPaging -import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.services.microblog.model.IUser import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiUser class FollowingPagingSource( - private val userKey: MicroBlogKey, + userKey: MicroBlogKey, private val service: RelationshipService -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val page = params.key - val result = service.following(userKey.id, nextPage = page) - val users = result.map { - it.toUi(userKey) - } - val nextPage = if (result is IPaging) { - result.nextPage - } else { - null - } - LoadResult.Page(data = users, prevKey = null, nextKey = nextPage) - } catch (e: Exception) { - LoadResult.Error(e) - } - } +) : UserPagingSource(userKey = userKey) { - override fun getRefreshKey(state: PagingState): String? { - return null + override suspend fun loadUsers(params: LoadParams): List { + val page = params.key + return service.following(userKey.id, nextPage = page) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt index 25b7ab53f..c20901a49 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt @@ -20,21 +20,16 @@ */ package com.twidere.twiderex.paging.source -import androidx.paging.PagingState import com.twidere.services.microblog.ListsService import com.twidere.services.microblog.model.IUser import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiUser class ListsSubscribersPagingSource( userKey: MicroBlogKey, private val service: ListsService, private val listId: String, ) : UserPagingSource(userKey) { - override fun getRefreshKey(state: PagingState): String? { - return null - } override suspend fun loadUsers(params: LoadParams): List { return service.listSubscribers(listId = listId, count = defaultLoadCount, params.key) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index c65382cf2..1975d0de4 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -20,11 +20,20 @@ */ package com.twidere.twiderex.mock.model +import com.twidere.services.mastodon.model.Trend +import com.twidere.services.mastodon.model.TrendHistory +import com.twidere.services.microblog.model.IListModel +import com.twidere.services.microblog.model.ITrend +import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.TwitterList +import com.twidere.services.twitter.model.TwitterPaging +import com.twidere.services.twitter.model.User import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch import org.jetbrains.annotations.TestOnly +import java.util.UUID @TestOnly internal fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty) = UiMedia( @@ -47,3 +56,48 @@ internal fun mockUiSearch(content: String = "", accountKey: MicroBlogKey = Micro saved = saved, accountKey = accountKey ) + +internal fun List.toIPaging(nextPaging: String? = UUID.randomUUID().toString()) = TwitterPaging( + data = this, + nextPage = nextPaging +) + +@TestOnly +internal fun mockIUser(): IUser { + val id = System.currentTimeMillis() + return User( + id = id, + idStr = id.toString(), + ) +} + +@TestOnly +internal fun mockITrend(): ITrend { + return Trend( + name = "trend timestamp:${System.currentTimeMillis()}", + url = "https://trend", + history = mutableListOf( + TrendHistory( + accounts = "1", + uses = "1", + day = System.currentTimeMillis().toString() + ) + ) + ) +} + +@TestOnly +internal fun mockIListModel( + name: String = "", + mode: String? = null, + description: String? = "", +): IListModel { + val id = System.currentTimeMillis() + return TwitterList( + id = id, + idStr = id.toString(), + name = name, + mode = mode, + description = description, + ) +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt new file mode 100644 index 000000000..3bce547fd --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt @@ -0,0 +1,11 @@ +package com.twidere.twiderex.mock.service + +import com.twidere.services.twitter.model.exceptions.TwitterApiException + +internal open class ErrorService { + var errorMsg:String? = null + + fun checkError(){ + if (!errorMsg.isNullOrEmpty()) throw TwitterApiException(errorMsg) + } +} \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index ac28392f4..1c37de0fa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -26,25 +26,25 @@ import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.exceptions.TwitterApiException +import com.twidere.twiderex.mock.model.mockIListModel +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toIPaging + +internal class MockListsService : ListsService, MicroBlogService, ErrorService() { -class MockListsService : ListsService, MicroBlogService { override suspend fun lists( userId: String?, screenName: String?, reverse: Boolean ): List { + checkError() val list = mutableListOf() for (i in 0 until 20) { - val id = System.currentTimeMillis() list.add( - TwitterList( - id = id, - idStr = id.toString(), - name = "list $i timestamp:${System.currentTimeMillis()}", - ) + mockIListModel() ) } - return list + return list.toIPaging(null) } override suspend fun createList( @@ -53,11 +53,8 @@ class MockListsService : ListsService, MicroBlogService { description: String?, repliesPolicy: String? ): IListModel { - if (name == "error") throw TwitterApiException(error = "throw exception intentional") - val id = System.currentTimeMillis() - return TwitterList( - id = id, - idStr = id.toString(), + checkError() + return mockIListModel( name = name, description = description, mode = mode, @@ -71,7 +68,7 @@ class MockListsService : ListsService, MicroBlogService { description: String?, repliesPolicy: String? ): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() return TwitterList( id = listId.toLong(), idStr = listId, @@ -82,32 +79,44 @@ class MockListsService : ListsService, MicroBlogService { } override suspend fun destroyList(listId: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() // do nothing } override suspend fun listMembers(listId: String, count: Int, cursor: String?): List { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - TODO("Not yet implemented") + checkError() + val list = mutableListOf() + for (i in 0 until count) { + list.add( + mockIUser() + ) + } + return list.toIPaging() } override suspend fun addMember(listId: String, userId: String, screenName: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() // do nothing } override suspend fun removeMember(listId: String, userId: String, screenName: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() // do nothing } override suspend fun listSubscribers(listId: String, count: Int, cursor: String?): List { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - TODO("Not yet implemented") + checkError() + val list = mutableListOf() + for (i in 0 until count) { + list.add( + mockIUser() + ) + } + return list.toIPaging() } override suspend fun unsubscribeList(listId: String): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() return TwitterList( id = listId.toLong(), idStr = listId, @@ -117,7 +126,7 @@ class MockListsService : ListsService, MicroBlogService { } override suspend fun subscribeList(listId: String): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") + checkError() return TwitterList( id = listId.toLong(), idStr = listId, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt index 845b39e5a..4c46b66e2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt @@ -20,37 +20,25 @@ */ package com.twidere.twiderex.mock.service -import com.twidere.services.mastodon.model.Trend -import com.twidere.services.mastodon.model.TrendHistory import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.TrendService import com.twidere.services.microblog.model.ITrend -import java.lang.IllegalArgumentException - -class MockTrendService : TrendService, MicroBlogService { +import com.twidere.twiderex.mock.model.mockITrend +import com.twidere.twiderex.mock.model.toIPaging +internal class MockTrendService : TrendService, MicroBlogService, ErrorService() { override suspend fun trends(locationId: String, limit: Int?): List { - + checkError() return if (locationId == "error") throw IllegalArgumentException("service error") else { - val list = mutableListOf() + val list = mutableListOf() for (i in 0 until (limit ?: 1)) { list.add( - Trend( - name = "trend $i timestamp:${System.currentTimeMillis()}", - url = "https://trend", - history = mutableListOf( - TrendHistory( - accounts = "1", - uses = "1", - day = System.currentTimeMillis().toString() - ) - ) - ) + mockITrend() ) } - list + list.toIPaging() } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt new file mode 100644 index 000000000..b78532bc2 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt @@ -0,0 +1,68 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.lists + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.paging.crud.PagingMemoryCache +import com.twidere.twiderex.paging.mediator.list.ListsMembersMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class ListMembersMediatorTest { + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessResultWhenSuccess() = runBlocking { + val userKey = MicroBlogKey.twitter("test") + val mediator = ListsMembersMediator( + memoryCache = PagingMemoryCache(), + userKey = userKey, + service = MockListsService(), + listId = "list id" + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val userKey = MicroBlogKey.twitter("test") + val mediator = ListsMembersMediator( + memoryCache = PagingMemoryCache(), + userKey = userKey, + service = MockListsService().apply { errorMsg = "throw test error" }, + listId = "list id" + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index f9529d1f9..8d71316bb 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -20,9 +20,11 @@ */ package com.twidere.twiderex.paging.lists +import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState +import androidx.paging.RemoteMediator import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.paging.collectDataForTest import com.twidere.twiderex.mock.service.MockListsService @@ -42,18 +44,33 @@ import org.mockito.junit.MockitoJUnitRunner class ListsMediatorTest { private var mockDataBase = MockCacheDatabase() - private var mockService = MockListsService() + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_saveToDatabaseWhenSuccess() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) + val mediator = ListsMediator(mockDataBase, MockListsService(), accountKey = accountKey) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert((result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + @OptIn(ExperimentalPagingApi::class) @Test - fun load_saveToDatabaseWhenSuccess() { - runBlocking { - val accountKey = MicroBlogKey.twitter("123") - assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) - val mediator = ListsMediator(mockDataBase, mockService, accountKey = MicroBlogKey.twitter("123")) - val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) - mediator.load(LoadType.REFRESH, pagingState) - // when mediator get data from service, it store to database\ - assert(mockDataBase.listsDao().getPagingSource(accountKey).collectDataForTest().isNotEmpty()) - } + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val userKey = MicroBlogKey.twitter("test") + val mediator = ListsMediator( + mockDataBase, + MockListsService().apply { + errorMsg = "throw test errors" + }, + accountKey = userKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt index 9fd6fe0de..b21c8e0f8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt @@ -20,9 +20,11 @@ */ package com.twidere.twiderex.paging.trend +import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState +import androidx.paging.RemoteMediator import com.twidere.twiderex.MainThreadTestBase import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.paging.collectDataForTest @@ -33,28 +35,40 @@ import com.twidere.twiderex.paging.mediator.trend.TrendMediator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner /** * instead of testing pagination, we should focus on our code logic */ -@RunWith(MockitoJUnitRunner::class) internal class TrendMediatorTest : MainThreadTestBase() { private var mockDataBase = MockCacheDatabase() private var mockService = MockTrendService() + @OptIn(ExperimentalPagingApi::class) @Test fun load_saveToDatabaseWhenSuccess() = runBlocking(Dispatchers.Main) { val accountKey = MicroBlogKey.twitter("123") assert(mockDataBase.trendDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) val mediator = TrendMediator(mockDataBase, mockService, accountKey = accountKey, "1") val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) - mediator.load(LoadType.REFRESH, pagingState) + val result = mediator.load(LoadType.REFRESH, pagingState) // when mediator get data from service, it store to database assert(mockDataBase.trendDao().getPagingSource(accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert((result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking(Dispatchers.Main) { + val accountKey = MicroBlogKey.twitter("123") + mockService.errorMsg = "throw test error" + assert(mockDataBase.trendDao().getPagingSource(accountKey).collectDataForTest().isEmpty()) + val mediator = TrendMediator(mockDataBase, mockService, accountKey = accountKey, "1") + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) } } From 00a42a363d271ee05c02f64974b5bafc1333a351 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 12 Aug 2021 17:09:40 +0800 Subject: [PATCH 053/615] add test for PagingTimelinMediatorBase --- .../twiderex/jobs/status/MastodonVoteJob.kt | 6 +- .../viewmodel/timeline/TimelineViewModel.kt | 1 + .../worker/status/UpdateStatusWorker.kt | 2 +- .../com/twidere/twiderex/db/dao/StatusDao.kt | 8 +- .../mediator/paging/PagingWithGapMediator.kt | 2 +- .../twiderex/repository/StatusRepository.kt | 8 +- .../twiderex/mock/db/MockCacheDatabase.kt | 8 +- .../mock/db/dao/MockPagingTimelineDao.kt | 96 +++++++++++++++++++ .../twiderex/mock/db/dao/MockStatusDao.kt | 65 +++++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 16 ++++ .../twiderex/mock/service/ErrorService.kt | 26 ++++- .../twiderex/mock/service/MockListsService.kt | 1 - .../paging/PagingTimelineMediatorBaseTest.kt | 86 +++++++++++++++++ 13 files changed, 305 insertions(+), 20 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index 0bbd2f102..c86fb307c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -50,7 +50,7 @@ class MastodonVoteJob( val pollId = status.poll.id var originPoll: Poll? = null - statusRepository.updateStatus(statusKey = status.statusKey) { + statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { it.extra = it.extra.fromJson() .let { extra -> originPoll = extra.poll @@ -64,13 +64,13 @@ class MastodonVoteJob( } try { val newPoll = service.vote(pollId, votes) - statusRepository.updateStatus(statusKey = status.statusKey) { + statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { it.extra = it.extra.fromJson().copy( poll = newPoll ).json() } } catch (e: Throwable) { - statusRepository.updateStatus(statusKey = status.statusKey) { + statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { it.extra = it.extra.fromJson().copy( poll = originPoll ).json() diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 5758d23ab..6f04885a3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -30,6 +30,7 @@ import com.twidere.twiderex.extensions.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager +import com.twidere.twiderex.paging.mediator.paging.toUi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index 13403f537..d8c186c3f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -67,7 +67,7 @@ class UpdateStatusWorker @AssistedInject constructor( val retweetCount = inputData.getNullableLong("retweetCount") val likeCount = inputData.getNullableLong("likeCount") repository.updateReaction(accountKey = accountKey, statusKey = statusKey, liked = liked, retweeted = retweeted) - statusRepository.updateStatus(statusKey = statusKey) { + statusRepository.updateStatus(statusKey = statusKey, accountKey = accountKey) { if (retweetCount != null) { it.retweetCount = retweetCount } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 7fe74cdc5..acde55326 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -26,14 +26,12 @@ import kotlinx.coroutines.flow.Flow // TODO OPERATION interface StatusDao { - suspend fun findWithStatusKey(it: MicroBlogKey): UiStatus? - suspend fun insertAll(it: List) - fun findWithStatusKeyWithReferenceFlow( + suspend fun insertAll(listOf: List) + suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? + fun findWithStatusKeyWithFlow( statusKey: MicroBlogKey, accountKey: MicroBlogKey ): Flow - - suspend fun findWithStatusKeyWithReference(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? suspend fun delete(statusKey: MicroBlogKey) suspend fun updateAction( statusKey: MicroBlogKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index c724912fb..f245f6200 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -98,7 +98,7 @@ abstract class PagingWithGapMediator( } try { val max_id = withContext(Dispatchers.IO) { - maxStatusKey?.let { database.statusDao().findWithStatusKey(it)?.statusId } + maxStatusKey?.let { database.statusDao().findWithStatusKey(it, accountKey)?.statusId } } val result = loadBetweenImpl(pageSize, max_id = max_id, since_id = null).let { list -> list.map { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index f34febe8c..316bdd5c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -46,15 +46,15 @@ class StatusRepository( statusKey: MicroBlogKey, accountKey: MicroBlogKey ): Flow { - return database.statusDao().findWithStatusKeyWithReferenceFlow(statusKey, accountKey) + return database.statusDao().findWithStatusKeyWithFlow(statusKey, accountKey) } suspend fun loadFromCache(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? { - return database.statusDao().findWithStatusKeyWithReference(statusKey, accountKey) + return database.statusDao().findWithStatusKey(statusKey, accountKey) } - suspend fun updateStatus(statusKey: MicroBlogKey, action: (UiStatus) -> UiStatus) { - database.statusDao().findWithStatusKey(statusKey)?.let { + suspend fun updateStatus(statusKey: MicroBlogKey, accountKey: MicroBlogKey, action: (UiStatus) -> UiStatus) { + database.statusDao().findWithStatusKey(statusKey, accountKey = accountKey)?.let { database.statusDao().insertAll(listOf(action.invoke(it))) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 4fc5ffa3a..05a2f337f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -32,12 +32,15 @@ import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.UserDao import com.twidere.twiderex.mock.db.dao.MockListsDao import com.twidere.twiderex.mock.db.dao.MockMediaDao +import com.twidere.twiderex.mock.db.dao.MockPagingTimelineDao +import com.twidere.twiderex.mock.db.dao.MockStatusDao import com.twidere.twiderex.mock.db.dao.MockTrendDao import org.jetbrains.annotations.TestOnly internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { + private val statusDao = MockStatusDao() override fun statusDao(): StatusDao { - TODO("Not yet implemented") + return statusDao } override fun mediaDao(): MediaDao { @@ -48,8 +51,9 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { TODO("Not yet implemented") } + private val pagingTimelineDao = MockPagingTimelineDao(statusDao) override fun pagingTimelineDao(): PagingTimelineDao { - TODO("Not yet implemented") + return pagingTimelineDao } private val listsDao = MockListsDao() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt new file mode 100644 index 000000000..30109e26a --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt @@ -0,0 +1,96 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.PagingTimelineDao +import com.twidere.twiderex.db.dao.StatusDao +import com.twidere.twiderex.mock.paging.MockPagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import kotlinx.coroutines.runBlocking +import org.jetbrains.annotations.TestOnly + +internal class MockPagingTimelineDao @TestOnly constructor(private val statusDao: StatusDao) : PagingTimelineDao { + private val fakeDb = mutableMapOf>() + + override fun getPagingSource( + pagingKey: String, + accountKey: MicroBlogKey + ): PagingSource { + return MockPagingSource( + fakeDb[pagingKey]?.mapNotNull { + if (it.accountKey != accountKey) null else { + runBlocking { + statusDao.findWithStatusKey(it.statusKey, accountKey = accountKey)?.let { status -> + PagingTimeLineWithStatus(timeline = it, status = status) + } + } + } + } ?: emptyList() + ) + } + + override suspend fun clearAll(pagingKey: String, accountKey: MicroBlogKey) { + fakeDb[pagingKey]?.removeAll { + it.accountKey == accountKey + } + } + + override suspend fun getLatest( + pagingKey: String, + accountKey: MicroBlogKey + ): PagingTimeLineWithStatus? { + return fakeDb[pagingKey]?.maxByOrNull { it.timestamp }?.let { + statusDao.findWithStatusKey(it.statusKey, accountKey = accountKey)?.let { status -> + PagingTimeLineWithStatus(timeline = it, status = status) + } + } + } + + override suspend fun findWithStatusKey( + maxStatusKey: MicroBlogKey, + accountKey: MicroBlogKey + ): PagingTimeLine? { + return fakeDb.values.flatten().find { + it.statusKey == maxStatusKey && it.accountKey == accountKey + } + } + + override suspend fun insertAll(listOf: List) { + listOf.forEach { timeline -> + fakeDb[timeline.pagingKey].let { + if (it.isNullOrEmpty()) { + fakeDb[timeline.pagingKey] = mutableListOf(timeline) + } else { + it.add(timeline) + } + } + } + } + + override suspend fun delete(statusKey: MicroBlogKey) { + return fakeDb.values.forEach { + it.removeAll { timeline -> timeline.statusKey == statusKey } + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt new file mode 100644 index 000000000..1dfe5c7fd --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt @@ -0,0 +1,65 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import com.twidere.twiderex.db.dao.StatusDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiStatus +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.jetbrains.annotations.TestOnly + +internal class MockStatusDao @TestOnly constructor() : StatusDao { + private val fakeDb = mutableMapOf() + override suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? { + return fakeDb[statusKey] + } + + override suspend fun insertAll(listOf: List) { + listOf.forEach { status -> + fakeDb[status.statusKey] = status + } + } + + override fun findWithStatusKeyWithFlow( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey + ): Flow { + return flow { + emit(findWithStatusKey(statusKey, accountKey)) + } + } + + override suspend fun delete(statusKey: MicroBlogKey) { + fakeDb.remove(statusKey) + } + + override suspend fun updateAction( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + liked: Boolean?, + retweet: Boolean? + ) { + fakeDb[statusKey]?.let { + fakeDb[statusKey] = it.copy(liked = liked ?: it.liked, retweeted = retweet ?: it.retweeted) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 1975d0de4..390e67646 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -23,11 +23,14 @@ package com.twidere.twiderex.mock.model import com.twidere.services.mastodon.model.Trend import com.twidere.services.mastodon.model.TrendHistory import com.twidere.services.microblog.model.IListModel +import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.StatusV2 import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging import com.twidere.services.twitter.model.User +import com.twidere.services.twitter.model.UserV2 import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia @@ -101,3 +104,16 @@ internal fun mockIListModel( description = description, ) } + +@TestOnly +internal fun mockIStatus(): IStatus { + val authorId = System.currentTimeMillis().toString() + return StatusV2( + id = System.currentTimeMillis().toString(), + authorID = authorId, + ).apply { + user = UserV2( + id = authorId, + ) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt index 3bce547fd..394093584 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt @@ -1,11 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.mock.service import com.twidere.services.twitter.model.exceptions.TwitterApiException internal open class ErrorService { - var errorMsg:String? = null + var errorMsg: String? = null - fun checkError(){ + fun checkError() { if (!errorMsg.isNullOrEmpty()) throw TwitterApiException(errorMsg) } -} \ No newline at end of file +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index 1c37de0fa..c0ee8338b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -25,7 +25,6 @@ import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.TwitterList -import com.twidere.services.twitter.model.exceptions.TwitterApiException import com.twidere.twiderex.mock.model.mockIListModel import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.mock.model.toIPaging diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt new file mode 100644 index 000000000..a43159ce3 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt @@ -0,0 +1,86 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.paging + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.services.microblog.model.IPaging +import com.twidere.services.microblog.model.IStatus +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.model.toIPaging +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.IPagination +import com.twidere.twiderex.paging.mediator.paging.PagingTimelineMediatorBase +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class PagingTimelineMediatorBaseTest { + @ExperimentalPagingApi + @Test + fun refresh_whenReturnedSuccessSaveResultToDatabase() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val database = MockCacheDatabase() + val mediator = MockPagingTimelineMediatorBase( + accountKey = accountKey, + database = database + ) + assert(database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest().isEmpty()) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } +} + +internal class MockPagingTimelineMediatorBase( + accountKey: MicroBlogKey, + database: CacheDatabase +) : PagingTimelineMediatorBase(accountKey = accountKey, database = database) { + class MockPagination( + val nextKey: String? + ) : IPagination + + override val pagingKey: String + get() = "Mock paging key" + + override fun provideNextPage( + raw: List, + result: List + ): MockPagination { + return if (raw is IPaging) { + MockPagination(raw.nextPage) + } else { + MockPagination(null) + } + } + + override suspend fun load(pageSize: Int, paging: MockPagination?): List { + return listOf(mockIStatus()).toIPaging() + } +} From 52e5194d2a156a89cdd32b199d4e21686374a109 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 12 Aug 2021 17:53:44 +0800 Subject: [PATCH 054/615] add test for PagingWithGapMediator --- .../mock/db/dao/MockPagingTimelineDao.kt | 4 +- .../twidere/twiderex/mock/model/MockModels.kt | 15 ++- .../paging/PagingTimelineMediatorBaseTest.kt | 38 +++++- .../paging/PagingWithGapMediatorTest.kt | 123 ++++++++++++++++++ 4 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt index 30109e26a..7a3554408 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt @@ -42,7 +42,7 @@ internal class MockPagingTimelineDao @TestOnly constructor(private val statusDao if (it.accountKey != accountKey) null else { runBlocking { statusDao.findWithStatusKey(it.statusKey, accountKey = accountKey)?.let { status -> - PagingTimeLineWithStatus(timeline = it, status = status) + PagingTimeLineWithStatus(timeline = it, status = status.copy(isGap = it.isGap)) } } } @@ -62,7 +62,7 @@ internal class MockPagingTimelineDao @TestOnly constructor(private val statusDao ): PagingTimeLineWithStatus? { return fakeDb[pagingKey]?.maxByOrNull { it.timestamp }?.let { statusDao.findWithStatusKey(it.statusKey, accountKey = accountKey)?.let { status -> - PagingTimeLineWithStatus(timeline = it, status = status) + PagingTimeLineWithStatus(timeline = it, status = status.copy(isGap = it.isGap)) } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 390e67646..ecc18b8f8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -29,13 +29,13 @@ import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.StatusV2 import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging -import com.twidere.services.twitter.model.User import com.twidere.services.twitter.model.UserV2 import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch import org.jetbrains.annotations.TestOnly +import java.util.Date import java.util.UUID @TestOnly @@ -66,11 +66,9 @@ internal fun List.toIPaging(nextPaging: String? = UUID.randomUUID().toStr ) @TestOnly -internal fun mockIUser(): IUser { - val id = System.currentTimeMillis() - return User( +internal fun mockIUser(id: String = System.currentTimeMillis().toString()): IUser { + return UserV2( id = id, - idStr = id.toString(), ) } @@ -79,6 +77,7 @@ internal fun mockITrend(): ITrend { return Trend( name = "trend timestamp:${System.currentTimeMillis()}", url = "https://trend", + history = mutableListOf( TrendHistory( accounts = "1", @@ -102,15 +101,17 @@ internal fun mockIListModel( name = name, mode = mode, description = description, + createdAt = Date().apply { time = System.currentTimeMillis() } ) } @TestOnly -internal fun mockIStatus(): IStatus { +internal fun mockIStatus(id: String = System.currentTimeMillis().toString()): IStatus { val authorId = System.currentTimeMillis().toString() return StatusV2( - id = System.currentTimeMillis().toString(), + id = id, authorID = authorId, + createdAt = Date().apply { time = System.currentTimeMillis() } ).apply { user = UserV2( id = authorId, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt index a43159ce3..372a7c52c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt @@ -38,6 +38,7 @@ import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.mediator.paging.PagingTimelineMediatorBase import kotlinx.coroutines.runBlocking import org.junit.Test +import kotlin.test.assertEquals internal class PagingTimelineMediatorBaseTest { @ExperimentalPagingApi @@ -56,9 +57,27 @@ internal class PagingTimelineMediatorBaseTest { assert(result is RemoteMediator.MediatorResult.Success) assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_SaveTransformedDataIfTransformed() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val database = MockCacheDatabase() + val mediator = MockTransformPagingTimelineMediatorBase( + accountKey = accountKey, + database = database + ) + assert(database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest().isEmpty()) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + val timelines = database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest() + assertEquals("transformed text", timelines.first().status.rawText) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } } -internal class MockPagingTimelineMediatorBase( +internal open class MockPagingTimelineMediatorBase( accountKey: MicroBlogKey, database: CacheDatabase ) : PagingTimelineMediatorBase(accountKey = accountKey, database = database) { @@ -84,3 +103,20 @@ internal class MockPagingTimelineMediatorBase( return listOf(mockIStatus()).toIPaging() } } + +internal class MockTransformPagingTimelineMediatorBase( + accountKey: MicroBlogKey, + database: CacheDatabase +) : MockPagingTimelineMediatorBase( + accountKey = accountKey, database = database +) { + override fun transform( + state: PagingState, + data: List, + list: List + ): List { + return data.map { + it.copy(status = it.status.copy(rawText = "transformed text")) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt new file mode 100644 index 000000000..7408ad9da --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt @@ -0,0 +1,123 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.paging + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.services.microblog.model.IStatus +import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.model.toIPaging +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.paging.saveToDb +import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class PagingWithGapMediatorTest { + @ExperimentalPagingApi + @Test + fun refresh_whenReturnedSuccessSaveResultToDatabase() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val database = MockCacheDatabase() + val pagingKey = "Mock Test" + val mediator = MockPagingGapMediator( + accountKey = accountKey, + database = database, + statusId = System.currentTimeMillis().toString(), + pagingKey = pagingKey + ) + assert(database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest().isEmpty()) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(database.pagingTimelineDao().getPagingSource(mediator.pagingKey, accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @ExperimentalPagingApi + @Test + fun refresh_whenReturnedResultNotInDatabaseIsGapShouldBeTrue() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val pagingKey = "Mock Paging Key" + val database = MockCacheDatabase() + val sinceId = "sinceId" + listOf(mockIStatus(sinceId).toPagingTimeline(accountKey, pagingKey)).saveToDb(database) + val mediator = MockPagingGapMediator( + accountKey = accountKey, + database = database, + statusId = "newId", + pagingKey = pagingKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + mediator.load(LoadType.REFRESH, pagingState) + val result = database.pagingTimelineDao().getLatest(pagingKey, accountKey) + assertEquals(true, result?.timeline?.isGap) + assertEquals(true, result?.status?.isGap) + } + + @ExperimentalPagingApi + @Test + fun refresh_whenReturnedResultInDatabaseIsGapShouldBeFalse() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val pagingKey = "Mock Paging Key" + val database = MockCacheDatabase() + val sinceId = "sinceId" + listOf(mockIStatus(sinceId).toPagingTimeline(accountKey, pagingKey)).saveToDb(database) + val mediator = MockPagingGapMediator( + accountKey = accountKey, + database = database, + statusId = sinceId, + pagingKey = pagingKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + mediator.load(LoadType.REFRESH, pagingState) + val result = database.pagingTimelineDao().getLatest(pagingKey, accountKey) + assertEquals(false, result?.timeline?.isGap) + assertEquals(false, result?.status?.isGap) + } +} + +internal class MockPagingGapMediator( + accountKey: MicroBlogKey, + database: CacheDatabase, + private val statusId: String, + override val pagingKey: String +) : PagingWithGapMediator( + accountKey = accountKey, + database = database +) { + override suspend fun loadBetweenImpl( + pageSize: Int, + max_id: String?, + since_id: String? + ): List { + return listOf(mockIStatus(statusId)).toIPaging() + } +} From f9445239eabf30d659cd6612b57715090b9df4fc Mon Sep 17 00:00:00 2001 From: huixing Date: Thu, 12 Aug 2021 20:04:57 +0800 Subject: [PATCH 055/615] full strategy for optimized video --- .../component/foundation/VideoPlayer.kt | 48 +++++++++++++++---- .../com/twidere/twiderex/scenes/MediaScene.kt | 3 +- .../twidere/twiderex/utils/video/VideoPool.kt | 46 ++++++++++++++++++ 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index dea0d2c21..8821d4062 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -68,6 +69,9 @@ import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.utils.video.CacheDataSourceFactory import com.twidere.twiderex.utils.video.VideoPool +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable fun VideoPlayer( @@ -78,6 +82,7 @@ fun VideoPlayer( showControls: Boolean = customControl == null, zOrderMediaOverlay: Boolean = false, keepScreenOn: Boolean = false, + isListItem: Boolean = true, thumb: @Composable (() -> Unit)? = null, ) { var playing by remember { mutableStateOf(false) } @@ -174,24 +179,41 @@ fun VideoPlayer( onDispose { updateState() player.release() + VideoPool.removeRect(url) lifecycle.removeObserver(observer) } } - var isInScreen by remember { + var middleLine = 0.0f + val composableScope = rememberCoroutineScope() + + var isMostCenter by remember(url) { mutableStateOf(false) } - + var debounceJob: Job? = null AndroidView( modifier = modifier.onGloballyPositioned { coordinates -> + if (middleLine == 0.0f) { + var rootCoordinates = coordinates + while (rootCoordinates.parentCoordinates != null) { + rootCoordinates = rootCoordinates.parentCoordinates!! + } + rootCoordinates.boundsInWindow().run { + middleLine = (top + bottom) / 2 + } + } coordinates.boundsInWindow().run { - if (isInScreen && top <= 0 && bottom <= 0) { - isInScreen = false - updateState() - player.playWhenReady = false - } else if (!isInScreen && top > 0 && bottom > 0) { - isInScreen = true - player.playWhenReady = autoPlay + VideoPool.setRect(url, this) + if (!isMostCenter && VideoPool.containsMiddleLine(url, middleLine)) { + debounceJob?.cancel() + debounceJob = composableScope.launch { + delay(VideoPool.DEBOUNCE_DELAY) + if (VideoPool.containsMiddleLine(url, middleLine)) { + isMostCenter = true + } + } + } else if (isMostCenter && !VideoPool.isMostCenter(url, middleLine)) { + isMostCenter = false } } }, @@ -204,9 +226,15 @@ fun VideoPlayer( } ) { it.player = player - if (isResume && isInScreen) { + if (isResume && isMostCenter) { + if (isListItem) { + player.playWhenReady = autoPlay + } it.onResume() } else { + if (isListItem) { + player.playWhenReady = false + } it.onPause() } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 90e05382a..49d082d5f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -446,7 +446,8 @@ fun MediaView( customControl = customControl, showControls = false, zOrderMediaOverlay = true, - keepScreenOn = true + keepScreenOn = true, + isListItem = false ) } MediaType.other -> Unit diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt index e919d7e37..bad2376f0 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt @@ -20,9 +20,14 @@ */ package com.twidere.twiderex.utils.video +import androidx.compose.ui.geometry.Rect import java.util.concurrent.ConcurrentHashMap +import kotlin.math.abs object VideoPool { + + const val DEBOUNCE_DELAY = 500L + private val pool = ConcurrentHashMap() fun get(url: String): Long { @@ -32,4 +37,45 @@ object VideoPool { fun set(url: String, position: Long) { pool[url] = position } + + private val positionPool = ConcurrentHashMap() + + fun setRect(url: String, rect: Rect) { + if (rect.top <= 0.0f && rect.bottom <= 0.0f) { + removeRect(url) + } else { + positionPool[url] = rect + } + } + + fun removeRect(url: String) { + positionPool.remove(url) + } + + fun containsMiddleLine(url: String, middle: Float): Boolean { + positionPool[url]?.let { + return it.top <= middle && it.bottom >= middle + } + return false + } + + fun isMostCenter(url: String, middle: Float): Boolean { + if (positionPool.size == 0) { + return false + } + if (positionPool.size == 1) { + return true + } + var centerUrl = url + var minGap = Float.MAX_VALUE + positionPool.forEach { + abs((it.value.top + it.value.bottom) / 2 - middle).let { curGap -> + if (curGap < minGap) { + minGap = curGap + centerUrl = it.key + } + } + } + return url == centerUrl + } } From 75f374eb43e6481091160c374dcf2d638e5762cb Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 13 Aug 2021 16:29:38 +0800 Subject: [PATCH 056/615] add test for DM mediators --- .../twiderex/paging/dm/DMEventMediatorTest.kt | 96 ------------- .../twiderex/mock/service/MockListsService.kt | 128 ------------------ .../twiderex/mock/service/MockTrendService.kt | 46 ------- .../db/dao/DirectMessageConversationDao.kt | 2 +- .../twiderex/mock/db/MockCacheDatabase.kt | 12 +- .../dao/MockDirectMessageConversationDao.kt | 86 ++++++++++++ .../mock/db/dao/MockDirectMessageEventDao.kt | 79 +++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 21 +++ .../mock/service/MockDirectMessageService.kt | 54 ++++++++ .../paging/dm/DMConversationMediatorTest.kt | 64 +++++++++ .../twiderex/paging/dm/DMEventMediatorTest.kt | 64 +++++++++ 11 files changed, 377 insertions(+), 275 deletions(-) delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt diff --git a/android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt b/android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt deleted file mode 100644 index 3809585ea..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.paging.dm - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.paging.ExperimentalPagingApi -import androidx.paging.LoadType -import androidx.paging.PagingConfig -import androidx.paging.PagingState -import androidx.paging.RemoteMediator -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage -import com.twidere.twiderex.mock.MockDirectMessageService -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.paging.mediator.dm.DMConversationMediator -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.concurrent.Executors - -/** - * instead of testing pagination, we should focus on our code logic - */ - -@RunWith(AndroidJUnit4::class) -class DMEventMediatorTest { - private lateinit var mockDataBase: CacheDatabase - - private var mockService = MockDirectMessageService() - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - mockDataBase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), CacheDatabase::class.java) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() - } - - @After - fun tearDown() { - mockDataBase.clearAllTables() - } - - @OptIn(ExperimentalPagingApi::class) - @Test - fun refresh_LoadReturnsSuccessResultWhenSuccess() { - runBlocking { - mockService.add(mockService.generateDirectMessage(20, System.currentTimeMillis().toString(), "123")) - Assert.assertEquals(0, mockDataBase.listsDao().findAll()?.size) - val mediator = DMConversationMediator(mockDataBase, accountKey = MicroBlogKey.twitter("123")) { - mockService.getDirectMessages(it, 50) - } - val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) - val result = mediator.load(LoadType.REFRESH, pagingState) - assert(result is RemoteMediator.MediatorResult.Success) - assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) - } - } - - @OptIn(ExperimentalPagingApi::class) - @Test - fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { - mockService.errorMsg = "Throw test failure" - val mediator = DMConversationMediator(mockDataBase, accountKey = MicroBlogKey.twitter("123"),) { - mockService.getDirectMessages(it, 50) - } - val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) - val result = mediator.load(LoadType.REFRESH, pagingState) - assert(result is RemoteMediator.MediatorResult.Error) - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt b/android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt deleted file mode 100644 index fa38bc28e..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/service/MockListsService.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.service - -import com.twidere.services.microblog.ListsService -import com.twidere.services.microblog.MicroBlogService -import com.twidere.services.microblog.model.IListModel -import com.twidere.services.microblog.model.IUser -import com.twidere.services.twitter.model.TwitterList -import com.twidere.services.twitter.model.exceptions.TwitterApiException - -class MockListsService : ListsService, MicroBlogService { - override suspend fun lists( - userId: String?, - screenName: String?, - reverse: Boolean - ): List { - val list = mutableListOf() - for (i in 0 until 20) { - val id = System.currentTimeMillis() - list.add( - TwitterList( - id = id, - idStr = id.toString(), - name = "list $i", - ) - ) - } - return list - } - - override suspend fun createList( - name: String, - mode: String?, - description: String?, - repliesPolicy: String? - ): IListModel { - if (name == "error") throw TwitterApiException(error = "throw exception intentional") - val id = System.currentTimeMillis() - return TwitterList( - id = id, - idStr = id.toString(), - name = name, - description = description, - mode = mode, - ) - } - - override suspend fun updateList( - listId: String, - name: String?, - mode: String?, - description: String?, - repliesPolicy: String? - ): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - return TwitterList( - id = listId.toLong(), - idStr = listId, - name = name, - mode = mode, - description = description, - ) - } - - override suspend fun destroyList(listId: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - // do nothing - } - - override suspend fun listMembers(listId: String, count: Int, cursor: String?): List { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - TODO("Not yet implemented") - } - - override suspend fun addMember(listId: String, userId: String, screenName: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - // do nothing - } - - override suspend fun removeMember(listId: String, userId: String, screenName: String) { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - // do nothing - } - - override suspend fun listSubscribers(listId: String, count: Int, cursor: String?): List { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - TODO("Not yet implemented") - } - - override suspend fun unsubscribeList(listId: String): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - return TwitterList( - id = listId.toLong(), - idStr = listId, - name = "", - following = false - ) - } - - override suspend fun subscribeList(listId: String): IListModel { - if (listId == "error") throw TwitterApiException(error = "throw exception intentional") - return TwitterList( - id = listId.toLong(), - idStr = listId, - name = "", - following = true - ) - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt b/android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt deleted file mode 100644 index 9776f8178..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/service/MockTrendService.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.service - -import com.twidere.services.mastodon.model.Trend -import com.twidere.services.mastodon.model.TrendHistory -import com.twidere.services.microblog.MicroBlogService -import com.twidere.services.microblog.TrendService -import com.twidere.services.microblog.model.ITrend -import java.lang.IllegalArgumentException - -class MockTrendService : TrendService, MicroBlogService { - override suspend fun trends(locationId: String, limit: Int?): List { - return if (locationId == "error") throw IllegalArgumentException("service error") else mutableListOf( - Trend( - name = "trend", - url = "https://trend", - history = mutableListOf( - TrendHistory( - accounts = "1", - uses = "1", - day = System.currentTimeMillis().toString() - ) - ) - ) - ) - } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt index ac1f6cf66..bb78ccf9f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt @@ -40,5 +40,5 @@ interface DirectMessageConversationDao { suspend fun insertAll(listOf: List) suspend fun find(accountKey: MicroBlogKey): List - suspend fun delete(it: UiDMConversation) + suspend fun delete(conversation: UiDMConversation) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 05a2f337f..9ad359fd2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -30,6 +30,8 @@ import com.twidere.twiderex.db.dao.PagingTimelineDao import com.twidere.twiderex.db.dao.StatusDao import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.mock.db.dao.MockDirectMessageConversationDao +import com.twidere.twiderex.mock.db.dao.MockDirectMessageEventDao import com.twidere.twiderex.mock.db.dao.MockListsDao import com.twidere.twiderex.mock.db.dao.MockMediaDao import com.twidere.twiderex.mock.db.dao.MockPagingTimelineDao @@ -70,12 +72,14 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { return trendDao } - override fun directMessageConversationDao(): DirectMessageConversationDao { - TODO("Not yet implemented") + private val dmDao = MockDirectMessageEventDao() + override fun directMessageDao(): DirectMessageEventDao { + return dmDao } - override fun directMessageDao(): DirectMessageEventDao { - TODO("Not yet implemented") + private val conversationDao = MockDirectMessageConversationDao(dmDao) + override fun directMessageConversationDao(): DirectMessageConversationDao { + return conversationDao } private var cleared = false diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt new file mode 100644 index 000000000..703529715 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt @@ -0,0 +1,86 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.DirectMessageConversationDao +import com.twidere.twiderex.mock.paging.MockPagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.runBlocking +import org.jetbrains.annotations.TestOnly + +internal class MockDirectMessageConversationDao @TestOnly constructor(private val eventDao: MockDirectMessageEventDao) : DirectMessageConversationDao { + private val fakeDb = mutableMapOf>() + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return runBlocking { + MockPagingSource( + data = find(accountKey) + ) + } + } + + override fun findWithConversationKeyFlow( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Flow { + return flow { + emit(findWithConversationKey(accountKey, conversationKey)) + } + } + + override suspend fun findWithConversationKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): UiDMConversation? { + return fakeDb[accountKey]?.find { it.conversationKey == conversationKey } + } + + override suspend fun insertAll(listOf: List) { + listOf.forEach { con -> + fakeDb[con.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[con.accountKey] = mutableListOf(con) + } else { + it.add(con) + } + } + } + } + + override suspend fun find(accountKey: MicroBlogKey): List { + return fakeDb[accountKey]?.mapNotNull { + eventDao.getLatestMessage(accountKey, it.conversationKey)?.let { event -> + UiDMConversationWithLatestMessage( + conversation = it, + latestMessage = event + ) + } + } ?: emptyList() + } + + override suspend fun delete(conversation: UiDMConversation) { + fakeDb[conversation.accountKey]?.removeAll { it.conversationKey == conversation.conversationKey } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt new file mode 100644 index 000000000..4f656cede --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt @@ -0,0 +1,79 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.mock.paging.MockPagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent +import org.jetbrains.annotations.TestOnly + +internal class MockDirectMessageEventDao @TestOnly constructor() : DirectMessageEventDao { + private val fakeDb = mutableMapOf>() + + fun getLatestMessage( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): UiDMEvent? { + return fakeDb[accountKey]?.filter { it.conversationKey == conversationKey }?.maxByOrNull { it.sortId } + } + + override fun getPagingSource( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): PagingSource { + return MockPagingSource( + data = fakeDb[accountKey]?.filter { it.conversationKey == conversationKey } ?: emptyList() + ) + } + + override suspend fun findWithMessageKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + messageKey: MicroBlogKey + ): UiDMEvent? { + return fakeDb[accountKey]?.find { it.conversationKey == conversationKey && it.messageKey == messageKey } + } + + override suspend fun delete(message: UiDMEvent) { + fakeDb[message.accountKey]?.removeAll { it.messageKey == message.messageKey } + } + + override suspend fun getMessageCount( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Long { + return fakeDb[accountKey]?.sumOf { if (it.conversationKey == conversationKey) 1L else 0L } ?: 0 + } + + override suspend fun insertAll(events: List) { + events.forEach { event -> + fakeDb[event.accountKey].let { + if (it.isNullOrEmpty()) { + fakeDb[event.accountKey] = mutableListOf(event) + } else { + it.add(event) + } + } + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index ecc18b8f8..d04685632 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -22,10 +22,15 @@ package com.twidere.twiderex.mock.model import com.twidere.services.mastodon.model.Trend import com.twidere.services.mastodon.model.TrendHistory +import com.twidere.services.microblog.model.IDirectMessage import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.DirectMessageEvent +import com.twidere.services.twitter.model.MessageCreate +import com.twidere.services.twitter.model.MessageData +import com.twidere.services.twitter.model.MessageTarget import com.twidere.services.twitter.model.StatusV2 import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging @@ -118,3 +123,19 @@ internal fun mockIStatus(id: String = System.currentTimeMillis().toString()): IS ) } } + +@TestOnly +internal fun mockIDirectMessage(id: String = System.currentTimeMillis().toString(), accountId: String, inCome: Boolean = true): IDirectMessage { + return DirectMessageEvent( + createdTimestamp = System.currentTimeMillis().toString(), + id = id, + type = "message_create", + messageCreate = MessageCreate( + messageData = MessageData(text = "mock message"), + senderId = if (inCome)UUID.randomUUID().toString() else accountId, + target = MessageTarget( + recipientId = if (inCome) accountId else UUID.randomUUID().toString() + ) + ) + ) +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt new file mode 100644 index 000000000..98b943de4 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt @@ -0,0 +1,54 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.DirectMessageService +import com.twidere.services.microblog.model.IDirectMessage +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.toIPaging +import com.twidere.twiderex.model.MicroBlogKey +import org.jetbrains.annotations.TestOnly + +internal class MockDirectMessageService @TestOnly constructor(private val accountKey: MicroBlogKey) : DirectMessageService, + ErrorService() { + private val deletedMessageId = mutableListOf() + + fun isDeleted(id: String) = deletedMessageId.contains(id) + + override suspend fun destroyDirectMessage(id: String) { + checkError() + deletedMessageId.add(id) + } + + override suspend fun getDirectMessages(cursor: String?, count: Int?): List { + checkError() + val list = mutableListOf() + for (i in 0 until (count ?: 1)) { + list.add(mockIDirectMessage(accountId = accountKey.id, inCome = i % 2 == 0)) + } + return list.toIPaging() + } + + override suspend fun showDirectMessage(id: String): IDirectMessage? { + checkError() + return mockIDirectMessage(id = id, accountId = accountKey.id) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt new file mode 100644 index 000000000..a36d59001 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt @@ -0,0 +1,64 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.dm + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockDirectMessageService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import com.twidere.twiderex.paging.mediator.dm.DMConversationMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class DMConversationMediatorTest { + private val accountKey = MicroBlogKey.twitter("123") + + private val mockDataBase = MockCacheDatabase() + + private var mockService = MockDirectMessageService(accountKey) + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessResultWhenSuccess() = runBlocking { + val mediator = DMConversationMediator(mockDataBase, accountKey = accountKey) { mockService.getDirectMessages(it, 50) } + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + mockService.errorMsg = "Throw test failure" + val mediator = DMConversationMediator(mockDataBase, accountKey = accountKey) { + mockService.getDirectMessages(it, 50) + } + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt new file mode 100644 index 000000000..240f17ce9 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt @@ -0,0 +1,64 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.dm + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockDirectMessageService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.paging.mediator.dm.DMEventMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +/** + * instead of testing pagination, we should focus on our code logic + */ + +class DMEventMediatorTest { + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessResultWhenSuccess() = runBlocking { + val accountKey = MicroBlogKey.twitter("123") + val mediator = DMEventMediator(conversationKey = MicroBlogKey.valueOf("conversation"), accountKey = accountKey, database = MockCacheDatabase()) { MockDirectMessageService(accountKey).getDirectMessages(it, 50) } + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val accountKey = MicroBlogKey.twitter("123") + val mediator = DMEventMediator(database = MockCacheDatabase(), accountKey = accountKey, conversationKey = MicroBlogKey.valueOf("conversation")) { + MockDirectMessageService(accountKey).apply { errorMsg = "throw test errors" }.getDirectMessages(it, 50) + } + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} From 2806171c5319888abdb005707fcc57c7870443f8 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 13 Aug 2021 17:03:53 +0800 Subject: [PATCH 057/615] add test for search mediators --- .../mediator/search/SearchMediaMediator.kt | 23 ++---- .../mock/service/MockSearchService.kt | 77 +++++++++++++++++++ .../paging/search/SearchMediaMediatorTest.kt | 73 ++++++++++++++++++ .../paging/search/SearchStatusMediatorTest.kt | 73 ++++++++++++++++++ .../services/mastodon/MastodonService.kt | 13 ++++ .../services/microblog/SearchService.kt | 6 ++ .../services/twitter/TwitterService.kt | 8 ++ 7 files changed, 257 insertions(+), 16 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt index 8c87c90d5..369bb7448 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt @@ -20,9 +20,8 @@ */ package com.twidere.twiderex.paging.mediator.search +import com.twidere.services.microblog.SearchService import com.twidere.services.microblog.model.IStatus -import com.twidere.services.twitter.TwitterService -import com.twidere.services.twitter.model.exceptions.TwitterApiExceptionV2 import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.CursorPagination @@ -33,23 +32,15 @@ class SearchMediaMediator( private val query: String, database: CacheDatabase, accountKey: MicroBlogKey, - private val service: TwitterService, + private val service: SearchService, ) : CursorPagingMediator(accountKey, database) { override val pagingKey = "search:$query:media" override suspend fun load(pageSize: Int, paging: CursorPagination?): List { - val result = try { - service.searchV2( - "$query has:media -is:retweet", - count = pageSize, - nextPage = paging?.cursor, - ) - } catch (e: TwitterApiExceptionV2) { - service.searchV1( - "$query filter:media -filter:retweets", - count = pageSize, - max_id = paging?.cursor - ) - } + val result = service.searchMedia( + query, + count = pageSize, + nextPage = paging?.cursor, + ) return CursorPagingResult(result.status, result.nextPage) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt new file mode 100644 index 000000000..3ee0289ff --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt @@ -0,0 +1,77 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.SearchService +import com.twidere.services.microblog.model.ISearchResponse +import com.twidere.services.microblog.model.IStatus +import com.twidere.services.microblog.model.IUser +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toIPaging +import org.jetbrains.annotations.TestOnly +import java.util.UUID + +internal class MockSearchService @TestOnly constructor() : SearchService, ErrorService() { + override suspend fun searchTweets( + query: String, + count: Int, + nextPage: String? + ): ISearchResponse { + checkError() + val list = mutableListOf() + val nextKey = UUID.randomUUID().toString() + for (i in 0 until count) { + list.add(mockIStatus()) + } + return MockSearchResponse( + nextPage = nextKey, + status = list + ) + } + + override suspend fun searchMedia( + query: String, + count: Int, + nextPage: String? + ): ISearchResponse { + return searchTweets(query, count, nextPage) + } + + override suspend fun searchUsers( + query: String, + page: Int?, + count: Int, + following: Boolean + ): List { + checkError() + val list = mutableListOf() + for (i in 0 until count) { + list.add(mockIUser()) + } + return list.toIPaging() + } +} + +internal class MockSearchResponse @TestOnly constructor( + override val nextPage: String?, + override val status: List +) : ISearchResponse diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt new file mode 100644 index 000000000..b16847466 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.search + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockSearchService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class SearchMediaMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_saveToDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = SearchMediaMediator( + query = "test", + database = mockDataBase, + accountKey = accountKey, + service = MockSearchService() + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(mockDataBase.pagingTimelineDao().getPagingSource(pagingKey = mediator.pagingKey, accountKey = accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val mediator = SearchMediaMediator( + query = "test", + database = MockCacheDatabase(), + accountKey = accountKey, + service = MockSearchService().apply { + errorMsg = "throw test errors" + } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt new file mode 100644 index 000000000..4ebe1dc22 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.search + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockSearchService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class SearchStatusMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_saveToDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = SearchStatusMediator( + query = "test", + database = mockDataBase, + accountKey = accountKey, + service = MockSearchService() + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(mockDataBase.pagingTimelineDao().getPagingSource(pagingKey = mediator.pagingKey, accountKey = accountKey).collectDataForTest().isNotEmpty()) + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val mediator = SearchStatusMediator( + query = "test", + database = MockCacheDatabase(), + accountKey = accountKey, + service = MockSearchService().apply { + errorMsg = "throw test errors" + } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt index 46109d5fe..ac7fd7851 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt @@ -253,6 +253,19 @@ class MastodonService( ).accounts ?: emptyList() } + override suspend fun searchMedia( + query: String, + count: Int, + nextPage: String? + ): ISearchResponse { + return object : ISearchResponse { + override val nextPage: String? + get() = null + override val status: List + get() = emptyList() + } + } + suspend fun hashtagTimeline( query: String, count: Int? = null, diff --git a/services/src/main/java/com/twidere/services/microblog/SearchService.kt b/services/src/main/java/com/twidere/services/microblog/SearchService.kt index 26d7501cf..a63c68ef7 100644 --- a/services/src/main/java/com/twidere/services/microblog/SearchService.kt +++ b/services/src/main/java/com/twidere/services/microblog/SearchService.kt @@ -36,4 +36,10 @@ interface SearchService { count: Int = 20, following: Boolean = false ): List + + suspend fun searchMedia( + query: String, + count: Int = 20, + nextPage: String? = null, + ): ISearchResponse } diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 744a541d4..629c8cb36 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -324,6 +324,14 @@ class TwitterService( } } + override suspend fun searchMedia(query: String, count: Int, nextPage: String?): ISearchResponse { + return try { + searchV2("$query has:media -is:retweet", count = count, nextPage = nextPage) + } catch (e: TwitterApiExceptionV2) { + searchV1("$query filter:media -filter:retweets", count = count, max_id = nextPage) + } + } + suspend fun searchV2( query: String, count: Int, From c38261bc821fb4b099cc73fb51a52baca7db076b Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 13 Aug 2021 18:00:10 +0800 Subject: [PATCH 058/615] add test for timeline and user mediators --- .../timeline/MentionsTimelineViewModel.kt | 10 ++- .../timeline/NotificationTimelineViewModel.kt | 10 ++- .../timeline/MentionTimelineMediator.kt | 10 +-- .../timeline/NotificationTimelineMediator.kt | 10 +-- .../twidere/twiderex/mock/model/MockModels.kt | 39 +++++++- .../mock/service/MockNotificationService.kt | 42 +++++++++ .../mock/service/MockTimelineService.kt | 88 +++++++++++++++++++ .../timeline/HomeTimelineMediatorTest.kt | 70 +++++++++++++++ .../timeline/MentionTimelineMediatorTest.kt | 76 ++++++++++++++++ .../NotificationTimelineMediatorTest.kt | 76 ++++++++++++++++ .../timeline/UserFavoriteMediatorTest.kt | 75 ++++++++++++++++ .../paging/timeline/UserMediaMediatorTest.kt | 72 +++++++++++++++ .../paging/timeline/UserStatusMediatorTest.kt | 72 +++++++++++++++ .../services/mastodon/MastodonService.kt | 10 +-- 14 files changed, 635 insertions(+), 25 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index bfbe4699d..ae03bd1eb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -24,6 +24,7 @@ import android.content.SharedPreferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.NotificationRepository @@ -46,7 +47,14 @@ class MentionsTimelineViewModel @AssistedInject constructor( service = account.service as TimelineService, accountKey = account.accountKey, database = database, - notificationRepository = notificationRepository + addCursorIfNeed = { data, accountKey -> + notificationRepository.addCursorIfNeeded( + accountKey, + NotificationCursorType.Mentions, + data.status.statusId, + data.status.timestamp, + ) + } ) override val savedStateKey: String = "${account.accountKey}_mentions" } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 89ab75921..0361cba34 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -24,6 +24,7 @@ import android.content.SharedPreferences import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.NotificationRepository @@ -46,7 +47,14 @@ class NotificationTimelineViewModel @AssistedInject constructor( service = account.service as NotificationService, accountKey = account.accountKey, database = database, - notificationRepository = notificationRepository, + addCursorIfNeed = { data, accountKey -> + notificationRepository.addCursorIfNeeded( + accountKey, + NotificationCursorType.General, + data.status.statusId, + data.status.timestamp + ) + } ) override val savedStateKey: String = "${account.accountKey}_notification" } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt index 184d4052f..396a228d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt @@ -24,14 +24,12 @@ import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator -import com.twidere.twiderex.repository.NotificationRepository class MentionTimelineMediator( private val service: TimelineService, - private val notificationRepository: NotificationRepository, + private val addCursorIfNeed: suspend (PagingTimeLineWithStatus, accountKey: MicroBlogKey) -> Unit, accountKey: MicroBlogKey, database: CacheDatabase, ) : PagingWithGapMediator(accountKey, database) { @@ -46,11 +44,9 @@ class MentionTimelineMediator( list: List ): List { if (data.any()) { - notificationRepository.addCursorIfNeeded( + addCursorIfNeed( + data.first(), accountKey, - NotificationCursorType.Mentions, - data.first().status.statusId, - data.first().status.timestamp, ) } return super.transform(data, list) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt index ab4c6273b..348b045ea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt @@ -24,14 +24,12 @@ import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator -import com.twidere.twiderex.repository.NotificationRepository class NotificationTimelineMediator( private val service: NotificationService, - private val notificationRepository: NotificationRepository, + private val addCursorIfNeed: suspend (PagingTimeLineWithStatus, accountKey: MicroBlogKey) -> Unit, accountKey: MicroBlogKey, database: CacheDatabase, ) : PagingWithGapMediator(accountKey, database) { @@ -46,11 +44,9 @@ class NotificationTimelineMediator( list: List ): List { if (data.any()) { - notificationRepository.addCursorIfNeeded( + addCursorIfNeed( + data.first(), accountKey, - NotificationCursorType.General, - data.first().status.statusId, - data.first().status.timestamp, ) } return super.transform(data, list) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index d04685632..d400bbc85 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -20,14 +20,21 @@ */ package com.twidere.twiderex.mock.model +import com.twidere.services.mastodon.model.Account +import com.twidere.services.mastodon.model.Notification +import com.twidere.services.mastodon.model.NotificationTypes +import com.twidere.services.mastodon.model.Status import com.twidere.services.mastodon.model.Trend import com.twidere.services.mastodon.model.TrendHistory import com.twidere.services.microblog.model.IDirectMessage import com.twidere.services.microblog.model.IListModel +import com.twidere.services.microblog.model.INotification import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.AttachmentsV2 import com.twidere.services.twitter.model.DirectMessageEvent +import com.twidere.services.twitter.model.MediaV2 import com.twidere.services.twitter.model.MessageCreate import com.twidere.services.twitter.model.MessageData import com.twidere.services.twitter.model.MessageTarget @@ -111,12 +118,18 @@ internal fun mockIListModel( } @TestOnly -internal fun mockIStatus(id: String = System.currentTimeMillis().toString()): IStatus { - val authorId = System.currentTimeMillis().toString() +internal fun mockIStatus( + id: String = System.currentTimeMillis().toString(), + hasMedia: Boolean = false, + authorId: String = System.currentTimeMillis().toString() +): IStatus { return StatusV2( id = id, authorID = authorId, - createdAt = Date().apply { time = System.currentTimeMillis() } + createdAt = Date().apply { time = System.currentTimeMillis() }, + attachments = if (hasMedia) AttachmentsV2(mediaKeys = listOf("mediaKey")).apply { + media = listOf(MediaV2(url = "mediaUrl")) + } else null ).apply { user = UserV2( id = authorId, @@ -124,6 +137,26 @@ internal fun mockIStatus(id: String = System.currentTimeMillis().toString()): IS } } +@TestOnly +internal fun mockINotification(id: String = System.currentTimeMillis().toString()): INotification { + val account = Account( + id = System.currentTimeMillis().toString(), + username = "", + displayName = "", + acct = "" + ) + return Notification( + id = id, + type = NotificationTypes.status, + createdAt = Date().apply { time = System.currentTimeMillis() }, + account = account, + status = Status( + id = id, + account = account + ) + ) +} + @TestOnly internal fun mockIDirectMessage(id: String = System.currentTimeMillis().toString(), accountId: String, inCome: Boolean = true): IDirectMessage { return DirectMessageEvent( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt new file mode 100644 index 000000000..f6aded351 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt @@ -0,0 +1,42 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.NotificationService +import com.twidere.services.microblog.model.INotification +import com.twidere.twiderex.mock.model.mockINotification +import com.twidere.twiderex.mock.model.toIPaging +import org.jetbrains.annotations.TestOnly + +internal class MockNotificationService @TestOnly constructor() : NotificationService, ErrorService() { + override suspend fun notificationTimeline( + count: Int, + since_id: String?, + max_id: String? + ): List { + checkError() + val list = mutableListOf() + for (i in 0 until count) { + list.add(mockINotification()) + } + return list.toIPaging() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt new file mode 100644 index 000000000..edadb7118 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt @@ -0,0 +1,88 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.TimelineService +import com.twidere.services.microblog.model.IStatus +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.model.toIPaging +import org.jetbrains.annotations.TestOnly + +internal class MockTimelineService @TestOnly constructor() : TimelineService, ErrorService() { + override suspend fun favorites( + user_id: String, + count: Int, + since_id: String?, + max_id: String? + ): List { + checkError() + return generateData(count) + } + + override suspend fun homeTimeline( + count: Int, + since_id: String?, + max_id: String? + ): List { + checkError() + return generateData(count) + } + + override suspend fun listTimeline( + list_id: String, + count: Int, + max_id: String?, + since_id: String? + ): List { + checkError() + return generateData(count) + } + + override suspend fun mentionsTimeline( + count: Int, + since_id: String?, + max_id: String? + ): List { + checkError() + return generateData(count) + } + + override suspend fun userTimeline( + user_id: String, + count: Int, + since_id: String?, + max_id: String?, + exclude_replies: Boolean + ): List { + checkError() + return generateData(count) { + mockIStatus(hasMedia = true, authorId = user_id) + } + } + + private fun generateData(count: Int, create: () -> IStatus = { mockIStatus() }): List { + val list = mutableListOf() + for (i in 0 until count) { + list.add(create()) + } + return list.toIPaging() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt new file mode 100644 index 000000000..faf2fa82e --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class HomeTimelineMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = HomeTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService() + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = HomeTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService().apply { + errorMsg = "throw test errors" + } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt new file mode 100644 index 000000000..bef3f6e4c --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt @@ -0,0 +1,76 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class MentionTimelineMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + var addCursor: PagingTimeLineWithStatus? = null + val mediator = MentionTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService(), + addCursorIfNeed = { data, _ -> + addCursor = data + } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + assert(addCursor != null) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = MentionTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService().apply { + errorMsg = "throw test errors" + }, + addCursorIfNeed = { _, _ -> } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt new file mode 100644 index 000000000..78f27a56d --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt @@ -0,0 +1,76 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockNotificationService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class NotificationTimelineMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + var addCursor: PagingTimeLineWithStatus? = null + val mediator = NotificationTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockNotificationService(), + addCursorIfNeed = { data, _ -> + addCursor = data + } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + assert(addCursor != null) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = NotificationTimelineMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockNotificationService().apply { + errorMsg = "throw test errors" + }, + addCursorIfNeed = { _, _ -> } + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt new file mode 100644 index 000000000..7b63452a8 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt @@ -0,0 +1,75 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.user.UserFavouriteMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class UserFavoriteMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserFavouriteMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService(), + userKey = accountKey, + platformType = PlatformType.Twitter + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserFavouriteMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService().apply { + errorMsg = "throw test errors" + }, + userKey = accountKey, + platformType = PlatformType.Twitter + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt new file mode 100644 index 000000000..b105835e8 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt @@ -0,0 +1,72 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.user.UserMediaMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class UserMediaMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserMediaMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService(), + userKey = accountKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserMediaMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService().apply { + errorMsg = "throw test errors" + }, + userKey = accountKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt new file mode 100644 index 000000000..b2eae44b0 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt @@ -0,0 +1,72 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.timeline + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingConfig +import androidx.paging.PagingState +import androidx.paging.RemoteMediator +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.paging.mediator.user.UserStatusMediator +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class UserStatusMediatorTest { + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsSuccessDatabaseWhenSuccess() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserStatusMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService(), + userKey = accountKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + // when mediator get data from service, it store to database\ + assert(result is RemoteMediator.MediatorResult.Success) + assert(!(result as RemoteMediator.MediatorResult.Success).endOfPaginationReached) + } + + @OptIn(ExperimentalPagingApi::class) + @Test + fun refresh_LoadReturnsErrorResultWhenErrorOccurs() = runBlocking { + val mockDataBase = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val mediator = UserStatusMediator( + database = mockDataBase, + accountKey = accountKey, + service = MockTimelineService().apply { + errorMsg = "throw test errors" + }, + userKey = accountKey + ) + val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) + val result = mediator.load(LoadType.REFRESH, pagingState) + assert(result is RemoteMediator.MediatorResult.Error) + } +} diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt index ac7fd7851..ece878ed8 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt @@ -258,12 +258,10 @@ class MastodonService( count: Int, nextPage: String? ): ISearchResponse { - return object : ISearchResponse { - override val nextPage: String? - get() = null - override val status: List - get() = emptyList() - } + return BasicSearchResponse( + nextPage = null, + status = emptyList() + ) } suspend fun hashtagTimeline( From 28965190d832e22da2a06226055a2884938d841a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 16 Aug 2021 17:42:25 +0800 Subject: [PATCH 059/615] add test for paging sources --- .../paging/source/UserPagingSource.kt | 2 +- .../twiderex/mock/service/MockListsService.kt | 11 ++- .../mock/service/MockRelationshipService.kt | 73 +++++++++++++++++ .../mock/service/MockSearchService.kt | 16 ++-- .../source/FollowersPagingSourceTest.kt | 48 +++++++++++ .../source/FollowingPagingSourceTest.kt | 48 +++++++++++ .../ListsSubscribersPagingSourceTest.kt | 49 +++++++++++ .../source/SearchUserPagingSourceTest.kt | 82 +++++++++++++++++++ .../paging/source/UserPagingSourceTest.kt | 80 ++++++++++++++++++ 9 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt index d795bd280..18121672e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt @@ -48,7 +48,7 @@ abstract class UserPagingSource( null } LoadResult.Page(data = users, prevKey = null, nextKey = nextPage) - } catch (e: Exception) { + } catch (e: Throwable) { LoadResult.Error(e) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index c0ee8338b..1cea3100b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -25,9 +25,11 @@ import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.TwitterList +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.mock.model.mockIListModel import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.mock.model.toIPaging +import com.twidere.twiderex.model.MicroBlogKey internal class MockListsService : ListsService, MicroBlogService, ErrorService() { @@ -103,17 +105,24 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() // do nothing } + private val subscribers = mutableListOf() override suspend fun listSubscribers(listId: String, count: Int, cursor: String?): List { checkError() val list = mutableListOf() for (i in 0 until count) { list.add( - mockIUser() + mockIUser().also { + subscribers.add(it.toUi(MicroBlogKey.twitter("123")).id) + } ) } return list.toIPaging() } + fun isSubscribers(userId: String): Boolean { + return subscribers.contains(userId) + } + override suspend fun unsubscribeList(listId: String): IListModel { checkError() return TwitterList( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt new file mode 100644 index 000000000..dc24cc397 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.RelationshipService +import com.twidere.services.microblog.model.IRelationship +import com.twidere.services.microblog.model.IUser +import com.twidere.services.microblog.model.Relationship +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toIPaging +import org.jetbrains.annotations.TestOnly + +internal class MockRelationshipService @TestOnly constructor() : RelationshipService, + ErrorService() { + private val followings = mutableListOf() + private val followers = mutableListOf() + + override suspend fun follow(user_id: String) { + checkError() + followings.add(user_id) + } + + override suspend fun followers(user_id: String, nextPage: String?): List { + checkError() + val id = nextPage ?: System.currentTimeMillis().toString() + return listOf( + mockIUser(id = id).also { + followers.add(id) + } + ).toIPaging() + } + + override suspend fun following(user_id: String, nextPage: String?): List { + checkError() + val id = nextPage ?: System.currentTimeMillis().toString() + return listOf( + mockIUser(id = id).also { + followings.add(id) + } + ).toIPaging() + } + + override suspend fun showRelationship(target_id: String): IRelationship { + checkError() + return Relationship( + followedBy = followers.contains(target_id), + following = followings.contains(target_id), + ) + } + + override suspend fun unfollow(user_id: String) { + checkError() + followings.remove(user_id) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt index 3ee0289ff..b1784ba8e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.mock.model.toIPaging import org.jetbrains.annotations.TestOnly import java.util.UUID -internal class MockSearchService @TestOnly constructor() : SearchService, ErrorService() { +internal class MockSearchService @TestOnly constructor(var searchUser: List? = null) : SearchService, ErrorService() { override suspend fun searchTweets( query: String, count: Int, @@ -63,11 +63,15 @@ internal class MockSearchService @TestOnly constructor() : SearchService, ErrorS following: Boolean ): List { checkError() - val list = mutableListOf() - for (i in 0 until count) { - list.add(mockIUser()) - } - return list.toIPaging() + return ( + searchUser ?: let { + val list = mutableListOf() + for (i in 0 until count) { + list.add(mockIUser()) + } + list + } + ).toIPaging() } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt new file mode 100644 index 000000000..aae5d1491 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt @@ -0,0 +1,48 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source + +import androidx.paging.PagingSource +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class FollowersPagingSourceTest { + @Test + fun loadFollowersFromService(): Unit = runBlocking { + val service = MockRelationshipService() + val pagingSource = FollowersPagingSource( + MicroBlogKey.twitter("123"), + service = service + ) + val result = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + (result as PagingSource.LoadResult.Page).data.map { + assert(service.showRelationship(it.id).followedBy) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt new file mode 100644 index 000000000..25c732086 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt @@ -0,0 +1,48 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source + +import androidx.paging.PagingSource +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class FollowingPagingSourceTest { + @Test + fun loadFollowingUsersFromService(): Unit = runBlocking { + val service = MockRelationshipService() + val pagingSource = FollowingPagingSource( + MicroBlogKey.twitter("123"), + service = service + ) + val result = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + (result as PagingSource.LoadResult.Page).data.map { + assert(service.showRelationship(it.id).following) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt new file mode 100644 index 000000000..f818d5cf6 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source + +import androidx.paging.PagingSource +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test + +class ListsSubscribersPagingSourceTest { + @Test + fun loadListSubscribersFromService(): Unit = runBlocking { + val service = MockListsService() + val pagingSource = ListsSubscribersPagingSource( + MicroBlogKey.twitter("123"), + service = service, + listId = "123" + ) + val result = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + (result as PagingSource.LoadResult.Page).data.map { + assert(service.isSubscribers(it.id)) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt new file mode 100644 index 000000000..9db6336d2 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt @@ -0,0 +1,82 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source + +import androidx.paging.PagingSource +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.service.MockSearchService +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class SearchUserPagingSourceTest { + @Test + fun loadUserFromSearchServiceAndApplyRightKeys() = runBlocking { + val service = MockSearchService() + val pagingSource = SearchUserPagingSource( + MicroBlogKey.twitter("123"), + service = service, + query = "test" + ) + val result = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 20, + placeholdersEnabled = false + ) + ) + assert(result is PagingSource.LoadResult.Page) + val nextKey = (result as PagingSource.LoadResult.Page).nextKey ?: 0 + assertEquals(1, nextKey) + + service.searchUser = listOf(mockIUser()) + val resultAppend = pagingSource.load( + PagingSource.LoadParams.Append( + key = nextKey, + loadSize = 20, + placeholdersEnabled = false + ) + ) + assert(resultAppend is PagingSource.LoadResult.Page) + assertNull((resultAppend as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun loadReturnErrorWhenErrorOccurred() = runBlocking { + val service = MockSearchService() + service.errorMsg = "throw test errors" + val pagingSource = SearchUserPagingSource( + MicroBlogKey.twitter("123"), + service = service, + query = "test" + ) + val result = pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 20, + placeholdersEnabled = false + ) + ) + assert(result is PagingSource.LoadResult.Error) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt new file mode 100644 index 000000000..199a2b704 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt @@ -0,0 +1,80 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source + +import androidx.paging.PagingSource +import com.twidere.services.microblog.model.IPaging +import com.twidere.services.microblog.model.IUser +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toIPaging +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class UserPagingSourceTest { + @Test + fun loadReturnsPageWhenOnSuccessfulLoadOfPageKeyedData() = runBlocking { + val pagingSource = MockUserPagingSource(MicroBlogKey.Empty) + assertEquals( + PagingSource.LoadResult.Page( + data = pagingSource.mockData.map { it.toUi(MicroBlogKey.Empty) }, + prevKey = null, + nextKey = (pagingSource.mockData as IPaging).nextPage + ), + pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) + ) + } + + @Test + fun loadReturnsErrorWhenErrorOccurred() = runBlocking { + val pagingSource = MockUserPagingSource(MicroBlogKey.Empty) + pagingSource.errorMsg = "throw test errors" + assert( + pagingSource.load( + PagingSource.LoadParams.Refresh( + key = null, + loadSize = 2, + placeholdersEnabled = false + ) + ) is PagingSource.LoadResult.Error + ) + } +} + +private class MockUserPagingSource(accountKey: MicroBlogKey) : UserPagingSource( + accountKey +) { + var errorMsg: String? = null + val mockData = listOf(mockIUser()).toIPaging() + + override suspend fun loadUsers(params: LoadParams): List { + if (!errorMsg.isNullOrEmpty()) throw Error(errorMsg) + return mockData + } +} From 554e054d8e1a6705315f0a633b9782a41f35f747 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 16 Aug 2021 18:36:01 +0800 Subject: [PATCH 060/615] migrate DirectMessageRepositoryTest to commonMain --- .../dm/DirectMessageRepositoryTest.kt | 198 ------------------ .../twiderex/mock/db/MockCacheDatabase.kt | 4 +- .../dao/MockDirectMessageConversationDao.kt | 1 + .../twiderex/mock/db/dao/MockUserDao.kt | 49 +++++ .../twidere/twiderex/mock/model/MockModels.kt | 17 +- .../mock/service/MockDirectMessageService.kt | 16 +- .../mock/service/MockLookUpService.kt | 56 +++++ .../repository/DirectMessageRepositoryTest.kt | 126 ++++++++++- 8 files changed, 250 insertions(+), 217 deletions(-) delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt diff --git a/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt b/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt deleted file mode 100644 index a360798ad..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/repository/dm/DirectMessageRepositoryTest.kt +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository.dm - -import android.util.Log -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.mock.MockDirectMessageService -import com.twidere.twiderex.mock.MockLookUpService -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.repository.DirectMessageRepository -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.concurrent.Executors - -/** - * instead of testing pagination, we should focus on our code logic - */ -@RunWith(AndroidJUnit4::class) -class DirectMessageRepositoryTest { - private lateinit var mockDataBase: CacheDatabase - - private var mockService = MockDirectMessageService() - private var mockLookUpService = MockLookUpService() - private val accountKey = MicroBlogKey.twitter("123") - - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - mockDataBase = Room.inMemoryDatabaseBuilder( - ApplicationProvider.getApplicationContext(), - CacheDatabase::class.java - ) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() - } - - @After - fun tearDown() { - mockDataBase.clearAllTables() - } - - @Test - fun fetchList_FetchEventAndStoreToDB() = runBlocking { - val repo = DirectMessageRepository(mockDataBase) - mockService.errorMsg = null - assert(mockDataBase.directMessageConversationDao().find(accountKey).isEmpty()) - - mockService.add( - mockService.generateDirectMessage( - 20, - senderId = "345", - recipientId = accountKey.id - ) - ) - val result = repo.fetchEventAndSaveToDataBase( - key = null, - accountKey = accountKey, - service = mockService, - lookupService = mockLookUpService - ) - assert(result.isNotEmpty()) - val conversationList = mockDataBase.directMessageConversationDao().find(accountKey) - assert(conversationList.isNotEmpty()) - assert(mockDataBase.directMessageDao().find(accountKey, conversationList.first().conversation.conversationKey).isNotEmpty()) - // check lookup for user - assert( - conversationList.first().latestMessage.sender.userId == "345" - ) - } - - @Test - fun createConversation_StoreNewConversationToDbIFNotExists() = runBlocking { - val repo = DirectMessageRepository(mockDataBase) - assert(mockDataBase.directMessageConversationDao().find(accountKey).isEmpty()) - val user = mockLookUpService.lookupUser("123").toDbUser(accountKey).toUi() - val conversationKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) - assert(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversationKey) != null) - - val user2 = mockLookUpService.lookupUser("234").toDbUser(accountKey).toUi() - val conversationKey2 = repo.createNewConversation(user2, accountKey, PlatformType.Twitter) - assert(conversationKey2 != conversationKey) - } - - @Test - fun createConversation_ReturnsExistsConversationKeyIfConversationExists() = runBlocking { - val repo = DirectMessageRepository(mockDataBase) - val user = mockLookUpService.lookupUser("123").toDbUser(accountKey).toUi() - val existsKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) - - val newKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) - assert(newKey == existsKey) - } - - @Test - fun checkNewMessage_ReturnsConversationsThatContainsNewMessages() = runBlocking { - val repo = DirectMessageRepository(mockDataBase) - for (i in 0 until 10) { - mockService.add( - mockService.generateDirectMessage( - 1, - senderId = i.toString(), - recipientId = accountKey.id - ) - ) - } - - val firstNewMessages = repo.checkNewMessages(accountKey, mockService, mockLookUpService) - Log.d("DMTest", "firstSize:${firstNewMessages.size}") - assert(firstNewMessages.size == 10) - - mockService.add(mockService.generateDirectMessage(1, senderId = 5.toString(), recipientId = accountKey.id)) - val secondNewMessages = repo.checkNewMessages(accountKey, mockService, mockLookUpService) - Log.d("DMTest", "second:${secondNewMessages.size}") - assert(secondNewMessages.size == 1) - Assert.assertEquals("5", secondNewMessages.first().latestMessage.sender.id) - - mockService.add(mockService.generateDirectMessage(1, senderId = accountKey.id, recipientId = 2.toString())) - val thirdNewMessage = repo.checkNewMessages(accountKey, mockService, mockLookUpService) - Log.d("DMTest", "third:${thirdNewMessage.size}") - assert(thirdNewMessage.isEmpty()) - } - - @Test - fun deleteMessage_DeleteFromBothDbAndApi() = runBlocking { - val repo = DirectMessageRepository(mockDataBase) - for (i in 0 until 10) { - mockService.add( - mockService.generateDirectMessage( - 2, - senderId = i.toString(), - recipientId = accountKey.id - ) - ) - } - repo.fetchEventAndSaveToDataBase(null, accountKey, mockService, mockLookUpService) - var conversation = mockDataBase.directMessageConversationDao().find(accountKey).first() - assert(mockDataBase.directMessageDao().find(accountKey, conversation.conversation.conversationKey).size == 2) - repo.deleteMessage( - accountKey = accountKey, - conversationKey = conversation.conversation.conversationKey, - messageId = conversation.latestMessage.message.messageId, - messageKey = conversation.latestMessage.message.messageKey, - mockService - ) - // after fetch from api, still can't find the deleted message - repo.fetchEventAndSaveToDataBase(null, accountKey, mockService, mockLookUpService) - assert(mockDataBase.directMessageDao().find(accountKey, conversation.conversation.conversationKey).size == 1) - // as long as conversation still contains messages , it won't be delete - assert(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversation.conversation.conversationKey) != null) - - conversation = mockDataBase.directMessageConversationDao().find(accountKey).first() - repo.deleteMessage( - accountKey = accountKey, - conversationKey = conversation.conversation.conversationKey, - messageId = conversation.latestMessage.message.messageId, - messageKey = conversation.latestMessage.message.messageKey, - mockService - ) - - repo.fetchEventAndSaveToDataBase(null, accountKey, mockService, mockLookUpService) - assert( - mockDataBase.directMessageDao() - .find(accountKey, conversation.conversation.conversationKey).isEmpty() - ) - // when conversation contains zero message, it will be delete too - assert(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversation.conversation.conversationKey) == null) - } -} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 9ad359fd2..4efa69d65 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -37,6 +37,7 @@ import com.twidere.twiderex.mock.db.dao.MockMediaDao import com.twidere.twiderex.mock.db.dao.MockPagingTimelineDao import com.twidere.twiderex.mock.db.dao.MockStatusDao import com.twidere.twiderex.mock.db.dao.MockTrendDao +import com.twidere.twiderex.mock.db.dao.MockUserDao import org.jetbrains.annotations.TestOnly internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { @@ -49,8 +50,9 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { return MockMediaDao() } + private val userDao = MockUserDao() override fun userDao(): UserDao { - TODO("Not yet implemented") + return userDao } private val pagingTimelineDao = MockPagingTimelineDao(statusDao) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt index 703529715..2b4d7a731 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt @@ -63,6 +63,7 @@ internal class MockDirectMessageConversationDao @TestOnly constructor(private va if (it.isNullOrEmpty()) { fakeDb[con.accountKey] = mutableListOf(con) } else { + it.removeAll { it.conversationKey == con.conversationKey } it.add(con) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt new file mode 100644 index 000000000..0629a9e20 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.jetbrains.annotations.TestOnly + +internal class MockUserDao @TestOnly constructor() : UserDao { + private val fakeDb = mutableMapOf() + override suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? { + return fakeDb[userKey.toString()] + } + + override suspend fun insertAll(listOf: List) { + listOf.map { + fakeDb[it.userKey.toString()] = it + } + } + + override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { + return flow { + findWithUserKey(userKey) + } + } + + val datas get() = fakeDb.values.toList() +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index d400bbc85..ca50f0125 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -78,9 +78,10 @@ internal fun List.toIPaging(nextPaging: String? = UUID.randomUUID().toStr ) @TestOnly -internal fun mockIUser(id: String = System.currentTimeMillis().toString()): IUser { +internal fun mockIUser(id: String = UUID.randomUUID().toString(), name: String = ""): IUser { return UserV2( id = id, + name = name ) } @@ -119,9 +120,9 @@ internal fun mockIListModel( @TestOnly internal fun mockIStatus( - id: String = System.currentTimeMillis().toString(), + id: String = UUID.randomUUID().toString(), hasMedia: Boolean = false, - authorId: String = System.currentTimeMillis().toString() + authorId: String = UUID.randomUUID().toString() ): IStatus { return StatusV2( id = id, @@ -138,9 +139,9 @@ internal fun mockIStatus( } @TestOnly -internal fun mockINotification(id: String = System.currentTimeMillis().toString()): INotification { +internal fun mockINotification(id: String = UUID.randomUUID().toString()): INotification { val account = Account( - id = System.currentTimeMillis().toString(), + id = UUID.randomUUID().toString(), username = "", displayName = "", acct = "" @@ -158,16 +159,16 @@ internal fun mockINotification(id: String = System.currentTimeMillis().toString( } @TestOnly -internal fun mockIDirectMessage(id: String = System.currentTimeMillis().toString(), accountId: String, inCome: Boolean = true): IDirectMessage { +internal fun mockIDirectMessage(id: String = UUID.randomUUID().toString(), accountId: String, otherUserID: String = UUID.randomUUID().toString(), inCome: Boolean = true): IDirectMessage { return DirectMessageEvent( createdTimestamp = System.currentTimeMillis().toString(), id = id, type = "message_create", messageCreate = MessageCreate( messageData = MessageData(text = "mock message"), - senderId = if (inCome)UUID.randomUUID().toString() else accountId, + senderId = if (inCome) otherUserID else accountId, target = MessageTarget( - recipientId = if (inCome) accountId else UUID.randomUUID().toString() + recipientId = if (inCome) accountId else otherUserID ) ) ) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt index 98b943de4..046d411a8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt @@ -27,7 +27,7 @@ import com.twidere.twiderex.mock.model.toIPaging import com.twidere.twiderex.model.MicroBlogKey import org.jetbrains.annotations.TestOnly -internal class MockDirectMessageService @TestOnly constructor(private val accountKey: MicroBlogKey) : DirectMessageService, +internal class MockDirectMessageService @TestOnly constructor(private val accountKey: MicroBlogKey, var messages: List? = null) : DirectMessageService, ErrorService() { private val deletedMessageId = mutableListOf() @@ -40,11 +40,15 @@ internal class MockDirectMessageService @TestOnly constructor(private val accoun override suspend fun getDirectMessages(cursor: String?, count: Int?): List { checkError() - val list = mutableListOf() - for (i in 0 until (count ?: 1)) { - list.add(mockIDirectMessage(accountId = accountKey.id, inCome = i % 2 == 0)) - } - return list.toIPaging() + return ( + messages ?: let { + val list = mutableListOf() + for (i in 0 until (count ?: 1)) { + list.add(mockIDirectMessage(accountId = accountKey.id, inCome = i % 2 == 0)) + } + list + } + ).toIPaging() } override suspend fun showDirectMessage(id: String): IDirectMessage? { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt new file mode 100644 index 000000000..c521469d1 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt @@ -0,0 +1,56 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.service + +import com.twidere.services.microblog.LookupService +import com.twidere.services.microblog.model.IStatus +import com.twidere.services.microblog.model.IUser +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.model.mockIUser +import org.jetbrains.annotations.TestOnly + +internal class MockLookUpService @TestOnly constructor() : LookupService, ErrorService() { + override suspend fun lookupStatus(id: String): IStatus { + return mockIStatus(id = id) + } + + override suspend fun lookupUser(id: String): IUser { + return mockIUser(id = id) + } + + override suspend fun lookupUserByName(name: String): IUser { + return mockIUser(name = name) + } + + override suspend fun lookupUsersByName(name: List): List { + return name.map { + lookupUserByName(it) + } + } + + override suspend fun userPinnedStatus(userId: String): List { + return listOf( + mockIStatus( + authorId = userId + ) + ) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt index 51be9ada4..81f759738 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt @@ -20,12 +20,130 @@ */ package com.twidere.twiderex.repository +import com.twidere.services.microblog.model.IDirectMessage +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.mock.db.MockCacheDatabase -import org.junit.Before +import com.twidere.twiderex.mock.db.dao.MockUserDao +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockDirectMessageService +import com.twidere.twiderex.mock.service.MockLookUpService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull internal class DirectMessageRepositoryTest { - private val cacheDatabase = MockCacheDatabase() - @Before - fun setUp() { + private val mockDataBase = MockCacheDatabase() + private val accountKey = MicroBlogKey.twitter("123") + private var mockService = MockDirectMessageService(accountKey) + private var mockLookUpService = MockLookUpService() + + @Test + fun fetchList_FetchEventAndStoreToDB() = runBlocking { + val repo = DirectMessageRepository(mockDataBase) + mockService.errorMsg = null + assert(mockDataBase.directMessageConversationDao().find(accountKey).isEmpty()) + assert((mockDataBase.userDao() as MockUserDao).datas.isEmpty()) + + val result = repo.fetchEventAndSaveToDataBase( + key = null, + accountKey = accountKey, + service = mockService, + lookupService = mockLookUpService + ) + assert(result.isNotEmpty()) + val conversationList = mockDataBase.directMessageConversationDao().find(accountKey) + assert(conversationList.isNotEmpty()) + assert(mockDataBase.directMessageDao().getPagingSource(accountKey, conversationList.first().conversation.conversationKey).collectDataForTest().isNotEmpty()) + assert((mockDataBase.userDao() as MockUserDao).datas.isNotEmpty()) + } + + @Test + fun createConversation_StoreNewConversationToDbIFNotExists() = runBlocking { + val repo = DirectMessageRepository(mockDataBase) + assert(mockDataBase.directMessageConversationDao().find(accountKey).isEmpty()) + val user = mockLookUpService.lookupUser("123").toUi(accountKey) + val conversationKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) + assert(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversationKey) != null) + + val user2 = mockLookUpService.lookupUser("234").toUi(accountKey) + val conversationKey2 = repo.createNewConversation(user2, accountKey, PlatformType.Twitter) + assert(conversationKey2 != conversationKey) + } + + @Test + fun createConversation_ReturnsExistsConversationKeyIfConversationExists() = runBlocking { + val repo = DirectMessageRepository(mockDataBase) + val user = mockLookUpService.lookupUser("123").toUi(accountKey) + val existsKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) + + val newKey = repo.createNewConversation(user, accountKey, PlatformType.Twitter) + assert(newKey == existsKey) + } + + @Test + fun checkNewMessage_ReturnsConversationsThatContainsNewMessages() = runBlocking { + val repo = DirectMessageRepository(mockDataBase) + val originMessage = mutableListOf() + for (i in 0 until 10) { + originMessage.add(mockIDirectMessage(accountId = accountKey.id, otherUserID = i.toString())) + mockService.messages = originMessage + } + + // new message + val firstNewMessages = repo.checkNewMessages(accountKey, mockService, mockLookUpService) + assert(firstNewMessages.size == 10) + + // same message with database, no new messages + val secondNewMessage = repo.checkNewMessages(accountKey, mockService, mockLookUpService) + assert(secondNewMessage.isEmpty()) + + // new message + mockService.messages = listOf(mockIDirectMessage(accountId = accountKey.id, otherUserID = 5.toString())) + val thirdNewMessages = repo.checkNewMessages(accountKey, mockService, mockLookUpService) + assertEquals(1, thirdNewMessages.size) + assertEquals("5", thirdNewMessages.first().latestMessage.sender.id) + } + + @Test + fun deleteMessage_DeleteFromBothDbAndApi() = runBlocking { + val repo = DirectMessageRepository(mockDataBase) + mockService.messages = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "test"), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "test") + ) + repo.fetchEventAndSaveToDataBase(null, accountKey, mockService, mockLookUpService) + var conversation = mockDataBase.directMessageConversationDao().find(accountKey).first() + repo.deleteMessage( + accountKey = accountKey, + conversationKey = conversation.conversation.conversationKey, + messageId = conversation.latestMessage.messageId, + messageKey = conversation.latestMessage.messageKey, + mockService + ) + // also delete from api + assert(mockService.isDeleted(conversation.latestMessage.messageId)) + + assertEquals(1, mockDataBase.directMessageDao().getMessageCount(accountKey, conversation.conversation.conversationKey)) + // as long as conversation still contains messages , it won't be delete + assertNotNull(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversation.conversation.conversationKey)) + + conversation = mockDataBase.directMessageConversationDao().find(accountKey).first() + repo.deleteMessage( + accountKey = accountKey, + conversationKey = conversation.conversation.conversationKey, + messageId = conversation.latestMessage.messageId, + messageKey = conversation.latestMessage.messageKey, + mockService + ) + assert(mockService.isDeleted(conversation.latestMessage.messageId)) + + assertEquals(0, mockDataBase.directMessageDao().getMessageCount(accountKey, conversation.conversation.conversationKey)) + // when conversation contains zero message, it will be delete too + assertNull(mockDataBase.directMessageConversationDao().findWithConversationKey(accountKey, conversation.conversation.conversationKey)) } } From 22571eac29633e2437e50cd653cb7bec6b577583 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 16 Aug 2021 18:51:01 +0800 Subject: [PATCH 061/615] add test for DraftRepository --- .../twiderex/mock/db/MockAppDatabase.kt | 4 +- .../twiderex/mock/db/dao/MockDraftDao.kt | 53 +++++++++++++ .../twiderex/mock/db/dao/MockUserDao.kt | 2 +- .../repository/DraftRepositoryTest.kt | 75 +++++++++++++++++++ 4 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt index 0e2b0c5ba..4b6a52839 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt @@ -23,12 +23,14 @@ package com.twidere.twiderex.mock.db import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.dao.DraftDao import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.mock.db.dao.MockDraftDao import com.twidere.twiderex.mock.db.dao.MockSearchDao import org.jetbrains.annotations.TestOnly internal class MockAppDatabase @TestOnly constructor() : AppDatabase { + private val draftDao = MockDraftDao() override fun draftDao(): DraftDao { - TODO("Not yet implemented") + return draftDao } private val searchDao = MockSearchDao() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt new file mode 100644 index 000000000..115d6814a --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt @@ -0,0 +1,53 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.model.ui.UiDraft +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +class MockDraftDao : DraftDao { + private val fakeDb = mutableMapOf() + override fun getAll(): Flow> { + return flow { + emit(fakeDb.values.toList()) + } + } + + override fun getDraftCount(): Flow { + return flow { + emit(fakeDb.keys.size.toLong()) + } + } + + override suspend fun insert(it: UiDraft) { + fakeDb[it.draftId] = it + } + + override suspend fun get(draftId: String): UiDraft? { + return fakeDb[draftId] + } + + override suspend fun remove(draft: UiDraft) { + fakeDb.remove(draft.draftId) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt index 0629a9e20..34481153d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt @@ -41,7 +41,7 @@ internal class MockUserDao @TestOnly constructor() : UserDao { override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { return flow { - findWithUserKey(userKey) + emit(findWithUserKey(userKey)) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt new file mode 100644 index 000000000..aab4b9a43 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt @@ -0,0 +1,75 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockAppDatabase +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +class DraftRepositoryTest { + @Test + fun sourceAndSourceCountWillUpdateAfterAddOrUpgrade() = runBlocking { + val repo = DraftRepository(MockAppDatabase()) + assertEquals(0, repo.sourceCount.first()) + repo.addOrUpgrade( + content = "draft", + media = emptyList(), + composeType = ComposeType.New, + statusKey = MicroBlogKey.twitter("test"), + ) + val insertDraft = repo.source.first().first() + assertEquals("draft", insertDraft.content) + assertEquals(1, repo.sourceCount.first()) + + repo.addOrUpgrade( + content = "upgrade", + media = emptyList(), + composeType = ComposeType.New, + statusKey = MicroBlogKey.twitter("test"), + draftId = insertDraft.draftId + ) + assertEquals("upgrade", repo.source.first().first().content) + assertEquals(1, repo.sourceCount.first()) + } + + @Test + fun sourceAndSourceCountWillUpdateAfterDeleteDraft() = runBlocking { + val repo = DraftRepository(MockAppDatabase()) + assertEquals(0, repo.sourceCount.first()) + repo.addOrUpgrade( + content = "draft", + media = emptyList(), + composeType = ComposeType.New, + statusKey = MicroBlogKey.twitter("test"), + ) + val insertDraft = repo.source.first().first() + + assertEquals(1, repo.sourceCount.first()) + + repo.remove(insertDraft.draftId) + assertEquals(0, repo.sourceCount.first()) + assert(repo.source.first().isEmpty()) + } +} From 7760b536d200d7bcb3868da6336c9b68002c7579 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 16 Aug 2021 19:22:09 +0800 Subject: [PATCH 062/615] add test for ListsRepository --- .../twiderex/mock/db/dao/MockDraftDao.kt | 3 +- .../twiderex/mock/db/dao/MockListsDao.kt | 13 +- .../repository/DraftRepositoryTest.kt | 2 +- .../repository/ListsRepositoryTest.kt | 116 ++++++++++++++++++ 4 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt index 115d6814a..ad0df1492 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt @@ -24,8 +24,9 @@ import com.twidere.twiderex.db.dao.DraftDao import com.twidere.twiderex.model.ui.UiDraft import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import org.jetbrains.annotations.TestOnly -class MockDraftDao : DraftDao { +internal class MockDraftDao @TestOnly constructor() : DraftDao { private val fakeDb = mutableMapOf() override fun getAll(): Flow> { return flow { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt index ec2e2e8fe..de4110af5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt @@ -73,9 +73,8 @@ internal class MockListsDao @TestOnly constructor() : ListsDao { if (it.isNullOrEmpty()) { fakeDb[list.accountKey] = mutableListOf(list) } else { - it.map { origin -> - if (origin.listKey == list.listKey) list else origin - } + it.removeAll { origin -> origin.listKey == list.listKey } + it.add(list) } } } @@ -83,13 +82,7 @@ internal class MockListsDao @TestOnly constructor() : ListsDao { override suspend fun delete(listOf: List) { listOf.forEach { list -> - fakeDb[list.accountKey].let { - if (!it.isNullOrEmpty()) { - it.mapNotNull { origin -> - if (origin.listKey == list.listKey) null else origin - } - } - } + fakeDb[list.accountKey]?.removeAll { it.listKey == list.listKey } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt index aab4b9a43..f43f99dfc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals -class DraftRepositoryTest { +internal class DraftRepositoryTest { @Test fun sourceAndSourceCountWillUpdateAfterAddOrUpgrade() = runBlocking { val repo = DraftRepository(MockAppDatabase()) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt new file mode 100644 index 000000000..1f7beeeb8 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt @@ -0,0 +1,116 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class ListsRepositoryTest { + + @Test + fun saveToDbAfterCreateListSuccess() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = ListsRepository(MockCacheDatabase()) + val listKey = repo.createLists( + accountKey = accountKey, + service = MockListsService(), + title = "list title", + description = "desc", + mode = "private", + ).listKey + val list = repo.findListWithListKey(accountKey, listKey).first() + assertNotNull(list) + assertEquals(accountKey, list.accountKey) + assertEquals("list title", list.title) + assertEquals("desc", list.descriptions) + assertEquals("private", list.mode) + } + + @Test + fun saveToDbAfterUpdateListSuccess() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = ListsRepository(MockCacheDatabase()) + val listId = repo.prepare(accountKey).id + + val listKey = repo.updateLists( + accountKey = accountKey, + service = MockListsService(), + title = "upgrade title", + description = "upgrade desc", + mode = "public", + listId = listId + ).listKey + + val list = repo.findListWithListKey(accountKey, listKey).first() + assertNotNull(list) + assertEquals("upgrade title", list.title) + assertEquals("upgrade desc", list.descriptions) + assertEquals("public", list.mode) + } + + @Test + fun deleteFromDbAfterDeleteListSuccess() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = ListsRepository(MockCacheDatabase()) + val list = repo.prepare(accountKey) + assertNotNull( + repo.deleteLists( + accountKey, + service = MockListsService(), + listId = list.id, + listKey = list.listKey + ) + ) + assertNull(repo.findListWithListKey(accountKey, list.listKey).first()) + } + + @Test + fun updateStatusToDbAfterSubscribeOrUnsubscribe() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = ListsRepository(MockCacheDatabase()) + val list = repo.prepare(accountKey) + assertEquals(true, list.isFollowed) + + repo.unsubscribeLists(accountKey, MockListsService(), list.listKey) + assertEquals(false, repo.findListWithListKey(accountKey, list.listKey).first()?.isFollowed) + + repo.subscribeLists(accountKey, MockListsService(), list.listKey) + assertEquals(true, repo.findListWithListKey(accountKey, list.listKey).first()?.isFollowed) + } + + private suspend fun ListsRepository.prepare(accountKey: MicroBlogKey): UiList { + return createLists( + accountKey = accountKey, + service = MockListsService(), + title = "list title", + description = "desc", + mode = "private", + ) + } +} From 0767af96789efb41bba139b6a2a4d881a6249b2e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 16 Aug 2021 19:22:44 +0800 Subject: [PATCH 063/615] add test for ListsRepository --- .../repository/ListsRepositoryTest.kt | 125 ------------------ 1 file changed, 125 deletions(-) delete mode 100644 android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt b/android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt deleted file mode 100644 index 9081dff63..000000000 --- a/android/src/test/java/com/twidere/twiderex/repository/ListsRepositoryTest.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository - -import com.twidere.twiderex.mock.MockCenter -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.MicroBlogKey -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.whenever - -@OptIn(ExperimentalCoroutinesApi::class) -class ListsRepositoryTest { - private lateinit var repository: ListsRepository - - private var mockDatabase = MockCenter.mockCacheDatabase() - - private var mockListsService = MockCenter.mockListsService() - - @Mock - private lateinit var mockAccountDetails: AccountDetails - - private lateinit var mocks: AutoCloseable - - @Before - fun setUp() { - mocks = MockitoAnnotations.openMocks(this) - repository = ListsRepository(mockDatabase) - whenever(mockAccountDetails.accountKey).thenReturn(MicroBlogKey.twitter("123")) - whenever(mockAccountDetails.service).thenReturn(mockListsService) - } - - @After - fun tearDown() { - mocks.close() - } - - @Test - fun createList_saveToDatabase() = runBlocking { - // check if the repository save result to db after create request success - val originList = mockDatabase.listsDao().findAll() - repository.createLists(mockAccountDetails, "create lists") - val createList = mockDatabase.listsDao().findAll() - Assert.assertNotNull(createList) - Assert.assertEquals(1, (createList?.size ?: 0) - (originList?.size ?: 0)) - Assert.assertEquals("create lists", createList!![0].title) - } - - @Test - fun updateList_updateToDatabase() = runBlocking { - val originData = repository.createLists(mockAccountDetails, "before title", description = "before title", mode = "public") - repository.updateLists( - mockAccountDetails, - originData.id, - "after title", - description = "after desc", - mode = "private" - ) - val updateData = mockDatabase.listsDao() - .findWithListKey(originData.listKey, accountKey = mockAccountDetails.accountKey) - Assert.assertNotNull(updateData) - Assert.assertEquals("after title", updateData?.title) - Assert.assertEquals("after desc", updateData?.description) - Assert.assertEquals("private", updateData?.mode) - } - - @Test - fun deleteList_deleteInDatabase() = runBlocking { - val originData = repository.createLists(mockAccountDetails, "delete") - val originList = mockDatabase.listsDao().findAll() - repository.deleteLists( - mockAccountDetails, - originData.listKey, - originData.id - ) - val deleteList = mockDatabase.listsDao().findAll() - Assert.assertEquals(1, originList!!.size - deleteList!!.size) - } - - @Test - fun unsubscribeList_updateFollowingInDatabase() = runBlocking { - val originData = repository.createLists(mockAccountDetails, "delete") - repository.unsubscribeLists( - mockAccountDetails, - originData.listKey, - ) - val unsubscribeData = mockDatabase.listsDao().findWithListKey(originData.listKey, mockAccountDetails.accountKey) - Assert.assertEquals(false, unsubscribeData?.isFollowed) - } - - @Test - fun subscribeList_updateFollowingInDatabase() = runBlocking { - val originData = repository.createLists(mockAccountDetails, "delete") - repository.subscribeLists( - mockAccountDetails, - originData.listKey, - ) - val unsubscribeData = mockDatabase.listsDao().findWithListKey(originData.listKey, mockAccountDetails.accountKey) - Assert.assertEquals(true, unsubscribeData?.isFollowed) - } -} From 3e515bc7b9ffc6bce03db8449139d2fb6c872f24 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 17 Aug 2021 11:21:32 +0800 Subject: [PATCH 064/615] migrate ListsUsersRepositoryTest to commomMain --- .../repository/ListsUsersRepositoryTest.kt | 78 ------------------- .../repository/ListsUsersRepositoryTest.kt | 58 ++++++++++++++ 2 files changed, 58 insertions(+), 78 deletions(-) delete mode 100644 android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt diff --git a/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt deleted file mode 100644 index d8fcb05e3..000000000 --- a/android/src/test/java/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository - -import com.twidere.services.mastodon.model.Account -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.mock.MockCenter -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.paging.crud.PagingMemoryCache -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.whenever - -class ListsUsersRepositoryTest { - private lateinit var repository: ListsUsersRepository - - private var mockListsService = MockCenter.mockListsService() - - @Mock - private lateinit var mockAccountDetails: AccountDetails - - private lateinit var mocks: AutoCloseable - - private val memoryCache = mutableMapOf>() - - @Before - fun setUp() { - mocks = MockitoAnnotations.openMocks(this) - repository = ListsUsersRepository(memoryCache) - whenever(mockAccountDetails.accountKey).thenReturn(MicroBlogKey.twitter("123")) - whenever(mockAccountDetails.service).thenReturn(mockListsService) - } - - @After - fun tearDown() { - mocks.close() - } - - @Test - fun addMembers_updateToMemoryCacheWhenSuccess() = runBlocking { - repository.addMember(mockAccountDetails, "fake listId", Account(id = "1", displayName = "x", username = "x", acct = "x").toDbUser(mockAccountDetails.accountKey).toUi()) - Assert.assertEquals("1", memoryCache["fake listId"]?.find(0, 1)?.get(0)?.id) - } - - @Test - fun removeMembers_updateToMemoryCacheWhenSuccess() = runBlocking { - val user = Account(id = "1", displayName = "x", username = "x", acct = "x").toDbUser(mockAccountDetails.accountKey).toUi() - repository.addMember(mockAccountDetails, "fake listId", user) - Assert.assertEquals(true, memoryCache["fake listId"]?.find(0, 1)?.isNotEmpty()) - repository.removeMember(mockAccountDetails, "fake listId", user) - Assert.assertEquals(true, memoryCache["fake listId"]?.find(0, 1)?.isEmpty()) - } -} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt new file mode 100644 index 000000000..16c33c6d6 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.paging.crud.PagingMemoryCache +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +class ListsUsersRepositoryTest { + + @Test + fun addMembers_updateToMemoryCacheWhenSuccess() = runBlocking { + val memoryCache = mutableMapOf>() + val accountKey = MicroBlogKey.twitter("test") + val repository = ListsUsersRepository(memoryCache) + val user = mockIUser().toUi(accountKey) + repository.addMember(MockListsService(), "fake listId", user) + assertEquals(user.id, memoryCache["fake listId"]?.find(0, 1)?.get(0)?.id) + } + + @Test + fun removeMembers_updateToMemoryCacheWhenSuccess() = runBlocking { + val memoryCache = mutableMapOf>() + val accountKey = MicroBlogKey.twitter("test") + val repository = ListsUsersRepository(memoryCache) + val mockListsService = MockListsService() + val user = mockIUser().toUi(accountKey) + repository.addMember(mockListsService, "fake listId", user) + assertEquals(true, memoryCache["fake listId"]?.find(0, 1)?.isNotEmpty()) + + repository.removeMember(mockListsService, "fake listId", user) + assertEquals(true, memoryCache["fake listId"]?.find(0, 1)?.isEmpty()) + } +} From 12ba1f4dd77a523245685f624b15a48d9d44a16b Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 17 Aug 2021 12:13:02 +0800 Subject: [PATCH 065/615] add test for notificationRepository --- .../twiderex/mock/db/MockCacheDatabase.kt | 4 +- .../mock/db/dao/MockNotificationCursorDao.kt | 44 ++++++++++++ .../mock/service/MockNotificationService.kt | 3 +- .../mock/service/MockTimelineService.kt | 3 +- .../repository/NotificationRepositoryTest.kt | 72 +++++++++++++++++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 4efa69d65..b0154003e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -34,6 +34,7 @@ import com.twidere.twiderex.mock.db.dao.MockDirectMessageConversationDao import com.twidere.twiderex.mock.db.dao.MockDirectMessageEventDao import com.twidere.twiderex.mock.db.dao.MockListsDao import com.twidere.twiderex.mock.db.dao.MockMediaDao +import com.twidere.twiderex.mock.db.dao.MockNotificationCursorDao import com.twidere.twiderex.mock.db.dao.MockPagingTimelineDao import com.twidere.twiderex.mock.db.dao.MockStatusDao import com.twidere.twiderex.mock.db.dao.MockTrendDao @@ -65,8 +66,9 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { return listsDao } + private val notificationCursorDao = MockNotificationCursorDao() override fun notificationCursorDao(): NotificationCursorDao { - TODO("Not yet implemented") + return notificationCursorDao } private val trendDao = MockTrendDao() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt new file mode 100644 index 000000000..6cd4d90c1 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock.db.dao + +import com.twidere.twiderex.db.dao.NotificationCursorDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import org.jetbrains.annotations.TestOnly + +internal class MockNotificationCursorDao @TestOnly constructor() : NotificationCursorDao { + private val fakeDb = mutableMapOf>() + override suspend fun find( + accountKey: MicroBlogKey, + type: NotificationCursorType + ): NotificationCursor? { + return fakeDb[accountKey]?.find { it.type == type } + } + + override suspend fun add(notificationCursor: NotificationCursor) { + fakeDb[notificationCursor.accountKey]?.let { + it.removeAll { cursor -> cursor.type == cursor.type } + it.add(notificationCursor) + } ?: let { fakeDb[notificationCursor.accountKey] = mutableListOf(notificationCursor) } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt index f6aded351..9d021f234 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt @@ -20,13 +20,14 @@ */ package com.twidere.twiderex.mock.service +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.model.INotification import com.twidere.twiderex.mock.model.mockINotification import com.twidere.twiderex.mock.model.toIPaging import org.jetbrains.annotations.TestOnly -internal class MockNotificationService @TestOnly constructor() : NotificationService, ErrorService() { +internal class MockNotificationService @TestOnly constructor() : NotificationService, ErrorService(), MicroBlogService { override suspend fun notificationTimeline( count: Int, since_id: String?, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt index edadb7118..a0de13c7d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt @@ -20,13 +20,14 @@ */ package com.twidere.twiderex.mock.service +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.TimelineService import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.mock.model.mockIStatus import com.twidere.twiderex.mock.model.toIPaging import org.jetbrains.annotations.TestOnly -internal class MockTimelineService @TestOnly constructor() : TimelineService, ErrorService() { +internal class MockTimelineService @TestOnly constructor() : TimelineService, MicroBlogService, ErrorService() { override suspend fun favorites( user_id: String, count: Int, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt new file mode 100644 index 000000000..4ac1bd5c6 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt @@ -0,0 +1,72 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockNotificationService +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class NotificationRepositoryTest { + + @Test + fun notification_AddLatestNotificationCursorAfterSuccess() = runBlocking { + val repo = NotificationRepository(MockCacheDatabase()) + val service = MockNotificationService() + val accountKey = MicroBlogKey.twitter("test") + var list = repo.activities( + accountKey = accountKey, + service = service + ) + assertNotNull(repo.findCursor(accountKey, NotificationCursorType.General)) + assert(list.isEmpty()) + list = repo.activities( + accountKey = accountKey, + service = service + ) + assert(list.isNotEmpty()) + assertEquals(list.maxOf { it.timestamp }, repo.findCursor(accountKey, NotificationCursorType.General)?.timestamp) + } + + @Test + fun mention_AddLatestNotificationCursorAfterSuccess() = runBlocking { + val repo = NotificationRepository(MockCacheDatabase()) + val service = MockTimelineService() + val accountKey = MicroBlogKey.twitter("test") + var list = repo.activities( + accountKey = accountKey, + service = service + ) + assertNotNull(repo.findCursor(accountKey, NotificationCursorType.Mentions)) + assert(list.isEmpty()) + list = repo.activities( + accountKey = accountKey, + service = service + ) + assert(list.isNotEmpty()) + assertEquals(list.maxOf { it.timestamp }, repo.findCursor(accountKey, NotificationCursorType.Mentions)?.timestamp) + } +} From ad799ea49916ed6b258c976807e158c42d277fef Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 17 Aug 2021 16:22:59 +0800 Subject: [PATCH 066/615] add test for rest repositories --- .../twiderex/repository/SearchRepository.kt | 2 +- .../repository/ListsUsersRepositoryTest.kt | 2 +- .../repository/NotificationRepositoryTest.kt | 2 +- .../repository/ReactionRepositoryTest.kt | 51 +++++++++ .../repository/SearchRepositoryTest.kt | 85 +++++++++++++++ .../repository/StatusRepositoryTest.kt | 101 ++++++++++++++++++ 6 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt index ca08d8b15..b1d28e121 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt @@ -44,7 +44,7 @@ class SearchRepository( } ?: UiSearch( content = content, lastActive = System.currentTimeMillis(), - saved = false, + saved = saved, accountKey = accountKey ) database.searchDao().insertAll( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt index 16c33c6d6..e4ddb30b4 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals -class ListsUsersRepositoryTest { +internal class ListsUsersRepositoryTest { @Test fun addMembers_updateToMemoryCacheWhenSuccess() = runBlocking { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt index 4ac1bd5c6..6e658fb89 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt @@ -30,7 +30,7 @@ import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class NotificationRepositoryTest { +internal class NotificationRepositoryTest { @Test fun notification_AddLatestNotificationCursorAfterSuccess() = runBlocking { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt new file mode 100644 index 000000000..9cfd02a4b --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class ReactionRepositoryTest { + @Test + fun updateStatusAfterSuccess() = runBlocking { + val database = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val repo = ReactionRepository(database) + val status = mockIStatus().toUi(accountKey = accountKey) + database.statusDao().insertAll(listOf(status)) + assert(!status.liked) + assert(!status.retweeted) + repo.updateReaction( + accountKey = accountKey, + statusKey = status.statusKey, + liked = true, + retweet = true + ) + val updateStatus = database.statusDao().findWithStatusKey(status.statusKey, accountKey) + assertEquals(true, updateStatus?.liked) + assertEquals(true, updateStatus?.retweeted) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt new file mode 100644 index 000000000..5a28fdace --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.mock.db.MockAppDatabase +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class SearchRepositoryTest { + @Test + fun addToSearchHistoryWhenSavedIsFalse() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = SearchRepository(MockAppDatabase()) + repo.addOrUpgrade(content = "query1", accountKey = accountKey) + repo.addOrUpgrade(content = "query2", accountKey = accountKey) + repo.addOrUpgrade(content = "query3", accountKey = accountKey, saved = true) + val searchHistory = repo.searchHistory(accountKey).first() + assertEquals(2, searchHistory.size) + searchHistory.forEach { + assert(!it.saved) + } + } + + @Test + fun addOrUpdateToSavedSearchWhenSavedIsTrue() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = SearchRepository(MockAppDatabase()) + repo.addOrUpgrade(content = "query1", accountKey = accountKey, saved = false) + assert(repo.savedSearch(accountKey).first().isEmpty()) + repo.addOrUpgrade(content = "query1", accountKey = accountKey, saved = true) + repo.addOrUpgrade(content = "query2", accountKey = accountKey, saved = true) + val savedSearch = repo.savedSearch(accountKey).first() + assertEquals(2, savedSearch.size) + savedSearch.forEach { + assert(it.saved) + } + } + + @Test + fun canNotSetSavedToFalseOnSavedSearch() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = SearchRepository(MockAppDatabase()) + repo.addOrUpgrade(content = "query", accountKey = accountKey, saved = true) + repo.addOrUpgrade(content = "query", accountKey = accountKey, saved = false) + val savedSearch = repo.savedSearch(accountKey).first() + assert(savedSearch.isNotEmpty()) + } + + @Test + fun deleteFromDbAfterRemove() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val repo = SearchRepository(MockAppDatabase()) + val savedSearchFlow = repo.savedSearch(accountKey) + val searchHistoryFlow = repo.searchHistory(accountKey) + repo.addOrUpgrade(content = "query saved", accountKey = accountKey, saved = true) + repo.addOrUpgrade(content = "query history", accountKey = accountKey, saved = false) + assert(savedSearchFlow.first().isNotEmpty()) + assert(searchHistoryFlow.first().isNotEmpty()) + repo.remove(repo.savedSearch(accountKey).first().first()) + repo.remove(repo.searchHistory(accountKey).first().first()) + assert(savedSearchFlow.first().isEmpty()) + assert(searchHistoryFlow.first().isEmpty()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt new file mode 100644 index 000000000..b604c18fa --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt @@ -0,0 +1,101 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.mock.service.MockLookUpService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.saveToDb +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class StatusRepositoryTest { + + @Test + fun statusFlowUpdatesWhenUpdateStatusSuccess() = runBlocking { + val database = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val status = mockIStatus().toUi(accountKey) + StatusRepository( + database = database, + nitterService = null + ).let { + database.statusDao().insertAll(listOf(status)) + it.updateStatus( + statusKey = status.statusKey, + accountKey = accountKey, + action = { + it.copy(rawText = "updated") + } + ) + assertEquals("updated", it.loadStatus(statusKey = status.statusKey, accountKey = accountKey).first()?.rawText) + } + } + + @Test + fun removeBothStatusAndPagingTimelineAfterDeleteStatusSuccess() = runBlocking { + val database = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val status = mockIStatus().also { + listOf(it.toPagingTimeline(accountKey, pagingKey = "test")) + .saveToDb(database) + }.toUi(accountKey) + StatusRepository( + database = database, + nitterService = null + ).let { + val statusFlow = it.loadStatus(statusKey = status.statusKey, accountKey = accountKey) + assertEquals(status.statusKey, statusFlow.first()?.statusKey) + assertEquals(status.statusKey, database.pagingTimelineDao().getLatest("test", accountKey)?.status?.statusKey) + it.removeStatus( + statusKey = status.statusKey, + ) + assertNull(statusFlow.first()) + assertNull(database.pagingTimelineDao().getLatest("test", accountKey)) + } + } + + @Test + fun saveToDbAfterLoadSuccessfullyFromNetwork(): Unit = runBlocking { + val database = MockCacheDatabase() + val accountKey = MicroBlogKey.twitter("test") + val repo = StatusRepository( + database = database, + nitterService = null + ) + val mockStatus = mockIStatus().toUi(accountKey) + + repo.loadTweetFromNetwork( + id = mockStatus.statusId, + accountKey = accountKey, + lookupService = MockLookUpService() + ) + + assertNotNull(repo.loadStatus(mockStatus.statusKey, accountKey = accountKey).first()) + } +} From cc8c9cc9db827850370a41ec8e23f158757ef2ca Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 18 Aug 2021 15:35:04 +0800 Subject: [PATCH 067/615] update repository invoke code --- .../twiderex/jobs/common/NotificationJob.kt | 5 ++- .../jobs/compose/TwitterComposeJob.kt | 8 ++--- .../twiderex/jobs/status/MastodonVoteJob.kt | 36 +++++++++++-------- .../twiderex/viewmodel/StatusViewModel.kt | 7 +++- .../viewmodel/compose/ComposeViewModel.kt | 10 ++++-- .../viewmodel/lists/ListsTimelineViewModel.kt | 4 ++- .../viewmodel/lists/ListsUserViewModel.kt | 17 ++++++--- .../viewmodel/lists/ListsViewModel.kt | 23 ++++++++---- .../mastodon/MastodonHashtagViewModel.kt | 4 ++- .../viewmodel/trend/TrendViewModel.kt | 6 +++- .../user/UserFavouriteTimelineViewModel.kt | 8 ++++- .../viewmodel/user/UserTimelineViewModel.kt | 4 ++- .../worker/status/UpdateStatusWorker.kt | 14 ++++---- 13 files changed, 98 insertions(+), 48 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index 55c036a0e..d4e02415e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -59,7 +59,10 @@ class NotificationJob( .map { account -> launch { val activities = try { - repository.activities(account) + repository.activities( + accountKey = account.accountKey, + service = account.service + ) } catch (e: Throwable) { // Ignore any exception cause there's no needs ot handle it emptyList() diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 7d867c5d0..71cba41a6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.jobs.compose import android.content.Context import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator @@ -78,9 +78,9 @@ class TwitterComposeJob constructor( lat = lat, long = long, exclude_reply_user_ids = composeData.excludedReplyUserIds - ).toDbStatusWithReference(accountKey) - listOf(result).saveToDb(cacheDatabase) - return result.toUi(accountKey) + ).toUi(accountKey) + cacheDatabase.statusDao().insertAll(listOf(result)) + return result } override suspend fun uploadImage( diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index c86fb307c..0bc5be565 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -51,29 +51,35 @@ class MastodonVoteJob( val pollId = status.poll.id var originPoll: Poll? = null statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { - it.extra = it.extra.fromJson() - .let { extra -> - originPoll = extra.poll - extra.copy( - poll = extra.poll?.copy( - voted = true, - ownVotes = votes + it.copy( + extra = it.extra.fromJson() + .let { extra -> + originPoll = extra.poll + extra.copy( + poll = extra.poll?.copy( + voted = true, + ownVotes = votes + ) ) - ) - }.json() + }.json() + ) } try { val newPoll = service.vote(pollId, votes) statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { - it.extra = it.extra.fromJson().copy( - poll = newPoll - ).json() + it.copy( + extra = it.extra.fromJson().copy( + poll = newPoll + ).json() + ) } } catch (e: Throwable) { statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { - it.extra = it.extra.fromJson().copy( - poll = originPoll - ).json() + it.copy( + extra = it.extra.fromJson().copy( + poll = originPoll + ).json() + ) } e.notify(inAppNotification) throw e diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index fa6dd83db..5065f5bdc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -44,7 +44,12 @@ class StatusViewModel @AssistedInject constructor( } val source by lazy { - statusRepository.conversation(statusKey = statusKey, account = account) + statusRepository.conversation( + statusKey = statusKey, + accountKey = account.accountKey, + platformType = account.type, + service = account.service + ) .cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 007b8c09b..57a7d1848 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -294,14 +294,18 @@ open class ComposeViewModel @AssistedInject constructor( composeType == ComposeType.Reply ) { val mentions = - status.mastodonExtra.mentions.mapNotNull { it.acct } - .filter { it != account.user.screenName }.map { "@$it" }.let { + status.mastodonExtra?.mentions?.mapNotNull { it.acct } + ?.filter { it != account.user.screenName } + ?.map { "@$it" } + ?.let { if (status.user.userKey != account.user.userKey) { listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it } else { it } - }.distinctBy { it }.takeIf { it.any() } + } + ?.distinctBy { it } + ?.takeIf { it.any() } ?.joinToString(" ", postfix = " ") { it } if (mentions != null) { setText( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index deb79f2e8..023994c61 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.lists import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn +import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository @@ -46,7 +47,8 @@ class ListsTimelineViewModel @AssistedInject constructor( val source by lazy { repository.listTimeline( listKey = listKey, - account = account, + accountKey = account.accountKey, + service = account.service as TimelineService ).cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5a8be9139..46ac4b859 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn +import com.twidere.services.microblog.ListsService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser @@ -50,11 +51,17 @@ class ListsUserViewModel @AssistedInject constructor( } private val members by lazy { - listsUsersRepository.fetchMembers(account = account, listId = listId).cachedIn(viewModelScope) + listsUsersRepository.fetchMembers( + accountKey = account.accountKey, + service = account.service as ListsService, listId = listId + ).cachedIn(viewModelScope) } private val subscribers by lazy { - listsUsersRepository.fetchSubscribers(account = account, listId = listId).cachedIn(viewModelScope) + listsUsersRepository.fetchSubscribers( + accountKey = account.accountKey, + service = account.service as ListsService, listId = listId + ).cachedIn(viewModelScope) } override val source: Flow> @@ -66,7 +73,7 @@ class ListsUserViewModel @AssistedInject constructor( try { viewModelScope.launch { listsUsersRepository.removeMember( - account = account, + service = account.service as ListsService, listId = listId, user = user ) @@ -99,14 +106,14 @@ class ListsAddMemberViewModel @AssistedInject constructor( listsUsersRepository.addMember( listId = listId, user = user, - account = account + service = account.service as ListsService, ) pendingMap[user.userKey] = user } } else { loadingRequest { listsUsersRepository.removeMember( - account = account, + service = account.service as ListsService, listId = listId, user = user ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 87355f54b..64fca8f74 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import androidx.paging.filter +import com.twidere.services.microblog.ListsService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode @@ -48,7 +49,10 @@ class ListsViewModel @AssistedInject constructor( } val source by lazy { - listsRepository.fetchLists(account = account).cachedIn(viewModelScope) + listsRepository.fetchLists( + accountKey = account.accountKey, + service = account.service as ListsService + ).cachedIn(viewModelScope) } val ownerSource by lazy { @@ -107,7 +111,8 @@ class ListsCreateViewModel @AssistedInject constructor( ) { loadingRequest(onResult) { listsRepository.createLists( - account = account, + accountKey = account.accountKey, + service = account.service as ListsService, title = title, description = description, mode = if (private)ListsMode.PRIVATE.value else ListsMode.PUBLIC.value @@ -133,7 +138,7 @@ class ListsModifyViewModel @AssistedInject constructor( var editPrivate = MutableStateFlow(false) val source by lazy { - listsRepository.findListWithListKey(account = account, listKey = listKey) + listsRepository.findListWithListKey(accountKey = account.accountKey, listKey = listKey) } init { @@ -155,7 +160,8 @@ class ListsModifyViewModel @AssistedInject constructor( ) { loadingRequest(onResult) { listsRepository.updateLists( - account = account, + accountKey = account.accountKey, + service = account.service as ListsService, listId = listId, title = title, description = description, @@ -171,7 +177,8 @@ class ListsModifyViewModel @AssistedInject constructor( ) { loadingRequest(onResult) { listsRepository.deleteLists( - account = account, + accountKey = account.accountKey, + service = account.service as ListsService, listKey = listKey, listId = listId, ) @@ -184,7 +191,8 @@ class ListsModifyViewModel @AssistedInject constructor( ) { loadingRequest(onResult) { listsRepository.subscribeLists( - account = account, + accountKey = account.accountKey, + service = account.service as ListsService, listKey = listKey ) } @@ -196,7 +204,8 @@ class ListsModifyViewModel @AssistedInject constructor( ) { loadingRequest(onResult) { listsRepository.unsubscribeLists( - account = account, + accountKey = account.accountKey, + service = account.service as ListsService, listKey = listKey ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 74f06191c..571c6347a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn +import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TimelineRepository import dagger.assisted.Assisted @@ -45,7 +46,8 @@ class MastodonHashtagViewModel @AssistedInject constructor( val source by lazy { repository.mastodonHashtagTimeline( keyword = keyword, - account = account, + accountKey = account.accountKey, + service = account.service as MastodonService ).cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index 76238a0d8..52dba4c82 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.viewmodel.trend import androidx.lifecycle.ViewModel +import com.twidere.services.microblog.TrendService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TrendRepository import dagger.assisted.Assisted @@ -37,6 +38,9 @@ class TrendViewModel @AssistedInject constructor( } val source by lazy { - repository.trendsSource(account) + repository.trendsSource( + accountKey = account.accountKey, + service = account.service as TrendService + ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 3e506d7c4..bb5dbfcce 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.user import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn +import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository @@ -44,6 +45,11 @@ class UserFavouriteTimelineViewModel @AssistedInject constructor( } val source by lazy { - repository.favouriteTimeline(userKey = userKey, account = account).cachedIn(viewModelScope) + repository.favouriteTimeline( + userKey = userKey, + accountKey = account.accountKey, + platformType = account.type, + service = account.service as TimelineService + ).cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 0795012ad..58317ca05 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.user import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn +import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository @@ -48,7 +49,8 @@ class UserTimelineViewModel @AssistedInject constructor( val source by lazy { repository.userTimeline( userKey = userKey, - account = account, + accountKey = account.accountKey, + service = account.service as TimelineService, exclude_replies = exclude_replies, ).cachedIn(viewModelScope) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index d8c186c3f..e1148efe4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -66,14 +66,14 @@ class UpdateStatusWorker @AssistedInject constructor( val retweeted = inputData.getNullableBoolean("retweeted") val retweetCount = inputData.getNullableLong("retweetCount") val likeCount = inputData.getNullableLong("likeCount") - repository.updateReaction(accountKey = accountKey, statusKey = statusKey, liked = liked, retweeted = retweeted) + repository.updateReaction(accountKey = accountKey, statusKey = statusKey, liked = liked, retweet = retweeted) statusRepository.updateStatus(statusKey = statusKey, accountKey = accountKey) { - if (retweetCount != null) { - it.retweetCount = retweetCount - } - if (likeCount != null) { - it.likeCount = likeCount - } + it.copy( + metrics = it.metrics.copy( + retweet = retweetCount ?: it.metrics.retweet, + like = likeCount ?: it.metrics.like + ) + ) } return Result.success() } From 38568f7332aa78702a4bd6ae18cd3a77e10c608c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 19 Aug 2021 15:37:05 +0800 Subject: [PATCH 068/615] migrate android database to androidMain --- .../com/twidere/twiderex/db/DbDMEventTest.kt | 9 +- .../com/twidere/twiderex/db/DbListTest.kt | 5 +- .../com/twidere/twiderex/db/DbTrendTest.kt | 8 +- ...est.kt => RoomAppDatabaseMigrationTest.kt} | 12 +- .../com/twidere/twiderex/db/CacheDatabase.kt | 105 ------ .../twiderex/db/transform/StatusTransform.kt | 203 ----------- .../com/twidere/twiderex/di/AndroidModule.kt | 10 +- .../com/twidere/twiderex/di/JobModule.kt | 8 +- .../twidere/twiderex/di/RepositoryModule.kt | 22 +- .../com/twidere/twiderex/di/TwidereModule.kt | 4 +- .../twiderex/extensions/PagingExtensions.kt | 3 +- .../jobs/compose/MastodonComposeJob.kt | 4 +- .../jobs/compose/TwitterComposeJob.kt | 4 +- .../twiderex/jobs/dm/DirectMessageSendJob.kt | 10 +- .../jobs/dm/TwitterDirectMessageSendJob.kt | 8 +- .../twiderex/repository/AccountRepository.kt | 4 +- .../twiderex/utils/MastodonEmojiCache.kt | 1 - .../twiderex/utils/PlatformResolver.kt | 6 +- .../viewmodel/search/SearchTweetsViewModel.kt | 4 +- .../timeline/HomeTimelineViewModel.kt | 4 +- .../timeline/MentionsTimelineViewModel.kt | 4 +- .../timeline/NotificationTimelineViewModel.kt | 4 +- .../mastodon/FederatedTimelineViewModel.kt | 4 +- .../mastodon/LocalTimelineViewModel.kt | 4 +- .../search/TwitterSearchMediaViewModel.kt | 4 +- .../user/UserMediaTimelineViewModel.kt | 4 +- .../com/twidere/twiderex/mock/MockCenter.kt | 8 +- .../twiderex/mock/db/MockCacheDatabse.kt | 20 +- .../db/MockDirectMessageConversationDao.kt | 10 +- .../mock/db/MockDirectMessageEventDao.kt | 6 +- .../twidere/twiderex/mock/db/MockTrendDao.kt | 4 +- .../twidere/twiderex/mock/db/MockUserDao.kt | 2 +- common/build.gradle.kts | 16 +- .../dataprovider/db/AppDataBaseImpl.kt | 49 +++ .../dataprovider/db/CacheDatabaseImpl.kt | 67 ++++ .../dao/DirectMessageConversationDaoImpl.kt | 59 +++ .../db/dao/DirectMessageEventDaoImpl.kt | 62 ++++ .../dataprovider/db/dao/DraftDaoImpl.kt | 43 +++ .../dataprovider/db/dao/ListsDaoImpl.kt | 62 ++++ .../dataprovider/db/dao/MediaDaoImpl.kt | 16 +- .../db/dao/NotificationCursorDaoImpl.kt | 41 +++ .../db/dao/PagingTimelineDaoImpl.kt | 62 ++++ .../dataprovider/db/dao/SearchDaoImpl.kt | 45 +++ .../dataprovider/db/dao/StatusDaoImpl.kt | 82 +++++ .../dataprovider/db/dao/TrendDaoImpl.kt | 57 +++ .../dataprovider/db/dao/UserDaoImpl.kt | 44 +++ .../twiderex/room/db/RoomAppDatabase.kt | 24 +- .../twiderex/room/db/RoomCacheDatabase.kt | 103 ++++++ .../dao/RoomDirectMessageConversationDao.kt | 8 +- .../room/db/dao/RoomDirectMessageEventDao.kt | 8 +- .../twiderex/room/db/dao/RoomDraftDao.kt | 6 +- .../twiderex/room/db/dao/RoomListsDao.kt | 6 +- .../twiderex/room/db/dao/RoomMediaDao.kt | 6 +- .../room/db/dao/RoomNotificationCursorDao.kt | 10 +- .../room/db/dao/RoomPagingTimelineDao.kt | 8 +- .../twiderex/room/db/dao/RoomReactionDao.kt | 12 +- .../twiderex/room/db/dao/RoomSearchDao.kt | 6 +- .../twiderex/room/db/dao/RoomStatusDao.kt | 8 +- .../room/db/dao/RoomStatusReferenceDao.kt | 8 +- .../twiderex/room/db/dao/RoomTrendDao.kt | 8 +- .../room/db/dao/RoomTrendHistoryDao.kt | 6 +- .../twiderex/room/db/dao/RoomUrlEntityDao.kt | 4 +- .../twiderex/room/db/dao/RoomUserDao.kt | 6 +- .../twidere/twiderex/room}/db/model/Alias.kt | 2 +- .../room}/db/model/DbDMConversation.kt | 6 +- .../twiderex/room}/db/model/DbDMEvent.kt | 10 +- .../twiderex/room}/db/model/DbDraft.kt | 2 +- .../twidere/twiderex/room}/db/model/DbList.kt | 2 +- .../twiderex/room}/db/model/DbMedia.kt | 2 +- .../room}/db/model/DbNotificationCursor.kt | 6 +- .../room}/db/model/DbPagingTimeline.kt | 13 +- .../twiderex/room}/db/model/DbSearch.kt | 2 +- .../twiderex/room}/db/model/DbStatus.kt | 35 +- .../room}/db/model/DbStatusReaction.kt | 2 +- .../room}/db/model/DbStatusReference.kt | 6 +- .../twiderex/room}/db/model/DbTrend.kt | 17 +- .../twiderex/room}/db/model/DbTrendHistory.kt | 2 +- .../twiderex/room}/db/model/DbUrlEntity.kt | 2 +- .../twidere/twiderex/room}/db/model/DbUser.kt | 2 +- .../model/converter/ComposeTypeConverter.kt | 2 +- .../db/model/converter/ExtraConverter.kt | 12 +- .../db/model/converter/MediaTypeConverter.kt | 2 +- .../model/converter/MicroBlogKeyConverter.kt | 2 +- .../NotificationCursorTypeConverter.kt | 10 +- .../converter/NotificationTypeConverter.kt | 2 +- .../model/converter/PlatformTypeConverter.kt | 2 +- .../db/model/converter/StringListConverter.kt | 2 +- .../converter/UserTimelineTypeConverter.kt | 4 +- .../room}/db/transform/AccountTransform.kt | 2 +- .../db/transform/DmConversationTransform.kt | 51 ++- .../room/db/transform/DraftTransform.kt | 38 +- .../room}/db/transform/EmojiTransform.kt | 2 +- .../room}/db/transform/ListTransform.kt | 20 +- .../room}/db/transform/MediaTransform.kt | 21 +- .../transform/NotificationCursorTransform.kt | 54 +++ .../room/db/transform/SearchTransform.kt | 23 +- .../room/db/transform/StatusTransform.kt | 345 ++++++++++++++++++ .../room/db/transform/TrendTransform.kt | 74 ++++ .../room/db/transform/UrlEntityTransform.kt | 49 +++ .../room}/db/transform/UserTransform.kt | 75 +++- .../room}/db/transform/WorkDataTransform.kt | 0 .../com/twidere/twiderex/db/dao/StatusDao.kt | 2 +- .../com/twidere/twiderex/db/dao/TrendDao.kt | 4 +- .../paging/mediator/trend/TrendMediator.kt | 2 +- .../twiderex/repository/StatusRepository.kt | 2 - .../twiderex/mock/db/dao/MockStatusDao.kt | 2 +- .../twiderex/mock/db/dao/MockTrendDao.kt | 4 +- 107 files changed, 1751 insertions(+), 618 deletions(-) rename android/src/androidTest/java/com/twidere/twiderex/db/{AppDatabaseMigrationTest.kt => RoomAppDatabaseMigrationTest.kt} (90%) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt rename android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt (74%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt rename android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt (77%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt rename android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt (93%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt (93%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt (92%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt (95%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt (91%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt (80%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt (92%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt (82%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt (94%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt (91%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt (88%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt (90%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt (90%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt (93%) rename android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt (92%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/Alias.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbDMConversation.kt (94%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbDMEvent.kt (91%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbDraft.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbList.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbMedia.kt (97%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbNotificationCursor.kt (91%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbPagingTimeline.kt (85%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbSearch.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbStatus.kt (85%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbStatusReaction.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbStatusReference.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbTrend.kt (80%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbTrendHistory.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbUrlEntity.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/DbUser.kt (98%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/ComposeTypeConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/ExtraConverter.kt (85%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/MediaTypeConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/MicroBlogKeyConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/NotificationCursorTypeConverter.kt (74%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/NotificationTypeConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/PlatformTypeConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/StringListConverter.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/model/converter/UserTimelineTypeConverter.kt (90%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/AccountTransform.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/DmConversationTransform.kt (57%) rename android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt (55%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/EmojiTransform.kt (96%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/ListTransform.kt (69%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/MediaTransform.kt (69%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt rename android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt (60%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/UserTransform.kt (55%) rename {android/src/main/kotlin/com/twidere/twiderex => common/src/androidMain/kotlin/com/twidere/twiderex/room}/db/transform/WorkDataTransform.kt (100%) diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt index dab27abc7..7d98b2a58 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt @@ -36,11 +36,12 @@ import com.twidere.services.twitter.model.PurpleMedia import com.twidere.services.twitter.model.User import com.twidere.twiderex.db.mapper.toDbDirectMessage import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDMConversation.Companion.saveToDb import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.db.model.DbDMEventWithAttachments.Companion.saveToDb import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbDMConversation +import com.twidere.twiderex.room.db.model.DbDMConversation.Companion.saveToDb import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.After @@ -54,7 +55,7 @@ import java.util.concurrent.Executors @RunWith(AndroidJUnit4::class) class DbDMEventTest { - private lateinit var cacheDatabase: CacheDatabase + private lateinit var cacheDatabase: RoomCacheDatabase private val user1AccountKey = MicroBlogKey.twitter("1") private val user2AccountKey = MicroBlogKey.twitter("2") private val conversationCount = 5 @@ -64,7 +65,7 @@ class DbDMEventTest { @Before fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), CacheDatabase::class.java) + cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() runBlocking { for (i in 0 until conversationCount) { diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt index 795326b83..b299aa83d 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt @@ -32,6 +32,7 @@ import com.twidere.services.twitter.model.User import com.twidere.twiderex.db.dao.ListsDao import com.twidere.twiderex.db.mapper.toDbList import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking import org.junit.After @@ -45,7 +46,7 @@ import java.util.concurrent.Executors @RunWith(AndroidJUnit4::class) class DbListTest { private lateinit var listsDao: ListsDao - private lateinit var cacheDatabase: CacheDatabase + private lateinit var cacheDatabase: RoomCacheDatabase private val twitterAccountKey = MicroBlogKey.twitter("123") private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") private val originData = mutableListOf() @@ -56,7 +57,7 @@ class DbListTest { @Before fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), CacheDatabase::class.java) + cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() listsDao = cacheDatabase.listsDao() for (i in 0 until twitterCount) { diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt index 49c706c17..70a695825 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt @@ -27,9 +27,9 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.twidere.services.mastodon.model.TrendHistory import com.twidere.twiderex.db.mapper.toDbTrend -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.db.model.saveToDb import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbTrendWithHistory import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert @@ -44,7 +44,7 @@ typealias MastodonTrend = com.twidere.services.mastodon.model.Trend @RunWith(AndroidJUnit4::class) class DbTrendTest { - private lateinit var cacheDatabase: CacheDatabase + private lateinit var cacheDatabase: RoomCacheDatabase private val twitterAccountKey = MicroBlogKey.twitter("123") private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") private val trends = mutableListOf() @@ -57,7 +57,7 @@ class DbTrendTest { @Before fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), CacheDatabase::class.java) + cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() for (i in 0 until twitterTrendCount) { trends.add( diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt similarity index 90% rename from android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt rename to android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt index e8d735570..5a5c44688 100644 --- a/android/src/androidTest/java/com/twidere/twiderex/db/AppDatabaseMigrationTest.kt +++ b/android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt @@ -32,7 +32,7 @@ import java.io.IOException import java.util.UUID @RunWith(AndroidJUnit4::class) -class AppDatabaseMigrationTest { +class RoomAppDatabaseMigrationTest { private val TEST_DB = "migration-test" @get:Rule @@ -56,7 +56,10 @@ class AppDatabaseMigrationTest { close() } - helper.runMigrationsAndValidate(TEST_DB, 2, true, AppDatabase_Migration_1_2).apply { + helper.runMigrationsAndValidate( + TEST_DB, 2, true, + com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 + ).apply { query(SimpleSQLiteQuery("SELECT * FROM draft WHERE _id = ?", arrayOf(id))).apply { moveToFirst() getString(getColumnIndex("_id")).also { @@ -87,7 +90,10 @@ class AppDatabaseMigrationTest { close() } - helper.runMigrationsAndValidate(TEST_DB, 3, true, AppDatabase_Migration_2_3).apply { + helper.runMigrationsAndValidate( + TEST_DB, 3, true, + com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 + ).apply { query(SimpleSQLiteQuery("SELECT * FROM search WHERE _id = ?", arrayOf(id))).apply { moveToFirst() getString(getColumnIndex("_id")).also { diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt deleted file mode 100644 index 71414330e..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/db/CacheDatabase.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.room.Database -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import com.twidere.twiderex.db.dao.DirectMessageConversationDao -import com.twidere.twiderex.db.dao.DirectMessageEventDao -import com.twidere.twiderex.db.dao.ListsDao -import com.twidere.twiderex.db.dao.MediaDao -import com.twidere.twiderex.db.dao.NotificationCursorDao -import com.twidere.twiderex.db.dao.PagingTimelineDao -import com.twidere.twiderex.db.dao.ReactionDao -import com.twidere.twiderex.db.dao.StatusDao -import com.twidere.twiderex.db.dao.StatusReferenceDao -import com.twidere.twiderex.db.dao.TrendDao -import com.twidere.twiderex.db.dao.TrendHistoryDao -import com.twidere.twiderex.db.dao.UrlEntityDao -import com.twidere.twiderex.db.dao.UserDao -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDMEvent -import com.twidere.twiderex.db.model.DbList -import com.twidere.twiderex.db.model.DbMedia -import com.twidere.twiderex.db.model.DbNotificationCursor -import com.twidere.twiderex.db.model.DbPagingTimeline -import com.twidere.twiderex.db.model.DbStatusReaction -import com.twidere.twiderex.db.model.DbStatusReference -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.DbTrend -import com.twidere.twiderex.db.model.DbTrendHistory -import com.twidere.twiderex.db.model.DbUrlEntity -import com.twidere.twiderex.db.model.DbUser -import com.twidere.twiderex.db.model.converter.ExtraConverter -import com.twidere.twiderex.db.model.converter.MediaTypeConverter -import com.twidere.twiderex.db.model.converter.MicroBlogKeyConverter -import com.twidere.twiderex.db.model.converter.NotificationCursorTypeConverter -import com.twidere.twiderex.db.model.converter.NotificationTypeConverter -import com.twidere.twiderex.db.model.converter.PlatformTypeConverter -import com.twidere.twiderex.db.model.converter.StringListConverter -import com.twidere.twiderex.db.model.converter.UserTimelineTypeConverter -import javax.inject.Singleton - -@Singleton -@Database( - entities = [ - DbStatusV2::class, - DbMedia::class, - DbUser::class, - DbStatusReaction::class, - DbPagingTimeline::class, - DbUrlEntity::class, - DbStatusReference::class, - DbList::class, - DbNotificationCursor::class, - DbTrend::class, - DbTrendHistory::class, - DbDMConversation::class, - DbDMEvent::class - ], - version = 20, -) -@TypeConverters( - MicroBlogKeyConverter::class, - PlatformTypeConverter::class, - MediaTypeConverter::class, - UserTimelineTypeConverter::class, - StringListConverter::class, - NotificationTypeConverter::class, - ExtraConverter::class, - NotificationCursorTypeConverter::class, -) -abstract class CacheDatabase : RoomDatabase() { - abstract fun statusDao(): StatusDao - abstract fun mediaDao(): MediaDao - abstract fun userDao(): UserDao - abstract fun reactionDao(): ReactionDao - abstract fun pagingTimelineDao(): PagingTimelineDao - abstract fun urlEntityDao(): UrlEntityDao - abstract fun statusReferenceDao(): StatusReferenceDao - abstract fun listsDao(): ListsDao - abstract fun notificationCursorDao(): NotificationCursorDao - abstract fun trendDao(): TrendDao - abstract fun trendHistoryDao(): TrendHistoryDao - abstract fun directMessageConversationDao(): DirectMessageConversationDao - abstract fun directMessageDao(): DirectMessageEventDao -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt deleted file mode 100644 index b88852a07..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/StatusTransform.kt +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db.transform - -import com.twidere.services.mastodon.model.Mention -import com.twidere.services.mastodon.model.Poll -import com.twidere.twiderex.db.model.DbMastodonStatusExtra -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.model.DbPreviewCard -import com.twidere.twiderex.db.model.DbStatusReaction -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.DbStatusWithMediaAndUser -import com.twidere.twiderex.db.model.DbStatusWithReference -import com.twidere.twiderex.db.model.DbTwitterStatusExtra -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.enums.ReferenceType -import com.twidere.twiderex.model.ui.Option -import com.twidere.twiderex.model.ui.StatusMetrics -import com.twidere.twiderex.model.ui.UiCard -import com.twidere.twiderex.model.ui.UiGeo -import com.twidere.twiderex.model.ui.UiMedia -import com.twidere.twiderex.model.ui.UiPoll -import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.model.ui.UiUrlEntity -import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.model.ui.mastodon.MastodonMention -import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra -import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json - -fun DbStatusV2.toUi( - user: UiUser, - media: List, - url: List, - reaction: DbStatusReaction?, - isGap: Boolean, - referenceStatus: Map = emptyMap(), -): UiStatus { - var poll: UiPoll? = null - var sensitive = false - var spoilerText: String? = null - val extra = when (platformType) { - PlatformType.Twitter -> Json.decodeFromString(extra).toUi() - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> Json.decodeFromString(extra).apply { - poll = this.poll?.toUi() - sensitive = this.sensitive - spoilerText = this.spoilerText - }.toUi() - } - return UiStatus( - statusId = statusId, - htmlText = htmlText, - timestamp = timestamp, - metrics = StatusMetrics( - retweet = retweetCount, - like = likeCount, - reply = replyCount, - ), - retweeted = reaction?.retweeted ?: false, - liked = reaction?.liked ?: false, - geo = UiGeo( - name = placeString ?: "", - lat = null, - long = null - ), - hasMedia = hasMedia, - user = user, - media = media, - isGap = isGap, - source = source, - url = url, - statusKey = statusKey, - rawText = rawText, - platformType = platformType, - extra = extra, - referenceStatus = referenceStatus, - card = previewCard?.toUi(), - poll = poll, - inReplyToStatusId = inReplyToStatusId, - inReplyToUserId = inReplyToStatusId, - sensitive = sensitive, - spoilerText = spoilerText - ) -} - -fun DbStatusWithMediaAndUser.toUi( - accountKey: MicroBlogKey, -): UiStatus { - val reaction = reactions.firstOrNull { it.accountKey == accountKey } - return data.toUi( - user = user.toUi(), - media = media.toUi(), - url = url.toUi(), - isGap = false, - reaction = reaction - ) -} - -fun DbStatusWithReference.toUi( - accountKey: MicroBlogKey, -) = with(status) { - val reaction = reactions.firstOrNull { it.accountKey == accountKey } - data.toUi( - user = user.toUi(), - media = media.toUi(), - isGap = false, - url = url.toUi(), - reaction = reaction, - referenceStatus = references.map { - it.reference.referenceType to it.status.toUi( - accountKey = accountKey - ) - }.toMap() - ) -} - -fun DbPagingTimelineWithStatus.toUi( - accountKey: MicroBlogKey, -) = with(status.status) { - val reaction = reactions.firstOrNull { it.accountKey == accountKey } - data.toUi( - user = user.toUi(), - media = media.toUi(), - isGap = timeline.isGap, - url = url.toUi(), - reaction = reaction, - referenceStatus = status.references.map { - it.reference.referenceType to it.status.toUi( - accountKey = accountKey - ) - }.toMap() - ) -} - -fun DbTwitterStatusExtra.toUi() = TwitterStatusExtra( - reply_settings = reply_settings, - quoteCount = quoteCount -) - -fun DbMastodonStatusExtra.toUi() = MastodonStatusExtra( - type = type, - emoji = emoji.toUi(), - visibility = visibility, - mentions = mentions?.toUi() -) - -fun List.toUi() = map { - MastodonMention( - id = it.id, - username = it.username, - url = it.url, - acct = it.acct - ) -} - -fun DbPreviewCard.toUi() = UiCard( - link = link, - displayLink = displayLink, - title = title, - description = desc, - image = image -) - -fun Poll.toUi() = id?.let { - UiPoll( - id = it, - options = options?.map { option -> - Option( - text = option.title ?: "", - count = option.votesCount ?: 0 - ) - } ?: emptyList(), - expired = expired ?: false, - expiresAt = expiresAt?.time, - multiple = multiple ?: false, - voted = voted ?: false, - votersCount = votersCount, - ownVotes = ownVotes, - votesCount = votesCount - ) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt index e5e28525f..5358b62f5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt @@ -30,9 +30,9 @@ import androidx.core.app.NotificationManagerCompat import androidx.room.Room import androidx.work.WorkManager import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.AppDatabase_Migration_1_2 -import com.twidere.twiderex.db.AppDatabase_Migration_2_3 -import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 +import com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -57,8 +57,8 @@ object AndroidModule { @Singleton @Provides - fun provideCacheDatabase(@ApplicationContext context: Context): CacheDatabase = - Room.databaseBuilder(context, CacheDatabase::class.java, "twiderex-db") + fun provideCacheDatabase(@ApplicationContext context: Context): RoomCacheDatabase = + Room.databaseBuilder(context, RoomCacheDatabase::class.java, "twiderex-db") .fallbackToDestructiveMigration() .build() diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt index 86883f273..45956d7a7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.di import android.content.Context -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.jobs.common.DownloadMediaJob import com.twidere.twiderex.jobs.common.NotificationJob import com.twidere.twiderex.jobs.common.ShareMediaJob @@ -49,6 +48,7 @@ import com.twidere.twiderex.repository.DirectMessageRepository import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.repository.NotificationRepository import com.twidere.twiderex.repository.StatusRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -209,7 +209,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: CacheDatabase, + cacheDatabase: RoomCacheDatabase, ): TwitterDirectMessageSendJob = TwitterDirectMessageSendJob( context = context, accountRepository = accountRepository, @@ -224,7 +224,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: CacheDatabase, + cacheDatabase: RoomCacheDatabase, exifScrambler: ExifScrambler, statusRepository: StatusRepository, remoteNavigator: RemoteNavigator @@ -245,7 +245,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: CacheDatabase, + cacheDatabase: RoomCacheDatabase, exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator ): MastodonComposeJob = MastodonComposeJob( diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index eac42a046..74c4780fb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -25,7 +25,6 @@ import coil.imageLoader import coil.util.CoilUtils import com.twidere.services.nitter.NitterService import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.CacheRepository import com.twidere.twiderex.repository.DirectMessageRepository @@ -40,6 +39,7 @@ import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.repository.TimelineRepository import com.twidere.twiderex.repository.TrendRepository import com.twidere.twiderex.repository.UserRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -61,7 +61,7 @@ object RepositoryModule { @Provides fun provideCacheRepository( - database: CacheDatabase, + database: RoomCacheDatabase, appDatabase: AppDatabase, @ApplicationContext context: Context, ): CacheRepository = CacheRepository( @@ -74,7 +74,7 @@ object RepositoryModule { @Provides fun provideStatusRepository( - database: CacheDatabase, + database: RoomCacheDatabase, nitterService: NitterService?, ): StatusRepository = StatusRepository( database = database, @@ -82,22 +82,22 @@ object RepositoryModule { ) @Provides - fun provideReactionRepository(database: CacheDatabase): ReactionRepository = + fun provideReactionRepository(database: RoomCacheDatabase): ReactionRepository = ReactionRepository(database = database) @Provides - fun provideTimelineRepository(database: CacheDatabase): TimelineRepository = + fun provideTimelineRepository(database: RoomCacheDatabase): TimelineRepository = TimelineRepository(database = database) @Provides fun provideUserRepository( - database: CacheDatabase, + database: RoomCacheDatabase, accountRepository: AccountRepository ): UserRepository = UserRepository(database = database, accountRepository = accountRepository) @Provides fun provideListsRepository( - database: CacheDatabase + database: RoomCacheDatabase ) = ListsRepository(database = database) @Provides @@ -105,21 +105,21 @@ object RepositoryModule { @Provides fun provideNotificationRepository( - database: CacheDatabase, + database: RoomCacheDatabase, ): NotificationRepository = NotificationRepository(database = database) @Provides fun provideTrendRepository( - database: CacheDatabase + database: RoomCacheDatabase ) = TrendRepository(database = database) @Provides fun provideDirectMessageRepository( - database: CacheDatabase + database: RoomCacheDatabase ) = DirectMessageRepository(database = database) @Provides fun provideDirectMediaRepository( - database: CacheDatabase + database: RoomCacheDatabase ) = MediaRepository(database = database) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt index 4c9d4d910..de4d70601 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -28,7 +28,6 @@ import androidx.work.WorkManager import com.twidere.services.nitter.NitterService import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver @@ -41,6 +40,7 @@ import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.utils.PlatformResolver import dagger.Module import dagger.Provides @@ -66,7 +66,7 @@ object TwidereModule { @Singleton @Provides - fun providePlatformResolver(database: CacheDatabase): PlatformResolver = + fun providePlatformResolver(database: RoomCacheDatabase): PlatformResolver = PlatformResolver(database = database) @Provides diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt index fb8988852..cb4eb8046 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt @@ -22,9 +22,8 @@ package com.twidere.twiderex.extensions import androidx.paging.Pager import androidx.paging.map -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus -import com.twidere.twiderex.db.transform.toUi import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbPagingTimelineWithStatus import kotlinx.coroutines.flow.map fun Pager.toUi(accountKey: MicroBlogKey) = diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index ff33403cd..80443796b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -25,7 +25,6 @@ import com.twidere.services.mastodon.MastodonService import com.twidere.services.mastodon.model.PostPoll import com.twidere.services.mastodon.model.PostStatus import com.twidere.services.mastodon.model.Visibility -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.db.mapper.toDbStatusWithReference import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver @@ -37,6 +36,7 @@ import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase import java.io.File import java.net.URI @@ -47,7 +47,7 @@ class MastodonComposeJob( exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator, private val fileResolver: FileResolver, - private val cacheDatabase: CacheDatabase, + private val cacheDatabase: RoomCacheDatabase, ) : ComposeJob( context, accountRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 71cba41a6..2fe421fd3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.jobs.compose import android.content.Context import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.dataprovider.mapper.toUi -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator @@ -34,6 +33,7 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase class TwitterComposeJob constructor( context: Context, @@ -43,7 +43,7 @@ class TwitterComposeJob constructor( remoteNavigator: RemoteNavigator, private val statusRepository: StatusRepository, private val fileResolver: FileResolver, - private val cacheDatabase: CacheDatabase, + private val cacheDatabase: RoomCacheDatabase, ) : ComposeJob( context, accountRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index 244d773c3..b4fb8050b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -25,12 +25,8 @@ import android.graphics.BitmapFactory import androidx.room.withTransaction import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.R -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbDMEvent -import com.twidere.twiderex.db.model.DbDMEvent.Companion.saveToDb import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.db.model.DbDMEventWithAttachments.Companion.saveToDb -import com.twidere.twiderex.db.model.DbMedia import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey @@ -42,12 +38,16 @@ import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.notification.notificationChannelId import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbDMEvent +import com.twidere.twiderex.room.db.model.DbDMEvent.Companion.saveToDb +import com.twidere.twiderex.room.db.model.DbMedia import java.net.URI import java.util.UUID abstract class DirectMessageSendJob( private val applicationContext: Context, - protected val cacheDatabase: CacheDatabase, + protected val cacheDatabase: RoomCacheDatabase, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, protected val fileResolver: FileResolver, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt index 4b1b4615a..fe2feb04f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt @@ -23,24 +23,24 @@ package com.twidere.twiderex.jobs.dm import android.content.Context import com.twidere.services.microblog.LookupService import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.db.mapper.autolink import com.twidere.twiderex.db.mapper.toDbDirectMessage import com.twidere.twiderex.db.mapper.toDbUser import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbUser class TwitterDirectMessageSendJob( context: Context, accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: CacheDatabase, + cacheDatabase: RoomCacheDatabase, ) : DirectMessageSendJob( context, cacheDatabase, accountRepository, notificationManager, fileResolver ) { @@ -59,7 +59,7 @@ class TwitterDirectMessageSendJob( sender = lookUpUser(cacheDatabase, sendData.accountKey, service) ) ?: throw Error() - private suspend fun lookUpUser(database: CacheDatabase, userKey: MicroBlogKey, service: TwitterService): DbUser { + private suspend fun lookUpUser(database: RoomCacheDatabase, userKey: MicroBlogKey, service: TwitterService): DbUser { return database.userDao().findWithUserKey(userKey) ?: let { val user = (service as LookupService).lookupUser(userKey.id) .toDbUser(userKey) diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 234ef04f4..d575a9c66 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.repository import android.accounts.Account import android.accounts.AccountManager import android.os.Build -import com.twidere.twiderex.db.transform.toAndroid -import com.twidere.twiderex.db.transform.toTwidere import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.room.db.transform.toAndroid +import com.twidere.twiderex.room.db.transform.toTwidere import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import kotlinx.coroutines.flow.MutableStateFlow diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt index 5e12660e8..6b4f35d28 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.utils import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.db.transform.toUi import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.ui.UiEmojiCategory import kotlinx.coroutines.flow.Flow diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt index d32a78e55..6ef89d020 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt @@ -21,16 +21,16 @@ package com.twidere.twiderex.utils import androidx.compose.runtime.staticCompositionLocalOf -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.room.db.RoomCacheDatabase import javax.inject.Inject class PlatformResolver @Inject constructor( - private val database: CacheDatabase, + private val database: RoomCacheDatabase, ) { suspend fun resolveStatus(statusKey: MicroBlogKey): PlatformType? { - return database.statusDao().findWithStatusKey(key = statusKey)?.platformType + return database.statusDao().findWithStatusKey(statusKey = statusKey)?.platformType } suspend fun resolveUser(userKey: MicroBlogKey): PlatformType? { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index b1051b9c3..cbac25759 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -25,16 +25,16 @@ import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map class SearchTweetsViewModel @AssistedInject constructor( - val database: CacheDatabase, + val database: RoomCacheDatabase, @Assisted private val account: AccountDetails, @Assisted keyword: String, ) : ViewModel() { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index b89573308..328474f49 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class HomeTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: CacheDatabase, + database: RoomCacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index ae03bd1eb..49fb2c7a9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -22,18 +22,18 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.NotificationRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class MentionsTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: CacheDatabase, + database: RoomCacheDatabase, notificationRepository: NotificationRepository, @Assisted private val account: AccountDetails ) : TimelineViewModel(preferences) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 0361cba34..77c6fccde 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -22,18 +22,18 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.NotificationService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.NotificationRepository +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class NotificationTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: CacheDatabase, + database: RoomCacheDatabase, notificationRepository: NotificationRepository, @Assisted private val account: AccountDetails ) : TimelineViewModel(preferences) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 8990510e0..36e866900 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline.mastodon import android.content.SharedPreferences import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedInject class FederatedTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: CacheDatabase, + database: RoomCacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 8b56dc661..4834bad03 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline.mastodon import android.content.SharedPreferences import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedInject class LocalTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: CacheDatabase, + database: RoomCacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index e399d99d5..9755357a6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -26,16 +26,16 @@ import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map class TwitterSearchMediaViewModel @AssistedInject constructor( - val database: CacheDatabase, + val database: RoomCacheDatabase, @Assisted private val account: AccountDetails, @Assisted keyword: String, ) : ViewModel() { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index cc90fb8eb..3ccdb9721 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -28,7 +28,6 @@ import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia @@ -36,13 +35,14 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.PagingMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.user.UserMediaMediator +import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map class UserMediaTimelineViewModel @AssistedInject constructor( - database: CacheDatabase, + database: RoomCacheDatabase, @Assisted account: AccountDetails, @Assisted userKey: MicroBlogKey, ) : ViewModel() { diff --git a/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt b/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt index 26c268470..427eb200b 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt @@ -21,14 +21,14 @@ package com.twidere.twiderex.mock import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.db.MockRoomCacheDatabase import com.twidere.twiderex.mock.service.MockListsService import com.twidere.twiderex.mock.service.MockTrendService +import com.twidere.twiderex.room.db.RoomCacheDatabase object MockCenter { - fun mockCacheDatabase(): CacheDatabase { - return MockCacheDatabase() + fun mockCacheDatabase(): RoomCacheDatabase { + return MockRoomCacheDatabase() } fun mockListsService(): MicroBlogService { diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt index 7ad01a165..79f3c702c 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt @@ -23,12 +23,7 @@ package com.twidere.twiderex.mock.db import androidx.room.DatabaseConfiguration import androidx.room.InvalidationTracker import androidx.sqlite.db.SupportSQLiteOpenHelper -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.dao.DirectMessageConversationDao -import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.db.dao.ListsDao -import com.twidere.twiderex.db.dao.MediaDao -import com.twidere.twiderex.db.dao.NotificationCursorDao import com.twidere.twiderex.db.dao.PagingTimelineDao import com.twidere.twiderex.db.dao.ReactionDao import com.twidere.twiderex.db.dao.StatusDao @@ -37,11 +32,16 @@ import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.TrendHistoryDao import com.twidere.twiderex.db.dao.UrlEntityDao import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao +import com.twidere.twiderex.room.db.dao.RoomMediaDao +import com.twidere.twiderex.room.db.dao.RoomNotificationCursorDao import java.util.ArrayDeque import java.util.concurrent.Executor import java.util.concurrent.Executors -class MockCacheDatabase : CacheDatabase() { +class MockRoomCacheDatabase : RoomCacheDatabase() { override fun getTransactionExecutor(): Executor { return object : Executor { private var mExecutor = Executors.newSingleThreadExecutor() @@ -86,7 +86,7 @@ class MockCacheDatabase : CacheDatabase() { } private val mediaDao = MockMediaDao() - override fun mediaDao(): MediaDao { + override fun mediaDao(): RoomMediaDao { return mediaDao } @@ -117,7 +117,7 @@ class MockCacheDatabase : CacheDatabase() { return listsDao } - override fun notificationCursorDao(): NotificationCursorDao { + override fun notificationCursorDao(): RoomNotificationCursorDao { TODO("Not yet implemented") } @@ -132,12 +132,12 @@ class MockCacheDatabase : CacheDatabase() { } private val conversationDao = MockDirectMessageConversationDao() - override fun directMessageConversationDao(): DirectMessageConversationDao { + override fun directMessageConversationDao(): RoomDirectMessageConversationDao { return conversationDao } private val dmDao = MockDirectMessageEventDao() - override fun directMessageDao(): DirectMessageEventDao { + override fun directMessageDao(): RoomDirectMessageEventDao { return dmDao } diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt index d7091a843..55922213b 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.mock.db import androidx.paging.PagingSource import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.dao.DirectMessageConversationDao import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.model.DbDMConversation +import com.twidere.twiderex.room.db.model.DbDMEvent +import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage import kotlinx.coroutines.flow.Flow -class MockDirectMessageConversationDao : DirectMessageConversationDao { +class MockDirectMessageConversationDao : RoomDirectMessageConversationDao { val db = mutableListOf() override suspend fun insertAll(conversations: List) { db.addAll(conversations) diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt index 4aa828858..7716433da 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt @@ -22,13 +22,13 @@ package com.twidere.twiderex.mock.db import androidx.paging.PagingSource import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMEvent import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao +import com.twidere.twiderex.room.db.model.DbDMEvent -class MockDirectMessageEventDao : DirectMessageEventDao { +class MockDirectMessageEventDao : RoomDirectMessageEventDao { val db = mutableListOf() override suspend fun insertAll(messages: List) { diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt index 871dad97f..76e0a3019 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt @@ -24,10 +24,10 @@ import androidx.paging.PagingSource import androidx.paging.PagingState import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.db.dao.TrendHistoryDao -import com.twidere.twiderex.db.model.DbTrend import com.twidere.twiderex.db.model.DbTrendHistory -import com.twidere.twiderex.db.model.DbTrendWithHistory import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbTrend +import com.twidere.twiderex.room.db.model.DbTrendWithHistory private val historiesMap = mutableMapOf>() class MockTrendDao : TrendDao { diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt index a2c4f3346..5867cdbb7 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt +++ b/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.mock.db import com.twidere.twiderex.db.dao.UserDao -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbUser import kotlinx.coroutines.flow.Flow class MockUserDao : UserDao { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index fc9685802..ae692395e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -5,6 +5,7 @@ plugins { id("org.jetbrains.compose") version Versions.compose_jb kotlin("plugin.serialization") version Versions.Kotlin.lang id("com.android.library") + kotlin("kapt") } group = Package.group @@ -54,14 +55,27 @@ kotlin { dependencies { api("io.insert-koin:koin-android:${Versions.koin}") api("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") + implementation("androidx.room:room-runtime:${Versions.room}") + implementation("androidx.room:room-ktx:${Versions.room}") + implementation("androidx.room:room-paging:${Versions.room}") + kapt("androidx.room:room-compiler:${Versions.room}") } } - val androidTest by getting + val androidTest by getting { + dependencies { + implementation("androidx.room:room-testing:${Versions.room}") + } + } + val desktopMain by getting val desktopTest by getting } } +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kapt(dependencyNotation: String) { + configurations["kapt"].dependencies.add(project.dependencies.create(dependencyNotation)) +} + android { compileSdk = AndroidSdk.compile sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt new file mode 100644 index 000000000..964ef267a --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db + +import androidx.room.withTransaction +import com.twidere.twiderex.dataprovider.db.dao.DraftDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.SearchDaoImpl +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.room.db.RoomAppDatabase + +internal class AppDataBaseImpl(private val roomDatabase: RoomAppDatabase) : AppDatabase { + private val draftDao = DraftDaoImpl(roomDatabase.draftDao()) + private val searchDao = SearchDaoImpl(roomDatabase.searchDao()) + override fun draftDao(): DraftDao { + return draftDao + } + + override fun searchDao(): SearchDao { + return searchDao + } + + override suspend fun clearAllTables() { + roomDatabase.clearAllTables() + } + + override suspend fun withTransaction(block: suspend () -> R) = roomDatabase.withTransaction { + block.invoke() + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt new file mode 100644 index 000000000..2ea9a8da8 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt @@ -0,0 +1,67 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db + +import androidx.room.withTransaction +import com.twidere.twiderex.dataprovider.db.dao.DirectMessageConversationDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.DirectMessageEventDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.ListsDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.MediaDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.NotificationCursorDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.PagingTimelineDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.StatusDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.TrendDaoImpl +import com.twidere.twiderex.dataprovider.db.dao.UserDaoImpl +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.room.db.RoomCacheDatabase + +internal class CacheDatabaseImpl(private val roomCacheDatabase: RoomCacheDatabase) : CacheDatabase { + private val statusDao = StatusDaoImpl(roomCacheDatabase) + override fun statusDao() = statusDao + + private val mediaDao = MediaDaoImpl(roomCacheDatabase.mediaDao()) + override fun mediaDao() = mediaDao + + private val userDao = UserDaoImpl(roomCacheDatabase.userDao()) + override fun userDao() = userDao + + private val pagingTimelineDao = PagingTimelineDaoImpl(roomCacheDatabase.pagingTimelineDao()) + override fun pagingTimelineDao() = pagingTimelineDao + + private val listsDao = ListsDaoImpl(roomCacheDatabase.listsDao()) + override fun listsDao() = listsDao + + private val notificationCursorDao = NotificationCursorDaoImpl(roomCacheDatabase.notificationCursorDao()) + override fun notificationCursorDao() = notificationCursorDao + + private val trendDao = TrendDaoImpl(roomCacheDatabase.trendDao(), roomCacheDatabase.trendHistoryDao()) + override fun trendDao() = trendDao + + private val dmConversationDao = DirectMessageConversationDaoImpl(roomCacheDatabase.directMessageConversationDao()) + override fun directMessageConversationDao() = dmConversationDao + + private val dmEventDao = DirectMessageEventDaoImpl(roomCacheDatabase) + override fun directMessageDao() = dmEventDao + + override suspend fun clearAllTables() = roomCacheDatabase.clearAllTables() + + override suspend fun withTransaction(block: suspend () -> R) = roomCacheDatabase.withTransaction(block) +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt new file mode 100644 index 000000000..3c0a3d0e1 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt @@ -0,0 +1,59 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.DirectMessageConversationDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.transform.toDbDMConversation +import com.twidere.twiderex.room.db.transform.toUi +import kotlinx.coroutines.flow.map + +internal class DirectMessageConversationDaoImpl(private val roomConversationDao: RoomDirectMessageConversationDao) : DirectMessageConversationDao { + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + TODO("Not yet implemented") + } + + override fun findWithConversationKeyFlow( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ) = roomConversationDao.findWithConversationKeyFlow(accountKey, conversationKey).map { it?.toUi() } + + override suspend fun findWithConversationKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ) = roomConversationDao.findWithConversationKey(accountKey, conversationKey)?.toUi() + + override suspend fun insertAll(listOf: List) { + roomConversationDao.insertAll(listOf.map { it.toDbDMConversation() }) + } + + override suspend fun find( + accountKey: MicroBlogKey + ) = roomConversationDao.find(accountKey).map { it.toUi() } + + override suspend fun delete(conversation: UiDMConversation) { + roomConversationDao.delete(conversation.toDbDMConversation()) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt new file mode 100644 index 000000000..9fbbda229 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments.Companion.saveToDb +import com.twidere.twiderex.room.db.transform.toDbMEventWithAttachments +import com.twidere.twiderex.room.db.transform.toUi + +internal class DirectMessageEventDaoImpl( + private val roomCacheDatabase: RoomCacheDatabase +) : DirectMessageEventDao { + override fun getPagingSource( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun findWithMessageKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + messageKey: MicroBlogKey + ) = roomCacheDatabase.directMessageDao().findWithMessageKey(accountKey, conversationKey, messageKey)?.toUi() + + override suspend fun delete(message: UiDMEvent) { + roomCacheDatabase.directMessageDao().delete(message.toDbMEventWithAttachments().message) + } + + override suspend fun getMessageCount( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ) = roomCacheDatabase.directMessageDao().getMessageCount(accountKey, conversationKey) + + override suspend fun insertAll(events: List) { + events.map { + it.toDbMEventWithAttachments() + }.saveToDb(roomCacheDatabase) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt new file mode 100644 index 000000000..bb2f795f4 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt @@ -0,0 +1,43 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.room.db.dao.RoomDraftDao +import com.twidere.twiderex.room.db.transform.toDbDraft +import com.twidere.twiderex.room.db.transform.toUiDraft +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class DraftDaoImpl(private val roomDraftDao: RoomDraftDao) : DraftDao { + override fun getAll(): Flow> = roomDraftDao.getAll().map { + it.map { dbDraft -> dbDraft.toUiDraft() } + } + + override fun getDraftCount() = roomDraftDao.getDraftCount() + + override suspend fun insert(it: UiDraft) = roomDraftDao.insertAll(it.toDbDraft()) + + override suspend fun get(draftId: String) = roomDraftDao.get(draftId)?.toUiDraft() + + override suspend fun remove(draft: UiDraft) = roomDraftDao.remove(draft.toDbDraft()) +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt new file mode 100644 index 000000000..7ca1bb8a6 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.ListsDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.room.db.dao.RoomListsDao +import com.twidere.twiderex.room.db.transform.toDbList +import com.twidere.twiderex.room.db.transform.toUi +import kotlinx.coroutines.flow.map + +internal class ListsDaoImpl(private val roomListsDao: RoomListsDao) : ListsDao { + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + TODO("Not yet implemented") + } + + override fun findWithListKeyWithFlow( + listKey: MicroBlogKey, + accountKey: MicroBlogKey + ) = roomListsDao.findWithListKeyWithFlow(listKey, accountKey).map { it?.toUi() } + + override suspend fun insertAll(listOf: List) { + roomListsDao.insertAll(listOf.map { it.toDbList() }) + } + + override suspend fun findWithListKey( + listKey: MicroBlogKey, + accountKey: MicroBlogKey + ) = roomListsDao.findWithListKey(listKey, accountKey)?.toUi() + + override suspend fun update(listOf: List) { + roomListsDao.update(listOf.map { it.toDbList() }) + } + + override suspend fun delete(listOf: List) { + roomListsDao.delete(listOf.map { it.toDbList() }) + } + + override suspend fun clearAll(accountKey: MicroBlogKey) { + roomListsDao.clearAll(accountKey) + } +} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt similarity index 74% rename from android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt index 38e518f56..a5bac3a67 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockMediaDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt @@ -18,17 +18,15 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.mock.db +package com.twidere.twiderex.dataprovider.db.dao import com.twidere.twiderex.db.dao.MediaDao -import com.twidere.twiderex.db.model.DbMedia import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.dao.RoomMediaDao +import com.twidere.twiderex.room.db.transform.toUi -class MockMediaDao : MediaDao { - override suspend fun insertAll(media: List) { - } - - override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { - TODO("Not yet implemented") - } +internal class MediaDaoImpl(private val roomMediaDao: RoomMediaDao) : MediaDao { + override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey) = roomMediaDao.findMediaByBelongToKey( + belongToKey + ).toUi() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt new file mode 100644 index 000000000..0fdfdbd10 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import com.twidere.twiderex.db.dao.NotificationCursorDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import com.twidere.twiderex.room.db.dao.RoomNotificationCursorDao +import com.twidere.twiderex.room.db.transform.toDb +import com.twidere.twiderex.room.db.transform.toDbCursor +import com.twidere.twiderex.room.db.transform.toUi + +internal class NotificationCursorDaoImpl(private val roomNotificationCursorDao: RoomNotificationCursorDao) : NotificationCursorDao { + override suspend fun find( + accountKey: MicroBlogKey, + type: NotificationCursorType + ) = roomNotificationCursorDao.find(accountKey, type.toDb())?.toUi() + + override suspend fun add(notificationCursor: NotificationCursor) { + roomNotificationCursorDao.add(notificationCursor.toDbCursor()) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt new file mode 100644 index 000000000..6edf396a5 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.PagingTimelineDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.room.db.dao.RoomPagingTimelineDao +import com.twidere.twiderex.room.db.transform.toDbPagingTimeline +import com.twidere.twiderex.room.db.transform.toPagingTimeline +import com.twidere.twiderex.room.db.transform.toUi + +internal class PagingTimelineDaoImpl(private val roomPagingTimelineDao: RoomPagingTimelineDao) : PagingTimelineDao { + override fun getPagingSource( + pagingKey: String, + accountKey: MicroBlogKey + ): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun clearAll(pagingKey: String, accountKey: MicroBlogKey) { + roomPagingTimelineDao.clearAll(pagingKey, accountKey) + } + + override suspend fun getLatest( + pagingKey: String, + accountKey: MicroBlogKey + ) = roomPagingTimelineDao.getLatest(pagingKey, accountKey)?.toPagingTimeline(accountKey) + + override suspend fun findWithStatusKey( + maxStatusKey: MicroBlogKey, + accountKey: MicroBlogKey + ) = roomPagingTimelineDao.findWithStatusKey(maxStatusKey, accountKey)?.toUi() + + override suspend fun insertAll(listOf: List) { + roomPagingTimelineDao.insertAll(listOf.map { it.toDbPagingTimeline() }) + } + + override suspend fun delete(statusKey: MicroBlogKey) { + roomPagingTimelineDao.delete(statusKey) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt new file mode 100644 index 000000000..e3ab1c9b8 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt @@ -0,0 +1,45 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.room.db.dao.RoomSearchDao +import com.twidere.twiderex.room.db.transform.toDbSearch +import com.twidere.twiderex.room.db.transform.toUiSearch +import kotlinx.coroutines.flow.map + +internal class SearchDaoImpl(private val roomSearchDao: RoomSearchDao) : SearchDao { + override suspend fun insertAll(search: List) = roomSearchDao.insertAll(search.map { it.toDbSearch() }) + + override fun getAll(accountKey: MicroBlogKey) = roomSearchDao.getAll(accountKey).map { list -> list.map { it.toUiSearch() } } + + override fun getAllHistory(accountKey: MicroBlogKey) = roomSearchDao.getAllHistory(accountKey).map { list -> list.map { it.toUiSearch() } } + + override fun getAllSaved(accountKey: MicroBlogKey) = roomSearchDao.getAllSaved(accountKey).map { list -> list.map { it.toUiSearch() } } + + override suspend fun get(content: String, accountKey: MicroBlogKey) = roomSearchDao.get(content, accountKey)?.toUiSearch() + + override suspend fun remove(search: UiSearch) = roomSearchDao.remove(search.toDbSearch()) + + override suspend fun clear() = roomSearchDao.clear() +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt new file mode 100644 index 000000000..f41509e48 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt @@ -0,0 +1,82 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import com.twidere.twiderex.db.dao.StatusDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbStatusReaction +import com.twidere.twiderex.room.db.model.saveToDb +import com.twidere.twiderex.room.db.transform.toDbStatusWithReference +import com.twidere.twiderex.room.db.transform.toUi +import kotlinx.coroutines.flow.map +import java.util.UUID + +internal class StatusDaoImpl( + private val roomCacheDatabase: RoomCacheDatabase +) : StatusDao { + override suspend fun insertAll(listOf: List, accountKey: MicroBlogKey) { + listOf.map { it.toDbStatusWithReference(accountKey) } + .saveToDb(roomCacheDatabase) + } + + override suspend fun findWithStatusKey( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey + ) = roomCacheDatabase.statusDao().findWithStatusKeyWithReference(statusKey)?.toUi(accountKey) + + override fun findWithStatusKeyWithFlow( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey + ) = roomCacheDatabase.statusDao().findWithStatusKeyWithReferenceFlow(statusKey).map { it?.toUi(accountKey) } + + override suspend fun delete(statusKey: MicroBlogKey) { + roomCacheDatabase.statusDao().delete(statusKey) + roomCacheDatabase.statusReferenceDao().delete(statusKey) + } + + override suspend fun updateAction( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + liked: Boolean?, + retweet: Boolean? + ) { + roomCacheDatabase.reactionDao().findWithStatusKey(statusKey, accountKey).let { + it ?: DbStatusReaction( + _id = UUID.randomUUID().toString(), + statusKey = statusKey, + accountKey = accountKey, + liked = false, + retweeted = false, + ) + }.let { + roomCacheDatabase.reactionDao().insertAll( + listOf( + it.copy( + liked = liked ?: it.liked, + retweeted = retweet ?: it.retweeted + ) + ) + ) + } + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt new file mode 100644 index 000000000..54e9119d9 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt @@ -0,0 +1,57 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.TrendDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.room.db.dao.RoomTrendDao +import com.twidere.twiderex.room.db.dao.RoomTrendHistoryDao +import com.twidere.twiderex.room.db.transform.toDbTrendWithHistory + +internal class TrendDaoImpl( + private val roomTrendDao: RoomTrendDao, + private val roomTrendHistoryDao: RoomTrendHistoryDao +) : TrendDao { + override suspend fun insertAll(trends: List) { + trends.toDbTrendWithHistory().apply { + map { it.trend }.let { + roomTrendDao.insertAll(it) + } + + map { it.history } + .flatten() + .let { + roomTrendHistoryDao.insertAll(it) + } + } + } + + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun clear(accountKey: MicroBlogKey) { + roomTrendDao.clearAll(accountKey) + roomTrendHistoryDao.clearAll(accountKey) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt new file mode 100644 index 000000000..b0154543f --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db.dao + +import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.room.db.dao.RoomUserDao +import com.twidere.twiderex.room.db.transform.toDbUser +import com.twidere.twiderex.room.db.transform.toUi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class UserDaoImpl(private val roomUserDao: RoomUserDao) : UserDao { + override suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? { + return roomUserDao.findWithUserKey(userKey)?.toUi() + } + + override suspend fun insertAll(listOf: List) { + roomUserDao.insertAll(listOf.map { it.toDbUser() }) + } + + override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { + return roomUserDao.findWithUserKeyFlow(userKey).map { it?.toUi() } + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt similarity index 77% rename from android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt index 549779a56..46ed958a7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/AppDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt @@ -18,23 +18,21 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db +package com.twidere.twiderex.room.db import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import com.twidere.twiderex.db.dao.DraftDao -import com.twidere.twiderex.db.dao.SearchDao -import com.twidere.twiderex.db.model.DbDraft -import com.twidere.twiderex.db.model.DbSearch -import com.twidere.twiderex.db.model.converter.ComposeTypeConverter -import com.twidere.twiderex.db.model.converter.MicroBlogKeyConverter -import com.twidere.twiderex.db.model.converter.StringListConverter -import javax.inject.Singleton +import com.twidere.twiderex.room.db.dao.RoomDraftDao +import com.twidere.twiderex.room.db.dao.RoomSearchDao +import com.twidere.twiderex.room.db.model.DbDraft +import com.twidere.twiderex.room.db.model.DbSearch +import com.twidere.twiderex.room.db.model.converter.ComposeTypeConverter +import com.twidere.twiderex.room.db.model.converter.MicroBlogKeyConverter +import com.twidere.twiderex.room.db.model.converter.StringListConverter -@Singleton @Database( entities = [ DbDraft::class, @@ -47,9 +45,9 @@ import javax.inject.Singleton ComposeTypeConverter::class, StringListConverter::class, ) -abstract class AppDatabase : RoomDatabase() { - abstract fun draftDao(): DraftDao - abstract fun searchDao(): SearchDao +abstract class RoomAppDatabase : RoomDatabase() { + abstract fun draftDao(): RoomDraftDao + abstract fun searchDao(): RoomSearchDao } val AppDatabase_Migration_1_2 = object : Migration(1, 2) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt new file mode 100644 index 000000000..2f632c134 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt @@ -0,0 +1,103 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db + +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao +import com.twidere.twiderex.room.db.dao.RoomListsDao +import com.twidere.twiderex.room.db.dao.RoomMediaDao +import com.twidere.twiderex.room.db.dao.RoomNotificationCursorDao +import com.twidere.twiderex.room.db.dao.RoomPagingTimelineDao +import com.twidere.twiderex.room.db.dao.RoomReactionDao +import com.twidere.twiderex.room.db.dao.RoomStatusDao +import com.twidere.twiderex.room.db.dao.RoomStatusReferenceDao +import com.twidere.twiderex.room.db.dao.RoomTrendDao +import com.twidere.twiderex.room.db.dao.RoomTrendHistoryDao +import com.twidere.twiderex.room.db.dao.RoomUrlEntityDao +import com.twidere.twiderex.room.db.dao.RoomUserDao +import com.twidere.twiderex.room.db.model.DbDMConversation +import com.twidere.twiderex.room.db.model.DbDMEvent +import com.twidere.twiderex.room.db.model.DbList +import com.twidere.twiderex.room.db.model.DbMedia +import com.twidere.twiderex.room.db.model.DbNotificationCursor +import com.twidere.twiderex.room.db.model.DbPagingTimeline +import com.twidere.twiderex.room.db.model.DbStatusReaction +import com.twidere.twiderex.room.db.model.DbStatusReference +import com.twidere.twiderex.room.db.model.DbStatusV2 +import com.twidere.twiderex.room.db.model.DbTrend +import com.twidere.twiderex.room.db.model.DbTrendHistory +import com.twidere.twiderex.room.db.model.DbUrlEntity +import com.twidere.twiderex.room.db.model.DbUser +import com.twidere.twiderex.room.db.model.converter.ExtraConverter +import com.twidere.twiderex.room.db.model.converter.MediaTypeConverter +import com.twidere.twiderex.room.db.model.converter.MicroBlogKeyConverter +import com.twidere.twiderex.room.db.model.converter.NotificationCursorTypeConverter +import com.twidere.twiderex.room.db.model.converter.NotificationTypeConverter +import com.twidere.twiderex.room.db.model.converter.PlatformTypeConverter +import com.twidere.twiderex.room.db.model.converter.StringListConverter +import com.twidere.twiderex.room.db.model.converter.UserTimelineTypeConverter + +@Database( + entities = [ + DbStatusV2::class, + DbMedia::class, + DbUser::class, + DbStatusReaction::class, + DbPagingTimeline::class, + DbUrlEntity::class, + DbStatusReference::class, + DbList::class, + DbNotificationCursor::class, + DbTrend::class, + DbTrendHistory::class, + DbDMConversation::class, + DbDMEvent::class + ], + version = 20, +) +@TypeConverters( + MicroBlogKeyConverter::class, + PlatformTypeConverter::class, + MediaTypeConverter::class, + UserTimelineTypeConverter::class, + StringListConverter::class, + NotificationTypeConverter::class, + ExtraConverter::class, + NotificationCursorTypeConverter::class, +) +abstract class RoomCacheDatabase : RoomDatabase() { + abstract fun statusDao(): RoomStatusDao + abstract fun mediaDao(): RoomMediaDao + abstract fun userDao(): RoomUserDao + abstract fun reactionDao(): RoomReactionDao + abstract fun pagingTimelineDao(): RoomPagingTimelineDao + abstract fun urlEntityDao(): RoomUrlEntityDao + abstract fun statusReferenceDao(): RoomStatusReferenceDao + abstract fun listsDao(): RoomListsDao + abstract fun notificationCursorDao(): RoomNotificationCursorDao + abstract fun trendDao(): RoomTrendDao + abstract fun trendHistoryDao(): RoomTrendHistoryDao + abstract fun directMessageConversationDao(): RoomDirectMessageConversationDao + abstract fun directMessageDao(): RoomDirectMessageEventDao +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index 129377068..83f5d1b35 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.paging.PagingSource import androidx.room.Dao @@ -27,14 +27,14 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbDMConversation +import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage import kotlinx.coroutines.flow.Flow import org.jetbrains.annotations.TestOnly @Dao -interface DirectMessageConversationDao { +interface RoomDirectMessageConversationDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(conversations: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index a5a03d44b..6c9b36eec 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.paging.PagingSource import androidx.room.Dao @@ -27,12 +27,12 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.twidere.twiderex.db.model.DbDMEvent -import com.twidere.twiderex.db.model.DbDMEventWithAttachments import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbDMEvent +import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments @Dao -interface DirectMessageEventDao { +interface RoomDirectMessageEventDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(messages: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt index de21b3351..41137903e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt @@ -18,18 +18,18 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbDraft +import com.twidere.twiderex.room.db.model.DbDraft import kotlinx.coroutines.flow.Flow @Dao -interface DraftDao { +interface RoomDraftDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(vararg draft: DbDraft) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index b8174d8b9..0f3d43404 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.paging.PagingSource import androidx.room.Dao @@ -28,12 +28,12 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import androidx.room.Update -import com.twidere.twiderex.db.model.DbList import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbList import kotlinx.coroutines.flow.Flow @Dao -interface ListsDao { +interface RoomListsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(lists: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt index a9fda1e6f..9c6261f83 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt @@ -18,17 +18,17 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbMedia import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbMedia @Dao -interface MediaDao { +interface RoomMediaDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt similarity index 80% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt index b1e1a1f05..e57248124 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt @@ -18,18 +18,18 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbNotificationCursor -import com.twidere.twiderex.db.model.NotificationCursorType import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbNotificationCursor +import com.twidere.twiderex.room.db.model.DbNotificationCursorType @Dao -interface NotificationCursorDao { +interface RoomNotificationCursorDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(lists: List) @@ -37,5 +37,5 @@ interface NotificationCursorDao { suspend fun add(item: DbNotificationCursor) @Query("SELECT * FROM notification_cursor WHERE accountKey == :accountKey AND type == :type") - suspend fun find(accountKey: MicroBlogKey, type: NotificationCursorType): DbNotificationCursor? + suspend fun find(accountKey: MicroBlogKey, type: DbNotificationCursorType): DbNotificationCursor? } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index 844a53948..78aad7fa2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.paging.PagingSource import androidx.room.Dao @@ -27,12 +27,12 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.twidere.twiderex.db.model.DbPagingTimeline -import com.twidere.twiderex.db.model.DbPagingTimelineWithStatus import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbPagingTimeline +import com.twidere.twiderex.room.db.model.DbPagingTimelineWithStatus @Dao -interface PagingTimelineDao { +interface RoomPagingTimelineDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(timeline: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt similarity index 82% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt index cae78059b..d0360f0d3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/ReactionDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt @@ -18,26 +18,20 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbStatusReaction import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbStatusReaction @Dao -interface ReactionDao { +interface RoomReactionDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) @Query("SELECT * FROM status_reactions WHERE statusKey == :statusKey AND accountKey == :accountKey") suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): DbStatusReaction? - suspend fun updateReaction( - statusKey: MicroBlogKey, - accountKey: MicroBlogKey, - liked: Boolean?, - retweeted: Boolean? - ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt index 6cc3edcb7..a03243fc9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt @@ -18,19 +18,19 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbSearch import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbSearch import kotlinx.coroutines.flow.Flow @Dao -interface SearchDao { +interface RoomSearchDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(search: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt index 866cb2685..a109c9ecc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Delete @@ -27,13 +27,13 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import androidx.room.Update -import com.twidere.twiderex.db.model.DbStatusV2 -import com.twidere.twiderex.db.model.DbStatusWithReference import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbStatusV2 +import com.twidere.twiderex.room.db.model.DbStatusWithReference import kotlinx.coroutines.flow.Flow @Dao -interface StatusDao { +interface RoomStatusDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(status: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt similarity index 88% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt index fdb793754..fe2e5fd9a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/StatusReferenceDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt @@ -18,20 +18,20 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.twidere.twiderex.db.model.DbStatusReference -import com.twidere.twiderex.db.model.DbStatusReferenceWithStatus import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ReferenceType +import com.twidere.twiderex.room.db.model.DbStatusReference +import com.twidere.twiderex.room.db.model.DbStatusReferenceWithStatus @Dao -interface StatusReferenceDao { +interface RoomStatusReferenceDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(items: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index 87fb3ff0d..afb7eae99 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.paging.PagingSource import androidx.room.Dao @@ -26,12 +26,12 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction -import com.twidere.twiderex.db.model.DbTrend -import com.twidere.twiderex.db.model.DbTrendWithHistory import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbTrend +import com.twidere.twiderex.room.db.model.DbTrendWithHistory @Dao -interface TrendDao { +interface RoomTrendDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(trends: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt index 37994b898..cde691c28 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/TrendHistoryDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt @@ -18,17 +18,17 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import com.twidere.twiderex.db.model.DbTrendHistory import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbTrendHistory @Dao -interface TrendHistoryDao { +interface RoomTrendHistoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(histories: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt index 299d80ab1..ebcee5595 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/UrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert @@ -26,7 +26,7 @@ import androidx.room.OnConflictStrategy import com.twidere.twiderex.db.model.DbUrlEntity @Dao -interface UrlEntityDao { +interface RoomUrlEntityDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt index 252452254..9c5ea1484 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/dao/UserDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt @@ -18,19 +18,19 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.dao +package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update -import com.twidere.twiderex.db.model.DbUser import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.model.DbUser import kotlinx.coroutines.flow.Flow @Dao -interface UserDao { +interface RoomUserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(user: List) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/Alias.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/Alias.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt index 4ea627f62..12ad8e88f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/Alias.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model typealias Html = String typealias Json = String diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt index 41e1be22a..b1bed3b25 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMConversation.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt @@ -18,15 +18,15 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Embedded import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase @Entity( tableName = "dm_conversation", @@ -51,7 +51,7 @@ data class DbDMConversation( } companion object { - suspend fun List.saveToDb(cacheDatabase: CacheDatabase) { + suspend fun List.saveToDb(cacheDatabase: RoomCacheDatabase) { cacheDatabase.directMessageConversationDao().insertAll(this) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt index 0be5e0ef2..40b6273f7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDMEvent.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt @@ -18,16 +18,16 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Embedded import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.db.model.DbDMEvent.Companion.saveToDb import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbDMEvent.Companion.saveToDb @Entity( tableName = "dm_event", @@ -55,7 +55,7 @@ data class DbDMEvent( get() = if (accountKey == senderAccountKey) recipientAccountKey else senderAccountKey companion object { - suspend fun List.saveToDb(cacheDatabase: CacheDatabase) { + suspend fun List.saveToDb(cacheDatabase: RoomCacheDatabase) { cacheDatabase.directMessageDao().insertAll(this) } } @@ -81,7 +81,7 @@ data class DbDMEventWithAttachments( val sender: DbUser ) { companion object { - suspend fun List.saveToDb(cacheDatabase: CacheDatabase) { + suspend fun List.saveToDb(cacheDatabase: RoomCacheDatabase) { map { it.message }.saveToDb(cacheDatabase) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt index c75936919..6a3c0652a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbDraft.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt index 80f71ea4e..196448c36 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbList.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt index e6b857d73..6e6a2ae5f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbMedia.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt index e9f951504..b25cb05b2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbNotificationCursor.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index @@ -41,12 +41,12 @@ data class DbNotificationCursor( @PrimaryKey val _id: String, val accountKey: MicroBlogKey, - val type: NotificationCursorType, + val type: DbNotificationCursorType, val value: String, val timestamp: Long, ) -enum class NotificationCursorType { +enum class DbNotificationCursorType { General, Mentions, Follower, diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt index ad43018d1..31d2fec65 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbPagingTimeline.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt @@ -18,14 +18,13 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Embedded import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey @Entity( @@ -67,13 +66,3 @@ enum class UserTimelineType { } fun UserTimelineType.pagingKey(accountKey: MicroBlogKey) = "user:$accountKey:$this" - -suspend fun List.saveToDb( - database: CacheDatabase, -) { - - this.map { it.status }.saveToDb(database) - this.map { it.timeline }.let { - database.pagingTimelineDao().insertAll(it) - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt index 2102d8e1f..e2c511551 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbSearch.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt index 51eb7f346..3af2ce9e3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatus.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.compose.runtime.Immutable import androidx.room.Embedded @@ -26,16 +26,14 @@ import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.services.mastodon.model.Card import com.twidere.services.mastodon.model.Emoji import com.twidere.services.mastodon.model.Mention -import com.twidere.services.mastodon.model.Poll -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.TwitterReplySettings +import com.twidere.twiderex.room.db.RoomCacheDatabase import kotlinx.serialization.Serializable @Entity( @@ -69,6 +67,8 @@ data class DbStatusV2( val previewCard: DbPreviewCard? = null, val inReplyToUserId: String? = null, val inReplyToStatusId: String? = null, + val poll: DbPoll? = null, + val spoilerText: String? = null, var extra: Json ) @@ -84,6 +84,27 @@ data class DbPreviewCard( val image: String?, ) +@Immutable +@Serializable +data class DbPoll( + val id: String, + val options: List, + val expiresAt: Long?, + val expired: Boolean, + val multiple: Boolean, + val voted: Boolean, + val votesCount: Long? = null, + val votersCount: Long? = null, + val ownVotes: List? = null, +) + +@Immutable +@Serializable +data class DbPollOption( + val text: String, + val count: Long, +) + @Immutable @Serializable data class DbTwitterStatusExtra( @@ -97,10 +118,6 @@ data class DbMastodonStatusExtra( val type: MastodonStatusType, val emoji: List, val visibility: MastodonVisibility, - val sensitive: Boolean, - val spoilerText: String?, - val poll: Poll?, - val card: Card?, val mentions: List?, ) : DbStatusExtra @@ -118,7 +135,7 @@ data class DbStatusWithMediaAndUser( ) suspend fun List.saveToDb( - database: CacheDatabase + database: RoomCacheDatabase ) { map { it.user }.let { database.userDao().insertAll(it) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt index fe223424a..e4561f55b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReaction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt index 818ba7433..ee8bdf488 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbStatusReference.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt @@ -18,16 +18,16 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Embedded import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ReferenceType +import com.twidere.twiderex.room.db.RoomCacheDatabase import java.util.UUID @Entity( @@ -96,7 +96,7 @@ data class DbStatusWithReference( ) suspend fun List.saveToDb( - database: CacheDatabase + database: RoomCacheDatabase ) { this.map { it.references.map { it.status } + it.status } .flatten() diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt similarity index 80% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt index ccd7930cd..72bba3bd6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrend.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt @@ -18,14 +18,13 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Embedded import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey @Entity( @@ -54,17 +53,3 @@ data class DbTrendWithHistory( ) val history: List, ) - -suspend fun List.saveToDb( - database: CacheDatabase -) { - map { it.trend }.let { - database.trendDao().insertAll(it) - } - - map { it.history } - .flatten() - .let { - database.trendHistoryDao().insertAll(it) - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt index c32099bbe..3267e8644 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbTrendHistory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt index f352297cc..48eb8ccad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUrlEntity.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.room.Entity import androidx.room.Index diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt index a2a3eaed6..9edb48ddb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/DbUser.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model +package com.twidere.twiderex.room.db.model import androidx.compose.runtime.Immutable import androidx.room.Entity diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt index bd912c702..8f203c612 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ComposeTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.ComposeType diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt index 989f560c9..89004bfab 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/ExtraConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt @@ -18,14 +18,14 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter -import com.twidere.twiderex.db.model.DbMastodonStatusExtra -import com.twidere.twiderex.db.model.DbMastodonUserExtra -import com.twidere.twiderex.db.model.DbPreviewCard -import com.twidere.twiderex.db.model.DbTwitterStatusExtra -import com.twidere.twiderex.db.model.DbTwitterUserExtra +import com.twidere.twiderex.room.db.model.DbMastodonStatusExtra +import com.twidere.twiderex.room.db.model.DbMastodonUserExtra +import com.twidere.twiderex.room.db.model.DbPreviewCard +import com.twidere.twiderex.room.db.model.DbTwitterStatusExtra +import com.twidere.twiderex.room.db.model.DbTwitterUserExtra import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt index fbb10fd21..5175afb10 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MediaTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.MediaType diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt index 9d35d6780..18a47d23a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/MicroBlogKeyConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.MicroBlogKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt similarity index 74% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt index 7b49c9a79..b9eea656e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationCursorTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt @@ -18,19 +18,19 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter -import com.twidere.twiderex.db.model.NotificationCursorType +import com.twidere.twiderex.room.db.model.DbNotificationCursorType class NotificationCursorTypeConverter { @TypeConverter - fun fromString(value: String?): NotificationCursorType? { - return value?.let { NotificationCursorType.valueOf(it) } + fun fromString(value: String?): DbNotificationCursorType? { + return value?.let { DbNotificationCursorType.valueOf(it) } } @TypeConverter - fun fromNotificationType(type: NotificationCursorType?): String? { + fun fromNotificationType(type: DbNotificationCursorType?): String? { return type?.name } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt index 8e018b098..10ec6a967 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/NotificationTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.MastodonStatusType diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt index 77fd7cd6f..96db32ee9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/PlatformTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.PlatformType diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt index f09a5974d..a2309dc21 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/StringListConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.utils.fromJson diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt index 4495fd560..b057acc3e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/model/converter/UserTimelineTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt @@ -18,10 +18,10 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.model.converter +package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter -import com.twidere.twiderex.db.model.UserTimelineType +import com.twidere.twiderex.room.db.model.UserTimelineType class UserTimelineTypeConverter { @TypeConverter diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt index b80e5a337..4aad0a455 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/AccountTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform import android.accounts.Account import com.twidere.twiderex.model.TwidereAccount diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt similarity index 57% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt index 7b1490125..f7200cffa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/DmConversationTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt @@ -18,15 +18,16 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbDMConversation -import com.twidere.twiderex.db.model.DbDMEvent -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbDirectMessageConversationWithMessage import com.twidere.twiderex.model.ui.UiDMConversation import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.room.db.model.DbDMConversation +import com.twidere.twiderex.room.db.model.DbDMEvent +import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments +import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage +import java.util.UUID fun DbDMConversation.toUi() = UiDMConversation( accountKey = accountKey, @@ -68,3 +69,43 @@ fun DbDMEventWithAttachments.toUi() = UiDMEvent( urlEntity = urlEntity.toUi(), sender = sender.toUi() ) + +fun UiDMConversation.toDbDMConversation() = DbDMConversation( + accountKey = accountKey, + conversationId = conversationId, + conversationKey = conversationKey, + conversationAvatar = conversationAvatar, + conversationName = conversationName, + conversationSubName = conversationSubName, + conversationType = when (conversationType) { + UiDMConversation.Type.ONE_TO_ONE -> DbDMConversation.Type.ONE_TO_ONE + UiDMConversation.Type.GROUP -> DbDMConversation.Type.GROUP + }, + recipientKey = recipientKey, + _id = UUID.randomUUID().toString() +) + +fun UiDMEvent.toDbMEventWithAttachments() = DbDMEventWithAttachments( + message = DbDMEvent( + _id = UUID.randomUUID().toString(), + accountKey = accountKey, + sortId = sortId, + conversationKey = conversationKey, + messageId = messageId, + messageKey = messageKey, + htmlText = htmlText, + originText = originText, + createdTimestamp = createdTimestamp, + messageType = messageType, + senderAccountKey = senderAccountKey, + recipientAccountKey = recipientAccountKey, + sendStatus = when (sendStatus) { + UiDMEvent.SendStatus.PENDING -> DbDMEvent.SendStatus.PENDING + UiDMEvent.SendStatus.SUCCESS -> DbDMEvent.SendStatus.SUCCESS + UiDMEvent.SendStatus.FAILED -> DbDMEvent.SendStatus.FAILED + }, + ), + media = media.toDbMedia(), + urlEntity = urlEntity.toDbUrl(messageKey), + sender = sender.toDbUser() +) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt similarity index 55% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt index b3880e5da..875ffd8e4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/TrendTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt @@ -18,27 +18,27 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbTrendHistory -import com.twidere.twiderex.db.model.DbTrendWithHistory -import com.twidere.twiderex.model.ui.UiTrend -import com.twidere.twiderex.model.ui.UiTrendHistory +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.room.db.model.DbDraft -fun DbTrendWithHistory.toUi() = UiTrend( - trendKey = trend.trendKey, - displayName = trend.displayName, - url = trend.url, - query = trend.query, - volume = trend.volume, - history = history.map { - it.toUi() - } +internal fun DbDraft.toUiDraft() = UiDraft( + content = content, + draftId = _id, + media = media, + composeType = composeType, + createdAt = createdAt, + statusKey = statusKey, + excludedReplyUserIds = excludedReplyUserIds ) -fun DbTrendHistory.toUi() = UiTrendHistory( - trendKey = trendKey, - day = day, - uses = uses, - accounts = accounts +internal fun UiDraft.toDbDraft() = DbDraft( + content = content, + _id = draftId, + media = media, + composeType = composeType, + createdAt = createdAt, + statusKey = statusKey, + excludedReplyUserIds = excludedReplyUserIds ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt index 055a25046..a07d6d700 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/EmojiTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform import com.twidere.services.mastodon.model.Emoji import com.twidere.twiderex.model.ui.UiEmoji diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt similarity index 69% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt index 475c8bd01..37dd1fe2d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/ListTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt @@ -18,10 +18,11 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbList import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.room.db.model.DbList +import java.util.UUID fun DbList.toUi() = UiList( @@ -36,3 +37,18 @@ fun DbList.toUi() = isFollowed = isFollowed, allowToSubscribe = allowToSubscribe, ) + +fun UiList.toDbList() = + DbList( + listId = id, + ownerId = ownerId, + listKey = listKey, + accountKey = accountKey, + title = title, + description = descriptions, + mode = mode, + replyPolicy = replyPolicy, + isFollowed = isFollowed, + allowToSubscribe = allowToSubscribe, + _id = UUID.randomUUID().toString() + ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt similarity index 69% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt index 60ec6624b..b187b50e6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/MediaTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt @@ -18,10 +18,11 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbMedia import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.room.db.model.DbMedia +import java.util.UUID fun List.toUi() = sortedBy { it.order }.map { UiMedia( @@ -37,3 +38,19 @@ fun List.toUi() = sortedBy { it.order }.map { order = it.order, ) } + +fun List.toDbMedia() = map { + DbMedia( + url = it.url, + belongToKey = it.belongToKey, + mediaUrl = it.mediaUrl, + previewUrl = it.previewUrl?.toString(), + type = it.type, + width = it.width, + height = it.height, + pageUrl = it.pageUrl, + altText = it.altText, + order = it.order, + _id = UUID.randomUUID().toString() + ) +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt new file mode 100644 index 000000000..efb919555 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt @@ -0,0 +1,54 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.transform + +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import com.twidere.twiderex.room.db.model.DbNotificationCursor +import com.twidere.twiderex.room.db.model.DbNotificationCursorType + +fun DbNotificationCursor.toUi() = NotificationCursor( + _id = _id, + accountKey = accountKey, + type = type.toUi(), + value = value, + timestamp = timestamp +) + +fun NotificationCursor.toDbCursor() = DbNotificationCursor( + _id = _id, + accountKey = accountKey, + type = type.toDb(), + value = value, + timestamp = timestamp +) + +fun DbNotificationCursorType.toUi() = when (this) { + DbNotificationCursorType.General -> NotificationCursorType.General + DbNotificationCursorType.Mentions -> NotificationCursorType.Mentions + DbNotificationCursorType.Follower -> NotificationCursorType.Follower +} + +fun NotificationCursorType.toDb() = when (this) { + NotificationCursorType.General -> DbNotificationCursorType.General + NotificationCursorType.Mentions -> DbNotificationCursorType.Mentions + NotificationCursorType.Follower -> DbNotificationCursorType.Follower +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt similarity index 60% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt index 74b07733d..c6da15c56 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/UrlEntityTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt @@ -18,12 +18,23 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbUrlEntity -import com.twidere.twiderex.model.ui.UiUrlEntity +import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.room.db.model.DbSearch +import java.util.UUID -fun DbUrlEntity.toUi() = UiUrlEntity( - url, expandedUrl, displayUrl, title, description, image +internal fun DbSearch.toUiSearch() = UiSearch( + content = content, + lastActive = lastActive, + saved = saved, + accountKey = accountKey +) + +internal fun UiSearch.toDbSearch() = DbSearch( + _id = UUID.randomUUID().toString(), + content = content, + lastActive = lastActive, + saved = saved, + accountKey = accountKey ) -fun List.toUi() = map { it.toUi() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt new file mode 100644 index 000000000..66caf8d7f --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt @@ -0,0 +1,345 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.transform + +import com.twidere.services.mastodon.model.Emoji +import com.twidere.services.mastodon.model.Mention +import com.twidere.services.mastodon.model.Poll +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.enums.ReferenceType +import com.twidere.twiderex.model.paging.PagingTimeLine +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus +import com.twidere.twiderex.model.ui.Option +import com.twidere.twiderex.model.ui.StatusMetrics +import com.twidere.twiderex.model.ui.UiCard +import com.twidere.twiderex.model.ui.UiGeo +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.model.ui.UiPoll +import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.model.ui.UiUrlEntity +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.model.ui.mastodon.MastodonMention +import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra +import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra +import com.twidere.twiderex.room.db.model.DbMastodonStatusExtra +import com.twidere.twiderex.room.db.model.DbPagingTimeline +import com.twidere.twiderex.room.db.model.DbPagingTimelineWithStatus +import com.twidere.twiderex.room.db.model.DbPoll +import com.twidere.twiderex.room.db.model.DbPollOption +import com.twidere.twiderex.room.db.model.DbPreviewCard +import com.twidere.twiderex.room.db.model.DbStatusReaction +import com.twidere.twiderex.room.db.model.DbStatusReference +import com.twidere.twiderex.room.db.model.DbStatusReferenceWithStatus +import com.twidere.twiderex.room.db.model.DbStatusV2 +import com.twidere.twiderex.room.db.model.DbStatusWithMediaAndUser +import com.twidere.twiderex.room.db.model.DbStatusWithReference +import com.twidere.twiderex.room.db.model.DbTwitterStatusExtra +import com.twidere.twiderex.utils.fromJson +import com.twidere.twiderex.utils.json +import java.util.UUID + +fun DbStatusV2.toUi( + user: UiUser, + media: List, + url: List, + reaction: DbStatusReaction?, + isGap: Boolean, + referenceStatus: Map = emptyMap(), +): UiStatus { + val extra = when (platformType) { + PlatformType.Twitter -> extra.fromJson().toUi() + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> extra.fromJson().toUi() + } + return UiStatus( + statusId = statusId, + htmlText = htmlText, + timestamp = timestamp, + metrics = StatusMetrics( + retweet = retweetCount, + like = likeCount, + reply = replyCount, + ), + retweeted = reaction?.retweeted ?: false, + liked = reaction?.liked ?: false, + geo = UiGeo( + name = placeString ?: "", + lat = null, + long = null + ), + hasMedia = hasMedia, + user = user, + media = media, + isGap = isGap, + source = source, + url = url, + statusKey = statusKey, + rawText = rawText, + platformType = platformType, + extra = extra, + referenceStatus = referenceStatus, + card = previewCard?.toUi(), + poll = poll?.toUi(), + inReplyToStatusId = inReplyToStatusId, + inReplyToUserId = inReplyToStatusId, + sensitive = is_possibly_sensitive, + spoilerText = spoilerText + ) +} + +fun DbStatusWithMediaAndUser.toUi( + accountKey: MicroBlogKey, +): UiStatus { + val reaction = reactions.firstOrNull { it.accountKey == accountKey } + return data.toUi( + user = user.toUi(), + media = media.toUi(), + url = url.toUi(), + isGap = false, + reaction = reaction + ) +} + +fun DbStatusWithReference.toUi( + accountKey: MicroBlogKey, +) = with(status) { + val reaction = reactions.firstOrNull { it.accountKey == accountKey } + data.toUi( + user = user.toUi(), + media = media.toUi(), + isGap = false, + url = url.toUi(), + reaction = reaction, + referenceStatus = references.map { + it.reference.referenceType to it.status.toUi( + accountKey = accountKey + ) + }.toMap() + ) +} + +fun UiStatus.toDbStatusWithReference(accountKey: MicroBlogKey) = DbStatusWithReference( + status = toDbStatusWithMediaAndUser(accountKey), + references = referenceStatus.map { entry -> + DbStatusReferenceWithStatus( + status = entry.value.toDbStatusWithMediaAndUser(accountKey), + reference = DbStatusReference( + _id = UUID.randomUUID().toString(), + referenceType = entry.key, + statusKey = statusKey, + referenceStatusKey = entry.value.statusKey + ) + ) + } +) + +fun UiStatus.toDbStatusWithMediaAndUser(accountKey: MicroBlogKey) = DbStatusWithMediaAndUser( + data = toDbStatusV2(), + media = media.toDbMedia(), + user = user.toDbUser(), + reactions = listOf( + DbStatusReaction( + _id = UUID.randomUUID().toString(), + statusKey = statusKey, + accountKey = accountKey, + liked = liked, + retweeted = retweeted + ) + ), + url = url.toDbUrl(statusKey) +) +fun UiStatus.toDbStatusV2() = DbStatusV2( + _id = UUID.randomUUID().toString(), + statusId = statusId, + htmlText = htmlText, + timestamp = timestamp, + hasMedia = hasMedia, + statusKey = statusKey, + rawText = rawText, + retweetCount = metrics.retweet, + likeCount = metrics.like, + replyCount = metrics.reply, + placeString = geo.name, + source = source, + userKey = user.userKey, + lang = "", + is_possibly_sensitive = sensitive, + platformType = platformType, + previewCard = card?.toDbCard(), + poll = poll?.toDbPoll(), + spoilerText = spoilerText, + inReplyToUserId = inReplyToUserId, + inReplyToStatusId = inReplyToStatusId, + extra = when (extra) { + is TwitterStatusExtra -> DbTwitterStatusExtra( + reply_settings = extra.reply_settings, + quoteCount = extra.quoteCount + ).json() + is MastodonStatusExtra -> DbMastodonStatusExtra( + emoji = extra.emoji.map { it.emoji }.flatten().map { + Emoji( + shortcode = it.shortcode, + url = it.url, + staticURL = it.staticURL, + visibleInPicker = it.visibleInPicker, + category = it.category + ) + }, + type = extra.type, + visibility = extra.visibility, + mentions = extra.mentions?.map { + Mention( + id = it.id, + username = it.username, + url = it.url, + acct = it.acct + ) + } + ).json() + else -> extra.json() + } +) + +private fun UiCard.toDbCard() = DbPreviewCard( + link = link, + displayLink = displayLink, + title = title, + desc = description, + image = image +) + +private fun UiPoll.toDbPoll() = DbPoll( + id = id, + options = options.map { DbPollOption(text = it.text, count = it.count) }, + expiresAt = expiresAt, + expired = expired, + multiple = multiple, + voted = voted, + votesCount = votesCount, + votersCount = votersCount, + ownVotes = ownVotes +) + +private fun DbPoll.toUi() = UiPoll( + id = id, + options = options.map { Option(text = it.text, count = it.count) }, + expiresAt = expiresAt, + expired = expired, + multiple = multiple, + voted = voted, + votesCount = votesCount, + votersCount = votersCount, + ownVotes = ownVotes +) + +fun DbPagingTimelineWithStatus.toPagingTimeline(accountKey: MicroBlogKey) = with(status) { + PagingTimeLineWithStatus( + timeline = timeline.toUi(), + status = status.toUi(accountKey = accountKey) + ) +} + +fun DbPagingTimeline.toUi() = PagingTimeLine( + accountKey = accountKey, + pagingKey = pagingKey, + statusKey = statusKey, + timestamp = timestamp, + sortId = sortId, + isGap = isGap +) + +fun PagingTimeLine.toDbPagingTimeline() = DbPagingTimeline( + accountKey = accountKey, + pagingKey = pagingKey, + statusKey = statusKey, + timestamp = timestamp, + sortId = sortId, + isGap = isGap, + _id = UUID.randomUUID().toString() +) + +fun DbPagingTimelineWithStatus.toUi( + accountKey: MicroBlogKey, +) = with(status.status) { + val reaction = reactions.firstOrNull { it.accountKey == accountKey } + data.toUi( + user = user.toUi(), + media = media.toUi(), + isGap = timeline.isGap, + url = url.toUi(), + reaction = reaction, + referenceStatus = status.references.map { + it.reference.referenceType to it.status.toUi( + accountKey = accountKey + ) + }.toMap() + ) +} + +fun DbTwitterStatusExtra.toUi() = TwitterStatusExtra( + reply_settings = reply_settings, + quoteCount = quoteCount +) + +fun DbMastodonStatusExtra.toUi() = MastodonStatusExtra( + type = type, + emoji = emoji.toUi(), + visibility = visibility, + mentions = mentions?.toUi() +) + +fun List.toUi() = map { + MastodonMention( + id = it.id, + username = it.username, + url = it.url, + acct = it.acct + ) +} + +fun DbPreviewCard.toUi() = UiCard( + link = link, + displayLink = displayLink, + title = title, + description = desc, + image = image +) + +fun Poll.toUi() = id?.let { + UiPoll( + id = it, + options = options?.map { option -> + Option( + text = option.title ?: "", + count = option.votesCount ?: 0 + ) + } ?: emptyList(), + expired = expired ?: false, + expiresAt = expiresAt?.time, + multiple = multiple ?: false, + voted = voted ?: false, + votersCount = votersCount, + ownVotes = ownVotes, + votesCount = votesCount + ) +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt new file mode 100644 index 000000000..425ec10bb --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt @@ -0,0 +1,74 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.model.ui.UiTrendHistory +import com.twidere.twiderex.room.db.model.DbTrend +import com.twidere.twiderex.room.db.model.DbTrendHistory +import com.twidere.twiderex.room.db.model.DbTrendWithHistory +import java.util.UUID + +fun DbTrendWithHistory.toUi(accountKey: MicroBlogKey) = UiTrend( + trendKey = trend.trendKey, + displayName = trend.displayName, + url = trend.url, + query = trend.query, + volume = trend.volume, + history = history.map { + it.toUi() + }, + accountKey = accountKey +) + +fun DbTrendHistory.toUi() = UiTrendHistory( + trendKey = trendKey, + day = day, + uses = uses, + accounts = accounts +) + +fun List.toDbTrendWithHistory() = map { + DbTrendWithHistory( + trend = it.toDbTrend(), + history = it.history.map { history -> history.toDbTrendHistory(it.accountKey) } + ) +} + +fun UiTrend.toDbTrend() = DbTrend( + _id = UUID.randomUUID().toString(), + trendKey = trendKey, + displayName = displayName, + url = url, + query = query, + volume = volume, + accountKey = accountKey +) + +fun UiTrendHistory.toDbTrendHistory(accountKey: MicroBlogKey) = DbTrendHistory( + trendKey = trendKey, + day = day, + uses = uses, + accounts = accounts, + _id = UUID.randomUUID().toString(), + accountKey = accountKey +) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt new file mode 100644 index 000000000..1fe1c0b14 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUrlEntity +import com.twidere.twiderex.room.db.model.DbUrlEntity +import java.util.UUID + +fun DbUrlEntity.toUi() = UiUrlEntity( + url = url, + expandedUrl = expandedUrl, + displayUrl = displayUrl, + title = title, + description = description, + image = image +) +fun List.toUi() = map { it.toUi() } + +fun List.toDbUrl(belongToKey: MicroBlogKey) = map { + DbUrlEntity( + url = it.url, + _id = UUID.randomUUID().toString(), + statusKey = belongToKey, + expandedUrl = it.expandedUrl, + displayUrl = it.displayUrl, + title = it.title, + description = it.description, + image = it.image + ) +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt similarity index 55% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt index 6501b8597..c0ed0d7bc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/db/transform/UserTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt @@ -18,11 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.transform +package com.twidere.twiderex.room.db.transform -import com.twidere.twiderex.db.model.DbMastodonUserExtra -import com.twidere.twiderex.db.model.DbTwitterUserExtra -import com.twidere.twiderex.db.model.DbUser +import com.twidere.services.mastodon.model.Emoji import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUrlEntity @@ -31,8 +29,13 @@ import com.twidere.twiderex.model.ui.UserMetrics import com.twidere.twiderex.model.ui.mastodon.Field import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json +import com.twidere.twiderex.room.db.model.DbMastodonUserExtra +import com.twidere.twiderex.room.db.model.DbTwitterUserExtra +import com.twidere.twiderex.room.db.model.DbUser +import com.twidere.twiderex.room.db.model.TwitterUrlEntity +import com.twidere.twiderex.utils.fromJson +import com.twidere.twiderex.utils.json +import java.util.UUID fun DbUser.toAmUser() = AmUser( @@ -74,14 +77,70 @@ fun DbUser.toUi() = userKey = userKey, platformType = platformType, extra = when (platformType) { - PlatformType.Twitter -> Json.decodeFromString(extra).toUi() + PlatformType.Twitter -> extra.fromJson().toUi() PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> Json.decodeFromString(extra).toUi() + PlatformType.Mastodon -> extra.fromJson().toUi() }, acct = acct, ) +fun UiUser.toDbUser() = + DbUser( + _id = UUID.randomUUID().toString(), + name = name, + screenName = screenName, + profileImage = profileImage.toString(), + profileBackgroundImage = profileBackgroundImage, + + rawDesc = rawDesc, + htmlDesc = htmlDesc, + website = website, + location = location, + verified = verified, + userKey = userKey, + platformType = platformType, + extra = when (extra) { + is TwitterUserExtra -> DbTwitterUserExtra( + pinned_tweet_id = extra.pinned_tweet_id, + url = extra.url.map { + TwitterUrlEntity( + url = it.url, + expandedUrl = it.expandedUrl, + displayUrl = it.displayUrl + ) + } + ).json() + is MastodonUserExtra -> DbMastodonUserExtra( + fields = extra.fields.map { + com.twidere.services.mastodon.model.Field( + name = it.name, + value = it.value, + ) + }, + emoji = extra.emoji.map { it.emoji }.flatten().map { + Emoji( + shortcode = it.shortcode, + url = it.url, + staticURL = it.staticURL, + visibleInPicker = it.visibleInPicker, + category = it.category + ) + }, + bot = extra.bot, + locked = extra.locked + ).json() + else -> extra.json() + }, + acct = acct, + userId = id, + followersCount = metrics.fans, + friendsCount = metrics.follow, + listedCount = metrics.listed, + isProtected = protected, + statusesCount = metrics.status + ) + fun DbTwitterUserExtra.toUi() = TwitterUserExtra( pinned_tweet_id = pinned_tweet_id, url = url.map { url -> diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/WorkDataTransform.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/WorkDataTransform.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index acde55326..9212ea1a1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.Flow // TODO OPERATION interface StatusDao { - suspend fun insertAll(listOf: List) + suspend fun insertAll(listOf: List, accountKey: MicroBlogKey) suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): UiStatus? fun findWithStatusKeyWithFlow( statusKey: MicroBlogKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt index 7e056a586..d1f1b70e2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt @@ -26,7 +26,7 @@ import com.twidere.twiderex.model.ui.UiTrend // TODO OPERATION interface TrendDao { - fun insertAll(trends: List) + suspend fun insertAll(trends: List) fun getPagingSource(accountKey: MicroBlogKey): PagingSource - suspend fun clear() + suspend fun clear(accountKey: MicroBlogKey) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt index bb51465e3..d3d7a569a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt @@ -52,7 +52,7 @@ class TrendMediator( if (loadType == LoadType.REFRESH) { val lists = service.trends(locationId) database.withTransaction { - database.trendDao().clear() + database.trendDao().clear(accountKey) database.trendDao().insertAll(lists.map { it.toUi(accountKey) }) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 316bdd5c8..92bd101ec 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -63,8 +63,6 @@ class StatusRepository( database.withTransaction { database.statusDao().delete(statusKey) database.pagingTimelineDao().delete(statusKey) - // TODO hide by implementation of statusDao - // database.statusReferenceDao().delete(statusKey) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt index 1dfe5c7fd..ea3ad97e0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt @@ -33,7 +33,7 @@ internal class MockStatusDao @TestOnly constructor() : StatusDao { return fakeDb[statusKey] } - override suspend fun insertAll(listOf: List) { + override suspend fun insertAll(listOf: List, accountKey: MicroBlogKey) { listOf.forEach { status -> fakeDb[status.statusKey] = status } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt index 6c1b0a95b..9cb2e93f2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt @@ -30,7 +30,7 @@ import org.jetbrains.annotations.TestOnly class MockTrendDao @TestOnly constructor() : TrendDao { private val fakeDb = mutableMapOf>() - override fun insertAll(trends: List) { + override suspend fun insertAll(trends: List) { trends.forEach { uiTrend -> fakeDb[uiTrend.accountKey].let { if (it.isNullOrEmpty()) { @@ -48,7 +48,7 @@ class MockTrendDao @TestOnly constructor() : TrendDao { ) } - override suspend fun clear() { + override suspend fun clear(accountKey: MicroBlogKey) { fakeDb.clear() } } From 4401a52dd498426b372f362599314f2531bf91f3 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 20 Aug 2021 15:17:59 +0800 Subject: [PATCH 069/615] add DbPagingSource to convert db model to ui model --- .../db/RoomAppDatabaseMigrationTest.kt | 115 ------------- .../twiderex/mock/MockDirectMessageService.kt | 103 ------------ .../twiderex/mock/MockLookUpService.kt | 69 -------- .../db/transform/WorkDataTransform.kt | 0 .../twiderex/mock/db/MockCacheDatabse.kt | 155 ------------------ .../db/MockDirectMessageConversationDao.kt | 96 ----------- .../mock/db/MockDirectMessageEventDao.kt | 92 ----------- .../twidere/twiderex/mock/db/MockListsDao.kt | 112 ------------- .../twidere/twiderex/mock/db/MockTrendDao.kt | 96 ----------- .../twidere/twiderex/mock/db/MockUserDao.kt | 43 ----- common/build.gradle.kts | 13 +- .../dataprovider/db/CacheDatabaseImpl.kt | 14 +- .../dao/DirectMessageConversationDaoImpl.kt | 17 +- .../dataprovider/db/dao/ListsDaoImpl.kt | 19 ++- .../dataprovider/db/dao/MediaDaoImpl.kt | 6 +- .../db/dao/NotificationCursorDaoImpl.kt | 8 +- .../db/dao/PagingTimelineDaoImpl.kt | 21 ++- .../dataprovider/db/dao/TrendDaoImpl.kt | 29 ++-- .../dataprovider/db/dao/UserDaoImpl.kt | 10 +- .../twiderex/room/db/RoomCacheDatabase.kt | 6 +- .../dao/RoomDirectMessageConversationDao.kt | 11 +- .../room/db/dao/RoomDirectMessageEventDao.kt | 11 +- .../twiderex/room/db/dao/RoomListsDao.kt | 9 +- .../room/db/dao/RoomPagingTimelineDao.kt | 9 +- .../twiderex/room/db/dao/RoomTrendDao.kt | 9 +- .../twiderex/room/db/dao/RoomUrlEntityDao.kt | 2 +- .../twidere/twiderex/room/db/model/DbTrend.kt | 16 +- .../room/db/model/converter/ExtraConverter.kt | 11 ++ .../converter/MastodonVisibilityConverter.kt | 17 +- .../TwitterReplySettingsConverter.kt | 25 ++- .../twiderex/room/db/paging/DbPagingSource.kt | 58 +++++++ .../room/db/paging/TablePagingSource.kt | 112 +++++++++++++ .../twidere/twiderex/model/enums/Mastodon.kt | 4 - .../twidere/twiderex/model/enums/Twitter.kt | 3 - .../twiderex/model/paging/PagingTimeLine.kt | 4 +- .../twiderex/repository/StatusRepository.kt | 5 +- .../twidere/twiderex/mock/model/MockModels.kt | 18 +- .../repository/ReactionRepositoryTest.kt | 2 +- .../repository/StatusRepositoryTest.kt | 2 +- 39 files changed, 335 insertions(+), 1017 deletions(-) delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt rename {common/src/androidMain/kotlin/com/twidere/twiderex/room => android/src/main/kotlin/com/twidere/twiderex}/db/transform/WorkDataTransform.kt (100%) delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt rename android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt (63%) rename android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt => common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt (57%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt deleted file mode 100644 index 5a5c44688..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/db/RoomAppDatabaseMigrationTest.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.room.testing.MigrationTestHelper -import androidx.sqlite.db.SimpleSQLiteQuery -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.twidere.twiderex.model.enums.ComposeType -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.io.IOException -import java.util.UUID - -@RunWith(AndroidJUnit4::class) -class RoomAppDatabaseMigrationTest { - private val TEST_DB = "migration-test" - - @get:Rule - val helper: MigrationTestHelper = MigrationTestHelper( - InstrumentationRegistry.getInstrumentation(), - AppDatabase::class.java, - ) - - @Test - @Throws(IOException::class) - fun migrate1To2() { - val id = UUID.randomUUID().toString() - val content = "Content" - val createdAt = 1000L - val composeType = ComposeType.New - helper.createDatabase(TEST_DB, 1).apply { - execSQL( - "INSERT INTO draft (_id, content, createdAt, composeType, media) VALUES (?, ?, ?, ?, ?)", - arrayOf(id, content, createdAt, composeType, "") - ) - close() - } - - helper.runMigrationsAndValidate( - TEST_DB, 2, true, - com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 - ).apply { - query(SimpleSQLiteQuery("SELECT * FROM draft WHERE _id = ?", arrayOf(id))).apply { - moveToFirst() - getString(getColumnIndex("_id")).also { - assert(it == id) - } - getString(getColumnIndex("content")).also { - assert(it == content) - } - getLong(getColumnIndex("createdAt")).also { - assert(it == createdAt) - } - } - close() - } - } - - @Test - @Throws(IOException::class) - fun migrate2To3() { - val id = UUID.randomUUID().toString() - val content = "Content" - val lastActive = 1000L - helper.createDatabase(TEST_DB, 2).apply { - execSQL( - "INSERT INTO search (_id, content, lastActive) VALUES (?, ?, ?)", - arrayOf(id, content, lastActive) - ) - close() - } - - helper.runMigrationsAndValidate( - TEST_DB, 3, true, - com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 - ).apply { - query(SimpleSQLiteQuery("SELECT * FROM search WHERE _id = ?", arrayOf(id))).apply { - moveToFirst() - getString(getColumnIndex("_id")).also { - assert(it == id) - } - getString(getColumnIndex("content")).also { - assert(it == content) - } - getLong(getColumnIndex("lastActive")).also { - assert(it == lastActive) - } - getInt(getColumnIndex("saved")).also { - assert(it == 0) - } - } - close() - } - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt b/android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt deleted file mode 100644 index a6690bc46..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/mock/MockDirectMessageService.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock - -import com.twidere.services.microblog.DirectMessageService -import com.twidere.services.microblog.MicroBlogService -import com.twidere.services.microblog.model.IDirectMessage -import com.twidere.services.twitter.model.Attachment -import com.twidere.services.twitter.model.DirectMessageEvent -import com.twidere.services.twitter.model.Entities -import com.twidere.services.twitter.model.EntitiesURL -import com.twidere.services.twitter.model.MessageCreate -import com.twidere.services.twitter.model.MessageData -import com.twidere.services.twitter.model.MessageTarget -import com.twidere.services.twitter.model.PurpleMedia -import com.twidere.services.twitter.model.TwitterPaging -import com.twidere.services.twitter.model.exceptions.TwitterApiException -import kotlinx.coroutines.delay -import java.util.UUID - -class MockDirectMessageService : DirectMessageService, MicroBlogService { - private val dataList = mutableListOf() - var errorMsg: String? = null - suspend fun generateDirectMessage(count: Int, senderId: String, recipientId: String): List { - val messageList = mutableListOf() - for (i in 0 until count) { - messageList.add( - DirectMessageEvent( - createdTimestamp = System.currentTimeMillis().toString(), - id = UUID.randomUUID().toString(), - type = "message_create", - messageCreate = MessageCreate( - messageData = MessageData( - text = "message:$count", - entities = Entities( - urls = listOf( - EntitiesURL( - display_url = "url$count", - expanded_url = "expanded:$count", - url = "url:$count", - indices = listOf(0, 1) - ) - ) - ), - attachment = Attachment( - type = "media", - media = PurpleMedia( - id = count.toLong(), - idStr = count.toString(), - ) - ) - ), - senderId = senderId, - target = MessageTarget( - recipientId - ) - ) - ) - ) - delay(1) - } - return messageList - } - - fun add(data: List) { - this.dataList.addAll(data) - } - - override suspend fun getDirectMessages(cursor: String?, count: Int?): List { - return if (errorMsg == null) TwitterPaging( - data = dataList, - nextPage = if (cursor == null) UUID.randomUUID().toString() else null - ) else throw TwitterApiException(errorMsg) // generateDirectMessage(count ?: 50, senderId = System.currentTimeMillis().toString(), recipientId = UUID.randomUUID().toString()) - } - - override suspend fun showDirectMessage(id: String): IDirectMessage? { - TODO("Not yet implemented") - } - - override suspend fun destroyDirectMessage(id: String) { - dataList.removeAll { - (it as DirectMessageEvent).id == id - } - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt b/android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt deleted file mode 100644 index e31060be7..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/mock/MockLookUpService.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock - -import com.twidere.services.microblog.LookupService -import com.twidere.services.microblog.MicroBlogService -import com.twidere.services.microblog.model.IStatus -import com.twidere.services.microblog.model.IUser -import com.twidere.services.twitter.model.User -import kotlinx.coroutines.delay - -class MockLookUpService : LookupService, MicroBlogService { - var errorMsg: String? = null - - override suspend fun lookupUserByName(name: String): IUser { - val id = System.currentTimeMillis() - return if (!errorMsg.isNullOrEmpty()) throw Error("Test error") else User( - id = id, - idStr = id.toString(), - name = name - ) - } - - override suspend fun lookupUsersByName(name: List): List { - return if (!errorMsg.isNullOrEmpty()) throw Error("Test error") else name.map { - delay(1) - val id = System.currentTimeMillis() - User( - id = id, - idStr = id.toString(), - name = it - ) - } - } - - override suspend fun lookupUser(id: String): IUser { - return if (!errorMsg.isNullOrEmpty()) throw Error("Test error") else User( - id = id.toLong(), - idStr = id, - name = "name" - ) - } - - override suspend fun lookupStatus(id: String): IStatus { - TODO("Not yet implemented") - } - - override suspend fun userPinnedStatus(userId: String): List { - TODO("Not yet implemented") - } -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/WorkDataTransform.kt b/android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt similarity index 100% rename from common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/WorkDataTransform.kt rename to android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt deleted file mode 100644 index 79f3c702c..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockCacheDatabse.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import androidx.room.DatabaseConfiguration -import androidx.room.InvalidationTracker -import androidx.sqlite.db.SupportSQLiteOpenHelper -import com.twidere.twiderex.db.dao.ListsDao -import com.twidere.twiderex.db.dao.PagingTimelineDao -import com.twidere.twiderex.db.dao.ReactionDao -import com.twidere.twiderex.db.dao.StatusDao -import com.twidere.twiderex.db.dao.StatusReferenceDao -import com.twidere.twiderex.db.dao.TrendDao -import com.twidere.twiderex.db.dao.TrendHistoryDao -import com.twidere.twiderex.db.dao.UrlEntityDao -import com.twidere.twiderex.db.dao.UserDao -import com.twidere.twiderex.room.db.RoomCacheDatabase -import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao -import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao -import com.twidere.twiderex.room.db.dao.RoomMediaDao -import com.twidere.twiderex.room.db.dao.RoomNotificationCursorDao -import java.util.ArrayDeque -import java.util.concurrent.Executor -import java.util.concurrent.Executors - -class MockRoomCacheDatabase : RoomCacheDatabase() { - override fun getTransactionExecutor(): Executor { - return object : Executor { - private var mExecutor = Executors.newSingleThreadExecutor() - private val mTasks = ArrayDeque() - private var mActive: Runnable? = null - - @Synchronized - override fun execute(command: Runnable) { - mTasks.offer( - Runnable { - try { - command.run() - } finally { - scheduleNext() - } - } - ) - if (mActive == null) { - scheduleNext() - } - } - - @Synchronized - fun scheduleNext() { - if (mTasks.poll().also { mActive = it } != null) { - mExecutor.execute(mActive) - } - } - } - } - override fun beginTransaction() { - } - - override fun setTransactionSuccessful() { - } - - override fun endTransaction() { - } - - override fun statusDao(): StatusDao { - TODO("Not yet implemented") - } - - private val mediaDao = MockMediaDao() - override fun mediaDao(): RoomMediaDao { - return mediaDao - } - - private val userDao = MockUserDao() - override fun userDao(): UserDao { - return userDao - } - - override fun reactionDao(): ReactionDao { - TODO("Not yet implemented") - } - - override fun pagingTimelineDao(): PagingTimelineDao { - TODO("Not yet implemented") - } - - private val urlEntityDao = MockUrlEntityDao() - override fun urlEntityDao(): UrlEntityDao { - return urlEntityDao - } - - override fun statusReferenceDao(): StatusReferenceDao { - TODO("Not yet implemented") - } - - private val listsDao = MockListsDao() - override fun listsDao(): ListsDao { - return listsDao - } - - override fun notificationCursorDao(): RoomNotificationCursorDao { - TODO("Not yet implemented") - } - - private val trendDao = MockTrendDao() - override fun trendDao(): TrendDao { - return trendDao - } - - private val trendHistoryDao = MockTrendHistoryDao() - override fun trendHistoryDao(): TrendHistoryDao { - return trendHistoryDao - } - - private val conversationDao = MockDirectMessageConversationDao() - override fun directMessageConversationDao(): RoomDirectMessageConversationDao { - return conversationDao - } - - private val dmDao = MockDirectMessageEventDao() - override fun directMessageDao(): RoomDirectMessageEventDao { - return dmDao - } - - override fun createOpenHelper(config: DatabaseConfiguration?): SupportSQLiteOpenHelper { - TODO("Not yet implemented") - } - - override fun createInvalidationTracker(): InvalidationTracker { - return InvalidationTracker(this, "mock") - } - - override fun clearAllTables() { - TODO("Not yet implemented") - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt deleted file mode 100644 index 55922213b..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageConversationDao.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import androidx.paging.PagingSource -import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao -import com.twidere.twiderex.room.db.model.DbDMConversation -import com.twidere.twiderex.room.db.model.DbDMEvent -import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage -import kotlinx.coroutines.flow.Flow - -class MockDirectMessageConversationDao : RoomDirectMessageConversationDao { - val db = mutableListOf() - override suspend fun insertAll(conversations: List) { - db.addAll(conversations) - } - - override suspend fun find(accountKey: MicroBlogKey): List { - return db.map { - DbDirectMessageConversationWithMessage( - conversation = it, - latestMessage = DbDMEventWithAttachments( - message = DbDMEvent( - "", - accountKey, - 1, - it.conversationKey, - "", - MicroBlogKey.Empty, - "", - "", - System.currentTimeMillis(), - "", - accountKey, - accountKey, - sendStatus = DbDMEvent.SendStatus.SUCCESS - ), - emptyList(), - emptyList(), - User( - id = accountKey.id.toLong(), - idStr = accountKey.id - ).toDbUser() - ) - ) - } - } - - override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - TODO("Not yet implemented") - } - - override fun findWithConversationKeyFlow( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): Flow { - TODO("Not yet implemented") - } - - override fun findWithConversationKey( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): DbDMConversation? { - TODO("Not yet implemented") - } - - override suspend fun delete(data: DbDMConversation) { - TODO("Not yet implemented") - } - - override suspend fun clearAll(accountKey: MicroBlogKey) { - TODO("Not yet implemented") - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt deleted file mode 100644 index 7716433da..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockDirectMessageEventDao.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import androidx.paging.PagingSource -import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao -import com.twidere.twiderex.room.db.model.DbDMEvent - -class MockDirectMessageEventDao : RoomDirectMessageEventDao { - val db = mutableListOf() - - override suspend fun insertAll(messages: List) { - db.addAll(messages) - } - - override suspend fun getAll(accountKey: MicroBlogKey): List { - return db.map { - DbDMEventWithAttachments( - message = it, - emptyList(), - emptyList(), - User( - it.accountKey.id.toLong(), - it.accountKey.id - ).toDbUser() - ) - } - } - - override suspend fun find( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): List { - TODO("Not yet implemented") - } - - override suspend fun findWithMessageKey( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey, - messageKey: MicroBlogKey - ): DbDMEventWithAttachments? { - TODO("Not yet implemented") - } - - override fun getPagingSource( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): PagingSource { - TODO("Not yet implemented") - } - - override suspend fun delete(data: DbDMEvent) { - TODO("Not yet implemented") - } - - override fun getMessageCount(accountKey: MicroBlogKey, conversationKey: MicroBlogKey): Long { - TODO("Not yet implemented") - } - - override suspend fun clearConversation( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ) { - TODO("Not yet implemented") - } - - override suspend fun clearAll(accountKey: MicroBlogKey) { - TODO("Not yet implemented") - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt deleted file mode 100644 index b981d1ccd..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockListsDao.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.twidere.twiderex.db.dao.ListsDao -import com.twidere.twiderex.db.model.DbList -import com.twidere.twiderex.model.MicroBlogKey -import kotlinx.coroutines.flow.Flow - -class MockListsDao : ListsDao { - private val fakeDatabase = mutableListOf() - private var pagingSource: PagingSource? = null - override suspend fun insertAll(lists: List) { - fakeDatabase.addAll(0, lists) - pagingSource?.invalidate() - } - - override suspend fun findWithListKey(listKey: MicroBlogKey, accountKey: MicroBlogKey): DbList? { - return fakeDatabase.find { - it.listKey == listKey - } - } - - override fun findWithListKeyWithFlow( - listKey: MicroBlogKey, - accountKey: MicroBlogKey - ): Flow { - TODO("Not yet implemented") - } - - override suspend fun findAll(): List? { - return fakeDatabase.toList() - } - - override suspend fun findWithAccountKey(accountKey: MicroBlogKey): List? { - TODO("Not yet implemented") - } - - override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - pagingSource = object : PagingSource() { - override fun getRefreshKey(state: PagingState): Int? { - return null - } - - override suspend fun load(params: LoadParams): LoadResult { - return try { - val page = params.key ?: 0 - val count = params.loadSize - val startIndex = page * count - val endIndex = page * count + count - val result = when { - endIndex <= fakeDatabase.size -> { - fakeDatabase.subList(startIndex, endIndex) - } - fakeDatabase.size in (startIndex + 1) until endIndex -> { - fakeDatabase.subList(startIndex, fakeDatabase.size) - } - else -> { - emptyList() - } - } - LoadResult.Page(result, null, if (result.isEmpty()) null else page + 1) - } catch (e: Exception) { - LoadResult.Error(e) - } - } - } - return pagingSource!! - } - - override suspend fun update(lists: List) { - lists.forEach { updateList -> - fakeDatabase.replaceAll { - if (it.listKey == updateList.listKey) updateList else it - } - } - pagingSource?.invalidate() - } - - override suspend fun delete(lists: List) { - lists.forEach { deleteList -> - fakeDatabase.removeAll { - it.listKey == deleteList.listKey - } - } - pagingSource?.invalidate() - } - - override suspend fun clearAll(accountKey: MicroBlogKey) { - fakeDatabase.clear() - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt deleted file mode 100644 index 76e0a3019..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockTrendDao.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.twidere.twiderex.db.dao.TrendDao -import com.twidere.twiderex.db.dao.TrendHistoryDao -import com.twidere.twiderex.db.model.DbTrendHistory -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.model.DbTrend -import com.twidere.twiderex.room.db.model.DbTrendWithHistory - -private val historiesMap = mutableMapOf>() -class MockTrendDao : TrendDao { - private val data = mutableListOf() - override suspend fun insertAll(trends: List) { - data.addAll(trends) - } - - override suspend fun find(accountKey: MicroBlogKey, limit: Int): List { - return data.map { - DbTrendWithHistory(it, historiesMap[it.trendKey] ?: emptyList()) - }.subList(0, limit.coerceIn(0, data.size)) - } - - override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - val pagingSource = object : PagingSource() { - override fun getRefreshKey(state: PagingState): Int? { - return null - } - - override suspend fun load(params: LoadParams): LoadResult { - return try { - val page = params.key ?: 0 - val count = params.loadSize - val startIndex = page * count - val endIndex = page * count + count - val result = when { - endIndex <= data.size -> { - data.subList(startIndex, endIndex) - } - data.size in (startIndex + 1) until endIndex -> { - data.subList(startIndex, data.size) - } - else -> { - emptyList() - } - }.map { - DbTrendWithHistory(trend = it, history = emptyList()) - } - LoadResult.Page(result, null, if (result.isEmpty()) null else page + 1) - } catch (e: Exception) { - LoadResult.Error(e) - } - } - } - return pagingSource - } - - override suspend fun clearAll(accountKey: MicroBlogKey) { - data.clear() - } -} - -class MockTrendHistoryDao : TrendHistoryDao { - override suspend fun insertAll(histories: List) { - histories.map { - val list = historiesMap[it.trendKey] ?: mutableListOf() - list.add(it) - historiesMap[it.trendKey] = list - } - } - - override suspend fun clearAll(accountKey: MicroBlogKey) { - historiesMap.clear() - } -} diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt b/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt deleted file mode 100644 index 5867cdbb7..000000000 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockUserDao.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.mock.db - -import com.twidere.twiderex.db.dao.UserDao -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.model.DbUser -import kotlinx.coroutines.flow.Flow - -class MockUserDao : UserDao { - override suspend fun insertAll(user: List) { - } - - override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { - TODO("Not yet implemented") - } - - override suspend fun findWithUserKey(userKey: MicroBlogKey): DbUser? { - TODO("Not yet implemented") - } - - override suspend fun update(user: List) { - TODO("Not yet implemented") - } -} diff --git a/common/build.gradle.kts b/common/build.gradle.kts index ae692395e..056993249 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -61,12 +61,7 @@ kotlin { kapt("androidx.room:room-compiler:${Versions.room}") } } - val androidTest by getting { - dependencies { - implementation("androidx.room:room-testing:${Versions.room}") - } - } - + val androidTest by getting val desktopMain by getting val desktopTest by getting } @@ -82,5 +77,11 @@ android { defaultConfig { minSdk = AndroidSdk.min targetSdk = AndroidSdk.target + + javaCompileOptions { + annotationProcessorOptions { + argument("room.schemaLocation", "$projectDir/schemas") + } + } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt index 2ea9a8da8..7f5f9560f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt @@ -37,25 +37,25 @@ internal class CacheDatabaseImpl(private val roomCacheDatabase: RoomCacheDatabas private val statusDao = StatusDaoImpl(roomCacheDatabase) override fun statusDao() = statusDao - private val mediaDao = MediaDaoImpl(roomCacheDatabase.mediaDao()) + private val mediaDao = MediaDaoImpl(roomCacheDatabase) override fun mediaDao() = mediaDao - private val userDao = UserDaoImpl(roomCacheDatabase.userDao()) + private val userDao = UserDaoImpl(roomCacheDatabase) override fun userDao() = userDao - private val pagingTimelineDao = PagingTimelineDaoImpl(roomCacheDatabase.pagingTimelineDao()) + private val pagingTimelineDao = PagingTimelineDaoImpl(roomCacheDatabase) override fun pagingTimelineDao() = pagingTimelineDao - private val listsDao = ListsDaoImpl(roomCacheDatabase.listsDao()) + private val listsDao = ListsDaoImpl(roomCacheDatabase) override fun listsDao() = listsDao - private val notificationCursorDao = NotificationCursorDaoImpl(roomCacheDatabase.notificationCursorDao()) + private val notificationCursorDao = NotificationCursorDaoImpl(roomCacheDatabase) override fun notificationCursorDao() = notificationCursorDao - private val trendDao = TrendDaoImpl(roomCacheDatabase.trendDao(), roomCacheDatabase.trendHistoryDao()) + private val trendDao = TrendDaoImpl(roomCacheDatabase) override fun trendDao() = trendDao - private val dmConversationDao = DirectMessageConversationDaoImpl(roomCacheDatabase.directMessageConversationDao()) + private val dmConversationDao = DirectMessageConversationDaoImpl(roomCacheDatabase) override fun directMessageConversationDao() = dmConversationDao private val dmEventDao = DirectMessageEventDaoImpl(roomCacheDatabase) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt index 3c0a3d0e1..231a5656d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt @@ -25,35 +25,36 @@ import com.twidere.twiderex.db.dao.DirectMessageConversationDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMConversation import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage -import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.paging.getPagingSource import com.twidere.twiderex.room.db.transform.toDbDMConversation import com.twidere.twiderex.room.db.transform.toUi import kotlinx.coroutines.flow.map -internal class DirectMessageConversationDaoImpl(private val roomConversationDao: RoomDirectMessageConversationDao) : DirectMessageConversationDao { +internal class DirectMessageConversationDaoImpl(private val database: RoomCacheDatabase) : DirectMessageConversationDao { override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - TODO("Not yet implemented") + return database.directMessageConversationDao().getPagingSource(cacheDatabase = database, accountKey = accountKey) } override fun findWithConversationKeyFlow( accountKey: MicroBlogKey, conversationKey: MicroBlogKey - ) = roomConversationDao.findWithConversationKeyFlow(accountKey, conversationKey).map { it?.toUi() } + ) = database.directMessageConversationDao().findWithConversationKeyFlow(accountKey, conversationKey).map { it?.toUi() } override suspend fun findWithConversationKey( accountKey: MicroBlogKey, conversationKey: MicroBlogKey - ) = roomConversationDao.findWithConversationKey(accountKey, conversationKey)?.toUi() + ) = database.directMessageConversationDao().findWithConversationKey(accountKey, conversationKey)?.toUi() override suspend fun insertAll(listOf: List) { - roomConversationDao.insertAll(listOf.map { it.toDbDMConversation() }) + database.directMessageConversationDao().insertAll(listOf.map { it.toDbDMConversation() }) } override suspend fun find( accountKey: MicroBlogKey - ) = roomConversationDao.find(accountKey).map { it.toUi() } + ) = database.directMessageConversationDao().find(accountKey).map { it.toUi() } override suspend fun delete(conversation: UiDMConversation) { - roomConversationDao.delete(conversation.toDbDMConversation()) + database.directMessageConversationDao().delete(conversation.toDbDMConversation()) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt index 7ca1bb8a6..0f6b67e57 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt @@ -24,39 +24,40 @@ import androidx.paging.PagingSource import com.twidere.twiderex.db.dao.ListsDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList -import com.twidere.twiderex.room.db.dao.RoomListsDao +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.paging.getPagingSource import com.twidere.twiderex.room.db.transform.toDbList import com.twidere.twiderex.room.db.transform.toUi import kotlinx.coroutines.flow.map -internal class ListsDaoImpl(private val roomListsDao: RoomListsDao) : ListsDao { +internal class ListsDaoImpl(private val database: RoomCacheDatabase) : ListsDao { override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - TODO("Not yet implemented") + return database.listsDao().getPagingSource(cacheDatabase = database, accountKey = accountKey) } override fun findWithListKeyWithFlow( listKey: MicroBlogKey, accountKey: MicroBlogKey - ) = roomListsDao.findWithListKeyWithFlow(listKey, accountKey).map { it?.toUi() } + ) = database.listsDao().findWithListKeyWithFlow(listKey, accountKey).map { it?.toUi() } override suspend fun insertAll(listOf: List) { - roomListsDao.insertAll(listOf.map { it.toDbList() }) + database.listsDao().insertAll(listOf.map { it.toDbList() }) } override suspend fun findWithListKey( listKey: MicroBlogKey, accountKey: MicroBlogKey - ) = roomListsDao.findWithListKey(listKey, accountKey)?.toUi() + ) = database.listsDao().findWithListKey(listKey, accountKey)?.toUi() override suspend fun update(listOf: List) { - roomListsDao.update(listOf.map { it.toDbList() }) + database.listsDao().update(listOf.map { it.toDbList() }) } override suspend fun delete(listOf: List) { - roomListsDao.delete(listOf.map { it.toDbList() }) + database.listsDao().delete(listOf.map { it.toDbList() }) } override suspend fun clearAll(accountKey: MicroBlogKey) { - roomListsDao.clearAll(accountKey) + database.listsDao().clearAll(accountKey) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt index a5bac3a67..5a79199a8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt @@ -22,11 +22,11 @@ package com.twidere.twiderex.dataprovider.db.dao import com.twidere.twiderex.db.dao.MediaDao import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.dao.RoomMediaDao +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.room.db.transform.toUi -internal class MediaDaoImpl(private val roomMediaDao: RoomMediaDao) : MediaDao { - override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey) = roomMediaDao.findMediaByBelongToKey( +internal class MediaDaoImpl(private val database: RoomCacheDatabase) : MediaDao { + override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey) = database.mediaDao().findMediaByBelongToKey( belongToKey ).toUi() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt index 0fdfdbd10..be0a6342e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt @@ -24,18 +24,18 @@ import com.twidere.twiderex.db.dao.NotificationCursorDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.model.paging.NotificationCursor -import com.twidere.twiderex.room.db.dao.RoomNotificationCursorDao +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.room.db.transform.toDb import com.twidere.twiderex.room.db.transform.toDbCursor import com.twidere.twiderex.room.db.transform.toUi -internal class NotificationCursorDaoImpl(private val roomNotificationCursorDao: RoomNotificationCursorDao) : NotificationCursorDao { +internal class NotificationCursorDaoImpl(private val database: RoomCacheDatabase) : NotificationCursorDao { override suspend fun find( accountKey: MicroBlogKey, type: NotificationCursorType - ) = roomNotificationCursorDao.find(accountKey, type.toDb())?.toUi() + ) = database.notificationCursorDao().find(accountKey, type.toDb())?.toUi() override suspend fun add(notificationCursor: NotificationCursor) { - roomNotificationCursorDao.add(notificationCursor.toDbCursor()) + database.notificationCursorDao().add(notificationCursor.toDbCursor()) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt index 6edf396a5..e4c672960 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt @@ -25,38 +25,43 @@ import com.twidere.twiderex.db.dao.PagingTimelineDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLine import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import com.twidere.twiderex.room.db.dao.RoomPagingTimelineDao +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.paging.getPagingSource import com.twidere.twiderex.room.db.transform.toDbPagingTimeline import com.twidere.twiderex.room.db.transform.toPagingTimeline import com.twidere.twiderex.room.db.transform.toUi -internal class PagingTimelineDaoImpl(private val roomPagingTimelineDao: RoomPagingTimelineDao) : PagingTimelineDao { +internal class PagingTimelineDaoImpl(private val database: RoomCacheDatabase) : PagingTimelineDao { override fun getPagingSource( pagingKey: String, accountKey: MicroBlogKey ): PagingSource { - TODO("Not yet implemented") + return database.pagingTimelineDao().getPagingSource( + accountKey = accountKey, + cacheDatabase = database, + pagingKey = pagingKey + ) } override suspend fun clearAll(pagingKey: String, accountKey: MicroBlogKey) { - roomPagingTimelineDao.clearAll(pagingKey, accountKey) + database.pagingTimelineDao().clearAll(pagingKey, accountKey) } override suspend fun getLatest( pagingKey: String, accountKey: MicroBlogKey - ) = roomPagingTimelineDao.getLatest(pagingKey, accountKey)?.toPagingTimeline(accountKey) + ) = database.pagingTimelineDao().getLatest(pagingKey, accountKey)?.toPagingTimeline(accountKey) override suspend fun findWithStatusKey( maxStatusKey: MicroBlogKey, accountKey: MicroBlogKey - ) = roomPagingTimelineDao.findWithStatusKey(maxStatusKey, accountKey)?.toUi() + ) = database.pagingTimelineDao().findWithStatusKey(maxStatusKey, accountKey)?.toUi() override suspend fun insertAll(listOf: List) { - roomPagingTimelineDao.insertAll(listOf.map { it.toDbPagingTimeline() }) + database.pagingTimelineDao().insertAll(listOf.map { it.toDbPagingTimeline() }) } override suspend fun delete(statusKey: MicroBlogKey) { - roomPagingTimelineDao.delete(statusKey) + database.pagingTimelineDao().delete(statusKey) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt index 54e9119d9..6ceae8028 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt @@ -24,34 +24,27 @@ import androidx.paging.PagingSource import com.twidere.twiderex.db.dao.TrendDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiTrend -import com.twidere.twiderex.room.db.dao.RoomTrendDao -import com.twidere.twiderex.room.db.dao.RoomTrendHistoryDao +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.model.DbTrendWithHistory.Companion.saveToDb +import com.twidere.twiderex.room.db.paging.getPagingSource import com.twidere.twiderex.room.db.transform.toDbTrendWithHistory internal class TrendDaoImpl( - private val roomTrendDao: RoomTrendDao, - private val roomTrendHistoryDao: RoomTrendHistoryDao + val roomCacheDatabase: RoomCacheDatabase ) : TrendDao { override suspend fun insertAll(trends: List) { - trends.toDbTrendWithHistory().apply { - map { it.trend }.let { - roomTrendDao.insertAll(it) - } - - map { it.history } - .flatten() - .let { - roomTrendHistoryDao.insertAll(it) - } - } + trends.toDbTrendWithHistory().saveToDb(roomCacheDatabase) } override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { - TODO("Not yet implemented") + return roomCacheDatabase.trendDao().getPagingSource( + cacheDatabase = roomCacheDatabase, + accountKey = accountKey + ) } override suspend fun clear(accountKey: MicroBlogKey) { - roomTrendDao.clearAll(accountKey) - roomTrendHistoryDao.clearAll(accountKey) + roomCacheDatabase.trendDao().clearAll(accountKey) + roomCacheDatabase.trendHistoryDao().clearAll(accountKey) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt index b0154543f..fe064ef35 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt @@ -23,22 +23,22 @@ package com.twidere.twiderex.dataprovider.db.dao import com.twidere.twiderex.db.dao.UserDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.room.db.dao.RoomUserDao +import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.room.db.transform.toDbUser import com.twidere.twiderex.room.db.transform.toUi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -internal class UserDaoImpl(private val roomUserDao: RoomUserDao) : UserDao { +internal class UserDaoImpl(private val database: RoomCacheDatabase) : UserDao { override suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? { - return roomUserDao.findWithUserKey(userKey)?.toUi() + return database.userDao().findWithUserKey(userKey)?.toUi() } override suspend fun insertAll(listOf: List) { - roomUserDao.insertAll(listOf.map { it.toDbUser() }) + database.userDao().insertAll(listOf.map { it.toDbUser() }) } override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { - return roomUserDao.findWithUserKeyFlow(userKey).map { it?.toUi() } + return database.userDao().findWithUserKeyFlow(userKey).map { it?.toUi() } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt index 2f632c134..cba1009f2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt @@ -50,12 +50,14 @@ import com.twidere.twiderex.room.db.model.DbTrendHistory import com.twidere.twiderex.room.db.model.DbUrlEntity import com.twidere.twiderex.room.db.model.DbUser import com.twidere.twiderex.room.db.model.converter.ExtraConverter +import com.twidere.twiderex.room.db.model.converter.MastodonVisibilityConverter import com.twidere.twiderex.room.db.model.converter.MediaTypeConverter import com.twidere.twiderex.room.db.model.converter.MicroBlogKeyConverter import com.twidere.twiderex.room.db.model.converter.NotificationCursorTypeConverter import com.twidere.twiderex.room.db.model.converter.NotificationTypeConverter import com.twidere.twiderex.room.db.model.converter.PlatformTypeConverter import com.twidere.twiderex.room.db.model.converter.StringListConverter +import com.twidere.twiderex.room.db.model.converter.TwitterReplySettingsConverter import com.twidere.twiderex.room.db.model.converter.UserTimelineTypeConverter @Database( @@ -74,7 +76,7 @@ import com.twidere.twiderex.room.db.model.converter.UserTimelineTypeConverter DbDMConversation::class, DbDMEvent::class ], - version = 20, + version = 21, ) @TypeConverters( MicroBlogKeyConverter::class, @@ -85,6 +87,8 @@ import com.twidere.twiderex.room.db.model.converter.UserTimelineTypeConverter NotificationTypeConverter::class, ExtraConverter::class, NotificationCursorTypeConverter::class, + TwitterReplySettingsConverter::class, + MastodonVisibilityConverter::class ) abstract class RoomCacheDatabase : RoomDatabase() { abstract fun statusDao(): RoomStatusDao diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index 83f5d1b35..2be453eeb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.room.db.dao -import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -31,14 +30,12 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.room.db.model.DbDMConversation import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage import kotlinx.coroutines.flow.Flow -import org.jetbrains.annotations.TestOnly @Dao interface RoomDirectMessageConversationDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(conversations: List) - @TestOnly @Transaction @Query( """ @@ -50,7 +47,6 @@ interface RoomDirectMessageConversationDao { ) suspend fun find(accountKey: MicroBlogKey): List - // use join to found latest msg under each conversation, then use relation to found the conversation @Transaction @Query( """ @@ -58,11 +54,14 @@ interface RoomDirectMessageConversationDao { JOIN (SELECT conversationKey, max(sortId) as sortId FROM dm_event WHERE accountKey == :accountKey GROUP BY conversationKey) AS table2 ON table1.conversationKey = table2.conversationKey AND table1.sortId = table2.sortId WHERE table1.accountKey == :accountKey ORDER BY table1.sortId DESC + LIMIT :limit OFFSET :offset """ ) - fun getPagingSource( + fun getPagingList( accountKey: MicroBlogKey, - ): PagingSource + limit: Int, + offset: Int + ): List @Query("SELECT * FROM dm_conversation WHERE accountKey == :accountKey AND conversationKey == :conversationKey") fun findWithConversationKeyFlow(accountKey: MicroBlogKey, conversationKey: MicroBlogKey): Flow diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index 6c9b36eec..edcce33ab 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.room.db.dao -import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -49,11 +48,13 @@ interface RoomDirectMessageEventDao { suspend fun findWithMessageKey(accountKey: MicroBlogKey, conversationKey: MicroBlogKey, messageKey: MicroBlogKey): DbDMEventWithAttachments? @Transaction - @Query("SELECT * FROM dm_event WHERE accountKey == :accountKey AND conversationKey == :conversationKey ORDER BY sortId DESC") - fun getPagingSource( + @Query("SELECT * FROM dm_event WHERE accountKey == :accountKey AND conversationKey == :conversationKey ORDER BY sortId DESC LIMIT :limit OFFSET :offset") + fun getPagingList( accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): PagingSource + conversationKey: MicroBlogKey, + limit: Int, + offset: Int + ): List @Delete suspend fun delete(data: DbDMEvent) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index 0f3d43404..ff6525a27 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.room.db.dao -import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -50,10 +49,12 @@ interface RoomListsDao { suspend fun findWithAccountKey(accountKey: MicroBlogKey): List? @Transaction - @Query("SELECT * FROM lists WHERE accountKey == :accountKey") - fun getPagingSource( + @Query("SELECT * FROM lists WHERE accountKey == :accountKey LIMIT :limit OFFSET :offset") + fun getPagingList( accountKey: MicroBlogKey, - ): PagingSource + limit: Int, + offset: Int + ): List @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun update(lists: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index 78aad7fa2..bce0576e1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.room.db.dao -import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -44,11 +43,13 @@ interface RoomPagingTimelineDao { suspend fun findAllWithStatusKey(statusKey: List): List @Transaction - @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY sortId DESC") - fun getPagingSource( + @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY sortId DESC LIMIT :limit OFFSET :offset") + fun getPagingList( pagingKey: String, accountKey: MicroBlogKey, - ): PagingSource + limit: Int, + offset: Int + ): List @Transaction @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY timestamp DESC") diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index afb7eae99..84ddd6ebc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.room.db.dao -import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy @@ -40,10 +39,12 @@ interface RoomTrendDao { suspend fun find(accountKey: MicroBlogKey, limit: Int): List @Transaction - @Query("SELECT * FROM trends WHERE accountKey == :accountKey") - fun getPagingSource( + @Query("SELECT * FROM trends WHERE accountKey == :accountKey LIMIT :limit OFFSET :offset") + fun getPagingList( accountKey: MicroBlogKey, - ): PagingSource + limit: Int, + offset: Int + ): List @Query("DELETE FROM trends WHERE accountKey == :accountKey") suspend fun clearAll( diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt index ebcee5595..551da008e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy -import com.twidere.twiderex.db.model.DbUrlEntity +import com.twidere.twiderex.room.db.model.DbUrlEntity @Dao interface RoomUrlEntityDao { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt index 72bba3bd6..543087e6e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt @@ -26,6 +26,7 @@ import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase @Entity( tableName = "trends", @@ -52,4 +53,17 @@ data class DbTrendWithHistory( entity = DbTrendHistory::class ) val history: List, -) +) { + companion object { + suspend fun List.saveToDb(database: RoomCacheDatabase) { + map { it.trend }.let { + database.trendDao().insertAll(it) + } + map { it.history } + .flatten() + .let { + database.trendHistoryDao().insertAll(it) + } + } + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt index 89004bfab..e9e8f9f05 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.room.db.model.DbMastodonStatusExtra import com.twidere.twiderex.room.db.model.DbMastodonUserExtra +import com.twidere.twiderex.room.db.model.DbPoll import com.twidere.twiderex.room.db.model.DbPreviewCard import com.twidere.twiderex.room.db.model.DbTwitterStatusExtra import com.twidere.twiderex.room.db.model.DbTwitterUserExtra @@ -79,4 +80,14 @@ class ExtraConverter { fun fromTarget(target: DbPreviewCard?): String? { return target?.json() } + + @TypeConverter + fun fromDbPoll(value: String?): DbPoll? { + return value?.fromJson() + } + + @TypeConverter + fun fromTarget(target: DbPoll?): String? { + return target?.json() + } } diff --git a/android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt similarity index 63% rename from android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt index 1f7700bbd..ba7c7b3a3 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/db/MockUrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt @@ -18,12 +18,19 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.mock.db +package com.twidere.twiderex.room.db.model.converter -import com.twidere.twiderex.db.dao.UrlEntityDao -import com.twidere.twiderex.db.model.DbUrlEntity +import androidx.room.TypeConverter +import com.twidere.twiderex.model.enums.MastodonVisibility -class MockUrlEntityDao : UrlEntityDao { - override suspend fun insertAll(media: List) { +class MastodonVisibilityConverter { + @TypeConverter + fun fromString(target: MastodonVisibility?): String? { + return target?.name + } + + @TypeConverter + fun fromMastodonVisibility(string: String?): MastodonVisibility? { + return string?.let { MastodonVisibility.valueOf(it) } } } diff --git a/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt similarity index 57% rename from android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt index 427eb200b..932f22ea0 100644 --- a/android/src/test/java/com/twidere/twiderex/mock/MockCenter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt @@ -18,24 +18,19 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.mock +package com.twidere.twiderex.room.db.model.converter -import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.mock.db.MockRoomCacheDatabase -import com.twidere.twiderex.mock.service.MockListsService -import com.twidere.twiderex.mock.service.MockTrendService -import com.twidere.twiderex.room.db.RoomCacheDatabase +import androidx.room.TypeConverter +import com.twidere.twiderex.model.enums.TwitterReplySettings -object MockCenter { - fun mockCacheDatabase(): RoomCacheDatabase { - return MockRoomCacheDatabase() +class TwitterReplySettingsConverter { + @TypeConverter + fun fromString(target: TwitterReplySettings?): String? { + return target?.name } - fun mockListsService(): MicroBlogService { - return MockListsService() - } - - fun mockTrendService(): MicroBlogService { - return MockTrendService() + @TypeConverter + fun fromTwitterReplySettings(string: String?): TwitterReplySettings? { + return string?.let { TwitterReplySettings.valueOf(it) } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt new file mode 100644 index 000000000..b5d3f2648 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.paging + +import android.annotation.SuppressLint +import androidx.paging.PagingSource +import androidx.paging.PagingState +import androidx.room.InvalidationTracker +import androidx.room.RoomDatabase + +@SuppressLint("RestrictedApi") +internal class DbPagingSource( + roomDatabase: RoomDatabase, + private val loadFromDb: suspend (offset: Int, limit: Int) -> List, + vararg tables: String +) : PagingSource() { + private val databaseObserver = object : InvalidationTracker.Observer(tables) { + override fun onInvalidated(tables: MutableSet) { + invalidate() + } + } + + init { + roomDatabase.invalidationTracker.addWeakObserver(databaseObserver) + } + + override fun getRefreshKey(state: PagingState): Int? { + return 0 + } + + override suspend fun load(params: LoadParams): LoadResult { + val list = loadFromDb(params.key ?: 0, params.loadSize) + val nextKey = (params.key ?: 0) + list.size + return LoadResult.Page( + data = list, + prevKey = null, + nextKey = nextKey + ) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt new file mode 100644 index 000000000..18e296a8d --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt @@ -0,0 +1,112 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.paging + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.dao.RoomDirectMessageConversationDao +import com.twidere.twiderex.room.db.dao.RoomDirectMessageEventDao +import com.twidere.twiderex.room.db.dao.RoomListsDao +import com.twidere.twiderex.room.db.dao.RoomPagingTimelineDao +import com.twidere.twiderex.room.db.dao.RoomTrendDao +import com.twidere.twiderex.room.db.transform.toPagingTimeline +import com.twidere.twiderex.room.db.transform.toUi + +internal fun RoomDirectMessageConversationDao.getPagingSource( + cacheDatabase: RoomCacheDatabase, + accountKey: MicroBlogKey +) = DbPagingSource( + roomDatabase = cacheDatabase, + loadFromDb = { offset, limit -> + getPagingList( + accountKey = accountKey, + limit = limit, + offset = offset + ).map { it.toUi() } + }, + "dm_conversation", + "dm_event" +) + +internal fun RoomDirectMessageEventDao.getPagingSource( + cacheDatabase: RoomCacheDatabase, + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey +) = DbPagingSource( + roomDatabase = cacheDatabase, + loadFromDb = { offset, limit -> + getPagingList( + accountKey = accountKey, + conversationKey = conversationKey, + limit = limit, + offset = offset + ).map { it.toUi() } + }, + "dm_event" +) + +internal fun RoomListsDao.getPagingSource( + cacheDatabase: RoomCacheDatabase, + accountKey: MicroBlogKey, +) = DbPagingSource( + roomDatabase = cacheDatabase, + loadFromDb = { offset, limit -> + getPagingList( + accountKey = accountKey, + limit = limit, + offset = offset + ).map { it.toUi() } + }, + "lists" +) + +internal fun RoomPagingTimelineDao.getPagingSource( + cacheDatabase: RoomCacheDatabase, + pagingKey: String, + accountKey: MicroBlogKey, +) = DbPagingSource( + roomDatabase = cacheDatabase, + loadFromDb = { offset, limit -> + getPagingList( + pagingKey = pagingKey, + accountKey = accountKey, + offset = offset, + limit = limit + ).map { it.toPagingTimeline(accountKey = accountKey) } + }, + "paging_timeline" +) + +internal fun RoomTrendDao.getPagingSource( + cacheDatabase: RoomCacheDatabase, + accountKey: MicroBlogKey, +) = DbPagingSource( + roomDatabase = cacheDatabase, + loadFromDb = { offset, limit -> + getPagingList( + accountKey = accountKey, + limit = limit, + offset = offset + ).map { it.toUi(accountKey = accountKey) } + }, + "trends", + "trend_histories" +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt index 0741f8c5d..64f934d73 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt @@ -20,9 +20,6 @@ */ package com.twidere.twiderex.model.enums -import kotlinx.serialization.Serializable - -@Serializable enum class MastodonStatusType { Status, NotificationFollow, @@ -34,7 +31,6 @@ enum class MastodonStatusType { NotificationStatus, } -@Serializable enum class MastodonVisibility { Public, Unlisted, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt index dca216f91..08dc3bc03 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt @@ -20,9 +20,6 @@ */ package com.twidere.twiderex.model.enums -import kotlinx.serialization.Serializable - -@Serializable enum class TwitterReplySettings { Everyone, MentionedUsers, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt index e97696009..ecc8b92b6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt @@ -49,8 +49,8 @@ fun UserTimelineType.pagingKey(accountKey: MicroBlogKey) = "user:$accountKey:$th suspend fun List.saveToDb( database: CacheDatabase, ) { - this.map { it.status }.let { - database.statusDao().insertAll(it) + this.groupBy { it.timeline.accountKey }.forEach { + database.statusDao().insertAll(it.value.map { it.status }, it.key) } this.map { it.timeline }.let { database.pagingTimelineDao().insertAll(it) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 92bd101ec..2f5fc295a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -55,7 +55,7 @@ class StatusRepository( suspend fun updateStatus(statusKey: MicroBlogKey, accountKey: MicroBlogKey, action: (UiStatus) -> UiStatus) { database.statusDao().findWithStatusKey(statusKey, accountKey = accountKey)?.let { - database.statusDao().insertAll(listOf(action.invoke(it))) + database.statusDao().insertAll(listOf(action.invoke(it)), accountKey) } } @@ -74,7 +74,8 @@ class StatusRepository( database.statusDao().insertAll( listOf( lookupService.lookupStatus(id).toUi(accountKey = accountKey) - ) + ), + accountKey = accountKey ) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index ca50f0125..3264024a7 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -51,7 +51,7 @@ import java.util.Date import java.util.UUID @TestOnly -internal fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty) = UiMedia( +fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty) = UiMedia( url = url, belongToKey = belongToKey, mediaUrl = url, @@ -65,20 +65,20 @@ internal fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlog ) @TestOnly -internal fun mockUiSearch(content: String = "", accountKey: MicroBlogKey = MicroBlogKey.Empty, saved: Boolean = false) = UiSearch( +fun mockUiSearch(content: String = "", accountKey: MicroBlogKey = MicroBlogKey.Empty, saved: Boolean = false) = UiSearch( content = content, lastActive = System.currentTimeMillis(), saved = saved, accountKey = accountKey ) -internal fun List.toIPaging(nextPaging: String? = UUID.randomUUID().toString()) = TwitterPaging( +fun List.toIPaging(nextPaging: String? = UUID.randomUUID().toString()) = TwitterPaging( data = this, nextPage = nextPaging ) @TestOnly -internal fun mockIUser(id: String = UUID.randomUUID().toString(), name: String = ""): IUser { +fun mockIUser(id: String = UUID.randomUUID().toString(), name: String = ""): IUser { return UserV2( id = id, name = name @@ -86,7 +86,7 @@ internal fun mockIUser(id: String = UUID.randomUUID().toString(), name: String = } @TestOnly -internal fun mockITrend(): ITrend { +fun mockITrend(): ITrend { return Trend( name = "trend timestamp:${System.currentTimeMillis()}", url = "https://trend", @@ -102,7 +102,7 @@ internal fun mockITrend(): ITrend { } @TestOnly -internal fun mockIListModel( +fun mockIListModel( name: String = "", mode: String? = null, description: String? = "", @@ -119,7 +119,7 @@ internal fun mockIListModel( } @TestOnly -internal fun mockIStatus( +fun mockIStatus( id: String = UUID.randomUUID().toString(), hasMedia: Boolean = false, authorId: String = UUID.randomUUID().toString() @@ -139,7 +139,7 @@ internal fun mockIStatus( } @TestOnly -internal fun mockINotification(id: String = UUID.randomUUID().toString()): INotification { +fun mockINotification(id: String = UUID.randomUUID().toString()): INotification { val account = Account( id = UUID.randomUUID().toString(), username = "", @@ -159,7 +159,7 @@ internal fun mockINotification(id: String = UUID.randomUUID().toString()): INoti } @TestOnly -internal fun mockIDirectMessage(id: String = UUID.randomUUID().toString(), accountId: String, otherUserID: String = UUID.randomUUID().toString(), inCome: Boolean = true): IDirectMessage { +fun mockIDirectMessage(id: String = UUID.randomUUID().toString(), accountId: String, otherUserID: String = UUID.randomUUID().toString(), inCome: Boolean = true): IDirectMessage { return DirectMessageEvent( createdTimestamp = System.currentTimeMillis().toString(), id = id, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt index 9cfd02a4b..3568bec7e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt @@ -35,7 +35,7 @@ internal class ReactionRepositoryTest { val accountKey = MicroBlogKey.twitter("test") val repo = ReactionRepository(database) val status = mockIStatus().toUi(accountKey = accountKey) - database.statusDao().insertAll(listOf(status)) + database.statusDao().insertAll(listOf(status), accountKey) assert(!status.liked) assert(!status.retweeted) repo.updateReaction( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt index b604c18fa..af40eb21a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt @@ -45,7 +45,7 @@ class StatusRepositoryTest { database = database, nitterService = null ).let { - database.statusDao().insertAll(listOf(status)) + database.statusDao().insertAll(listOf(status), accountKey) it.updateStatus( statusKey = status.statusKey, accountKey = accountKey, From 6e75316b0248a9b13ab93894e16681a22db4f7bf Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 23 Aug 2021 17:05:18 +0800 Subject: [PATCH 070/615] migrate RoomAppDatabaseMigrationTest to common module --- common/build.gradle.kts | 29 +- .../1.json | 0 .../2.json | 0 .../3.json | 0 .../10.json | 0 .../11.json | 0 .../12.json | 0 .../13.json | 0 .../14.json | 0 .../15.json | 0 .../16.json | 0 .../17.json | 0 .../18.json | 0 .../19.json | 0 .../20.json | 0 .../21.json | 1095 +++++++++++++++++ .../room/RoomAppDatabaseMigrationTest.kt | 116 ++ 17 files changed, 1239 insertions(+), 1 deletion(-) rename {android/schemas/com.twidere.twiderex.db.AppDatabase => common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase}/1.json (100%) rename {android/schemas/com.twidere.twiderex.db.AppDatabase => common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase}/2.json (100%) rename {android/schemas/com.twidere.twiderex.db.AppDatabase => common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase}/3.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/10.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/11.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/12.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/13.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/14.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/15.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/16.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/17.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/18.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/19.json (100%) rename {android/schemas/com.twidere.twiderex.db.CacheDatabase => common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase}/20.json (100%) create mode 100644 common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/21.json create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 056993249..194c7b098 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -61,7 +61,17 @@ kotlin { kapt("androidx.room:room-compiler:${Versions.room}") } } - val androidTest by getting + val androidAndroidTest by getting { + dependencies { + implementation(kotlin("test-junit")) + implementation("androidx.arch.core:core-testing:2.1.0") + implementation("androidx.test:core:${Versions.androidx_test}") + implementation("androidx.test:runner:${Versions.androidx_test}") + implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") + implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") + implementation("androidx.room:room-testing:${Versions.room}") + } + } val desktopMain by getting val desktopTest by getting } @@ -74,9 +84,11 @@ fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kapt(dependencyNo android { compileSdk = AndroidSdk.compile sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["androidTest"].assets.srcDirs("$projectDir/schemas") defaultConfig { minSdk = AndroidSdk.min targetSdk = AndroidSdk.target + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { @@ -84,4 +96,19 @@ android { } } } + + packagingOptions { + resources { + excludes.addAll( + listOf( + "META-INF/AL2.0", + "META-INF/LGPL2.1", + "DebugProbesKt.bin", + "win32-x86-64/attach_hotspot_windows.dll", + "win32-x86/attach_hotspot_windows.dll", + "META-INF/licenses/ASM" + ) + ) + } + } } diff --git a/android/schemas/com.twidere.twiderex.db.AppDatabase/1.json b/common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/1.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.AppDatabase/1.json rename to common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/1.json diff --git a/android/schemas/com.twidere.twiderex.db.AppDatabase/2.json b/common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/2.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.AppDatabase/2.json rename to common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/2.json diff --git a/android/schemas/com.twidere.twiderex.db.AppDatabase/3.json b/common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/3.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.AppDatabase/3.json rename to common/schemas/com.twidere.twiderex.room.db.RoomAppDatabase/3.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/10.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/10.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/10.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/10.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/11.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/11.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/11.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/11.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/12.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/12.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/12.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/12.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/13.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/13.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/13.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/13.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/14.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/14.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/14.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/14.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/15.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/15.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/15.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/15.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/16.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/16.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/16.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/16.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/17.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/17.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/17.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/17.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/18.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/18.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/18.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/18.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/19.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/19.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/19.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/19.json diff --git a/android/schemas/com.twidere.twiderex.db.CacheDatabase/20.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/20.json similarity index 100% rename from android/schemas/com.twidere.twiderex.db.CacheDatabase/20.json rename to common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/20.json diff --git a/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/21.json b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/21.json new file mode 100644 index 000000000..a365a3ada --- /dev/null +++ b/common/schemas/com.twidere.twiderex.room.db.RoomCacheDatabase/21.json @@ -0,0 +1,1095 @@ +{ + "formatVersion": 1, + "database": { + "version": 21, + "identityHash": "15dfea2ff6653926f388915d097c8bc6", + "entities": [ + { + "tableName": "status", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `statusId` TEXT NOT NULL, `statusKey` TEXT NOT NULL, `htmlText` TEXT NOT NULL, `rawText` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `retweetCount` INTEGER NOT NULL, `likeCount` INTEGER NOT NULL, `replyCount` INTEGER NOT NULL, `placeString` TEXT, `source` TEXT NOT NULL, `hasMedia` INTEGER NOT NULL, `userKey` TEXT NOT NULL, `lang` TEXT, `is_possibly_sensitive` INTEGER NOT NULL, `platformType` TEXT NOT NULL, `previewCard` TEXT, `inReplyToUserId` TEXT, `inReplyToStatusId` TEXT, `poll` TEXT, `spoilerText` TEXT, `extra` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusId", + "columnName": "statusId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "htmlText", + "columnName": "htmlText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rawText", + "columnName": "rawText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "retweetCount", + "columnName": "retweetCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "likeCount", + "columnName": "likeCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "replyCount", + "columnName": "replyCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "placeString", + "columnName": "placeString", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hasMedia", + "columnName": "hasMedia", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userKey", + "columnName": "userKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lang", + "columnName": "lang", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "is_possibly_sensitive", + "columnName": "is_possibly_sensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "platformType", + "columnName": "platformType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "previewCard", + "columnName": "previewCard", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToUserId", + "columnName": "inReplyToUserId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "inReplyToStatusId", + "columnName": "inReplyToStatusId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "poll", + "columnName": "poll", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "spoilerText", + "columnName": "spoilerText", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_status_statusKey", + "unique": true, + "columnNames": [ + "statusKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_status_statusKey` ON `${TABLE_NAME}` (`statusKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "media", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `belongToKey` TEXT NOT NULL, `url` TEXT, `mediaUrl` TEXT, `previewUrl` TEXT, `type` TEXT NOT NULL, `width` INTEGER NOT NULL, `height` INTEGER NOT NULL, `pageUrl` TEXT, `altText` TEXT NOT NULL, `order` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "belongToKey", + "columnName": "belongToKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mediaUrl", + "columnName": "mediaUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "previewUrl", + "columnName": "previewUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "width", + "columnName": "width", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageUrl", + "columnName": "pageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "altText", + "columnName": "altText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_media_belongToKey_order", + "unique": true, + "columnNames": [ + "belongToKey", + "order" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_media_belongToKey_order` ON `${TABLE_NAME}` (`belongToKey`, `order`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `userId` TEXT NOT NULL, `name` TEXT NOT NULL, `userKey` TEXT NOT NULL, `acct` TEXT NOT NULL, `screenName` TEXT NOT NULL, `profileImage` TEXT NOT NULL, `profileBackgroundImage` TEXT, `followersCount` INTEGER NOT NULL, `friendsCount` INTEGER NOT NULL, `listedCount` INTEGER NOT NULL, `htmlDesc` TEXT NOT NULL, `rawDesc` TEXT NOT NULL, `website` TEXT, `location` TEXT, `verified` INTEGER NOT NULL, `isProtected` INTEGER NOT NULL, `platformType` TEXT NOT NULL, `statusesCount` INTEGER NOT NULL, `extra` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userKey", + "columnName": "userKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "acct", + "columnName": "acct", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "screenName", + "columnName": "screenName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileImage", + "columnName": "profileImage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileBackgroundImage", + "columnName": "profileBackgroundImage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "friendsCount", + "columnName": "friendsCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "listedCount", + "columnName": "listedCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "htmlDesc", + "columnName": "htmlDesc", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rawDesc", + "columnName": "rawDesc", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "website", + "columnName": "website", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "verified", + "columnName": "verified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isProtected", + "columnName": "isProtected", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "platformType", + "columnName": "platformType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusesCount", + "columnName": "statusesCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_user_userKey", + "unique": true, + "columnNames": [ + "userKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_userKey` ON `${TABLE_NAME}` (`userKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "status_reactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `statusKey` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `liked` INTEGER NOT NULL, `retweeted` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "liked", + "columnName": "liked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "retweeted", + "columnName": "retweeted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_status_reactions_statusKey_accountKey", + "unique": true, + "columnNames": [ + "statusKey", + "accountKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_status_reactions_statusKey_accountKey` ON `${TABLE_NAME}` (`statusKey`, `accountKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "paging_timeline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `pagingKey` TEXT NOT NULL, `statusKey` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `sortId` INTEGER NOT NULL, `isGap` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pagingKey", + "columnName": "pagingKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sortId", + "columnName": "sortId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isGap", + "columnName": "isGap", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_paging_timeline_accountKey_statusKey_pagingKey", + "unique": true, + "columnNames": [ + "accountKey", + "statusKey", + "pagingKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_paging_timeline_accountKey_statusKey_pagingKey` ON `${TABLE_NAME}` (`accountKey`, `statusKey`, `pagingKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "url_entity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `statusKey` TEXT NOT NULL, `url` TEXT NOT NULL, `expandedUrl` TEXT NOT NULL, `displayUrl` TEXT NOT NULL, `title` TEXT, `description` TEXT, `image` TEXT, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expandedUrl", + "columnName": "expandedUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayUrl", + "columnName": "displayUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_url_entity_statusKey_url", + "unique": true, + "columnNames": [ + "statusKey", + "url" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_url_entity_statusKey_url` ON `${TABLE_NAME}` (`statusKey`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "status_reference", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `referenceType` TEXT NOT NULL, `statusKey` TEXT NOT NULL, `referenceStatusKey` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "referenceType", + "columnName": "referenceType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "statusKey", + "columnName": "statusKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "referenceStatusKey", + "columnName": "referenceStatusKey", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_status_reference_referenceType_statusKey_referenceStatusKey", + "unique": true, + "columnNames": [ + "referenceType", + "statusKey", + "referenceStatusKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_status_reference_referenceType_statusKey_referenceStatusKey` ON `${TABLE_NAME}` (`referenceType`, `statusKey`, `referenceStatusKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "lists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `listId` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `listKey` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `mode` TEXT NOT NULL, `replyPolicy` TEXT NOT NULL, `isFollowed` INTEGER NOT NULL, `allowToSubscribe` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "listKey", + "columnName": "listKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mode", + "columnName": "mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "replyPolicy", + "columnName": "replyPolicy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isFollowed", + "columnName": "isFollowed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "allowToSubscribe", + "columnName": "allowToSubscribe", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_lists_accountKey_listKey", + "unique": true, + "columnNames": [ + "accountKey", + "listKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_lists_accountKey_listKey` ON `${TABLE_NAME}` (`accountKey`, `listKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "notification_cursor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `type` TEXT NOT NULL, `value` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_notification_cursor_accountKey_type", + "unique": true, + "columnNames": [ + "accountKey", + "type" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_notification_cursor_accountKey_type` ON `${TABLE_NAME}` (`accountKey`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "trends", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `trendKey` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `displayName` TEXT NOT NULL, `url` TEXT NOT NULL, `query` TEXT NOT NULL, `volume` INTEGER NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "trendKey", + "columnName": "trendKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "query", + "columnName": "query", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "volume", + "columnName": "volume", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_trends_trendKey_url", + "unique": true, + "columnNames": [ + "trendKey", + "url" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_trends_trendKey_url` ON `${TABLE_NAME}` (`trendKey`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "trend_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `trendKey` TEXT NOT NULL, `day` INTEGER NOT NULL, `uses` INTEGER NOT NULL, `accounts` INTEGER NOT NULL, `accountKey` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "trendKey", + "columnName": "trendKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uses", + "columnName": "uses", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accounts", + "columnName": "accounts", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_trend_histories_trendKey_day", + "unique": true, + "columnNames": [ + "trendKey", + "day" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_trend_histories_trendKey_day` ON `${TABLE_NAME}` (`trendKey`, `day`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "dm_conversation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `conversationId` TEXT NOT NULL, `conversationKey` TEXT NOT NULL, `conversationAvatar` TEXT NOT NULL, `conversationName` TEXT NOT NULL, `conversationSubName` TEXT NOT NULL, `conversationType` TEXT NOT NULL, `recipientKey` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationId", + "columnName": "conversationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationKey", + "columnName": "conversationKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationAvatar", + "columnName": "conversationAvatar", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationName", + "columnName": "conversationName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationSubName", + "columnName": "conversationSubName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conversationType", + "columnName": "conversationType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "recipientKey", + "columnName": "recipientKey", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_dm_conversation_accountKey_conversationKey", + "unique": true, + "columnNames": [ + "accountKey", + "conversationKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_conversation_accountKey_conversationKey` ON `${TABLE_NAME}` (`accountKey`, `conversationKey`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "dm_event", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` TEXT NOT NULL, `accountKey` TEXT NOT NULL, `sortId` INTEGER NOT NULL, `conversationKey` TEXT NOT NULL, `messageId` TEXT NOT NULL, `messageKey` TEXT NOT NULL, `htmlText` TEXT NOT NULL, `originText` TEXT NOT NULL, `createdTimestamp` INTEGER NOT NULL, `messageType` TEXT NOT NULL, `senderAccountKey` TEXT NOT NULL, `recipientAccountKey` TEXT NOT NULL, `sendStatus` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountKey", + "columnName": "accountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sortId", + "columnName": "sortId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "conversationKey", + "columnName": "conversationKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "messageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "messageKey", + "columnName": "messageKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "htmlText", + "columnName": "htmlText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originText", + "columnName": "originText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdTimestamp", + "columnName": "createdTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageType", + "columnName": "messageType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderAccountKey", + "columnName": "senderAccountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "recipientAccountKey", + "columnName": "recipientAccountKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sendStatus", + "columnName": "sendStatus", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_dm_event_accountKey_conversationKey_messageKey", + "unique": true, + "columnNames": [ + "accountKey", + "conversationKey", + "messageKey" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_event_accountKey_conversationKey_messageKey` ON `${TABLE_NAME}` (`accountKey`, `conversationKey`, `messageKey`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '15dfea2ff6653926f388915d097c8bc6')" + ] + } +} \ No newline at end of file diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt new file mode 100644 index 000000000..66e3f317b --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt @@ -0,0 +1,116 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room + +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.room.db.RoomAppDatabase +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException +import java.util.UUID + +@RunWith(AndroidJUnit4::class) +class RoomAppDatabaseMigrationTest { + private val TEST_DB = "migration-test" + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + RoomAppDatabase::class.java, + ) + + @Test + @Throws(IOException::class) + fun migrate1To2() { + val id = UUID.randomUUID().toString() + val content = "Content" + val createdAt = 1000L + val composeType = ComposeType.New + helper.createDatabase(TEST_DB, 1).apply { + execSQL( + "INSERT INTO draft (_id, content, createdAt, composeType, media) VALUES (?, ?, ?, ?, ?)", + arrayOf(id, content, createdAt, composeType, "") + ) + close() + } + + helper.runMigrationsAndValidate( + TEST_DB, 2, true, + com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 + ).apply { + query(SimpleSQLiteQuery("SELECT * FROM draft WHERE _id = ?", arrayOf(id))).apply { + moveToFirst() + getString(getColumnIndex("_id")).also { + assert(it == id) + } + getString(getColumnIndex("content")).also { + assert(it == content) + } + getLong(getColumnIndex("createdAt")).also { + assert(it == createdAt) + } + } + close() + } + } + + @Test + @Throws(IOException::class) + fun migrate2To3() { + val id = UUID.randomUUID().toString() + val content = "Content" + val lastActive = 1000L + helper.createDatabase(TEST_DB, 2).apply { + execSQL( + "INSERT INTO search (_id, content, lastActive) VALUES (?, ?, ?)", + arrayOf(id, content, lastActive) + ) + close() + } + + helper.runMigrationsAndValidate( + TEST_DB, 3, true, + com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 + ).apply { + query(SimpleSQLiteQuery("SELECT * FROM search WHERE _id = ?", arrayOf(id))).apply { + moveToFirst() + getString(getColumnIndex("_id")).also { + assert(it == id) + } + getString(getColumnIndex("content")).also { + assert(it == content) + } + getLong(getColumnIndex("lastActive")).also { + assert(it == lastActive) + } + getInt(getColumnIndex("saved")).also { + assert(it == 0) + } + } + close() + } + } +} From 96025d33a750672d3056d4753ce6028ec3e3af7e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 12:14:36 +0800 Subject: [PATCH 071/615] add test for android TrenDaoImpl and make all classess/functoions access to internal in androidMain --- .../twidere/twiderex/db/TrendDaoImplTest.kt | 159 ++++++++++++++++++ .../room/RoomAppDatabaseMigrationTest.kt | 2 +- ...{AppDataBaseImpl.kt => AppDatabaseImpl.kt} | 2 +- .../twiderex/room/db/RoomAppDatabase.kt | 2 +- .../twiderex/room/db/RoomCacheDatabase.kt | 2 +- .../dao/RoomDirectMessageConversationDao.kt | 2 +- .../room/db/dao/RoomDirectMessageEventDao.kt | 2 +- .../twiderex/room/db/dao/RoomDraftDao.kt | 2 +- .../twiderex/room/db/dao/RoomListsDao.kt | 2 +- .../twiderex/room/db/dao/RoomMediaDao.kt | 2 +- .../room/db/dao/RoomNotificationCursorDao.kt | 2 +- .../room/db/dao/RoomPagingTimelineDao.kt | 2 +- .../twiderex/room/db/dao/RoomReactionDao.kt | 2 +- .../twiderex/room/db/dao/RoomSearchDao.kt | 2 +- .../twiderex/room/db/dao/RoomStatusDao.kt | 2 +- .../room/db/dao/RoomStatusReferenceDao.kt | 2 +- .../twiderex/room/db/dao/RoomTrendDao.kt | 8 +- .../room/db/dao/RoomTrendHistoryDao.kt | 7 +- .../twiderex/room/db/dao/RoomUrlEntityDao.kt | 2 +- .../twiderex/room/db/dao/RoomUserDao.kt | 2 +- .../twidere/twiderex/room/db/model/Alias.kt | 6 +- .../room/db/model/DbDMConversation.kt | 4 +- .../twiderex/room/db/model/DbDMEvent.kt | 4 +- .../twidere/twiderex/room/db/model/DbDraft.kt | 2 +- .../twidere/twiderex/room/db/model/DbList.kt | 2 +- .../twidere/twiderex/room/db/model/DbMedia.kt | 2 +- .../room/db/model/DbNotificationCursor.kt | 2 +- .../room/db/model/DbPagingTimeline.kt | 8 +- .../twiderex/room/db/model/DbSearch.kt | 2 +- .../twiderex/room/db/model/DbStatus.kt | 18 +- .../room/db/model/DbStatusReaction.kt | 2 +- .../room/db/model/DbStatusReference.kt | 10 +- .../twidere/twiderex/room/db/model/DbTrend.kt | 4 +- .../twiderex/room/db/model/DbTrendHistory.kt | 2 +- .../twiderex/room/db/model/DbUrlEntity.kt | 2 +- .../twidere/twiderex/room/db/model/DbUser.kt | 8 +- .../model/converter/ComposeTypeConverter.kt | 2 +- .../room/db/model/converter/ExtraConverter.kt | 2 +- .../converter/MastodonVisibilityConverter.kt | 2 +- .../db/model/converter/MediaTypeConverter.kt | 2 +- .../model/converter/MicroBlogKeyConverter.kt | 2 +- .../NotificationCursorTypeConverter.kt | 2 +- .../converter/NotificationTypeConverter.kt | 2 +- .../model/converter/PlatformTypeConverter.kt | 2 +- .../db/model/converter/StringListConverter.kt | 2 +- .../TwitterReplySettingsConverter.kt | 2 +- .../converter/UserTimelineTypeConverter.kt | 2 +- .../twiderex/room/db/paging/DbPagingSource.kt | 18 +- .../room/db/transform/AccountTransform.kt | 4 +- .../db/transform/DmConversationTransform.kt | 10 +- .../room/db/transform/EmojiTransform.kt | 2 +- .../room/db/transform/ListTransform.kt | 4 +- .../room/db/transform/MediaTransform.kt | 4 +- .../transform/NotificationCursorTransform.kt | 8 +- .../room/db/transform/StatusTransform.kt | 30 ++-- .../room/db/transform/TrendTransform.kt | 10 +- .../room/db/transform/UrlEntityTransform.kt | 6 +- .../room/db/transform/UserTransform.kt | 10 +- .../twidere/twiderex/mock/model/MockModels.kt | 18 ++ 59 files changed, 312 insertions(+), 120 deletions(-) create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt rename common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/{AppDataBaseImpl.kt => AppDatabaseImpl.kt} (96%) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt new file mode 100644 index 000000000..dd1513519 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt @@ -0,0 +1,159 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.paging.PagingSource +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.twidere.services.mastodon.model.TrendHistory +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.dataprovider.mapper.toUiTrend +import com.twidere.twiderex.mock.model.mockITrend +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.room.db.RoomCacheDatabase +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.util.concurrent.Executors +import kotlin.test.assertEquals +import kotlin.test.assertNull + +typealias TwitterTrend = com.twidere.services.twitter.model.Trend +typealias MastodonTrend = com.twidere.services.mastodon.model.Trend + +@RunWith(AndroidJUnit4::class) +internal class TrendDaoImplTest { + private lateinit var roomDatabase: RoomCacheDatabase + private val twitterAccountKey = MicroBlogKey.twitter("123") + private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") + private val trends = mutableListOf() + + private val twitterTrendCount = 10 + private val mastodonTrendCount = 10 + + @get:Rule + val rule = InstantTaskExecutorRule() + + @Before + fun setUp() { + roomDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) + .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() + for (i in 0 until twitterTrendCount) { + trends.add( + TwitterTrend( + name = "tweet $i", + url = "https://tweet $i" + ).toUiTrend(twitterAccountKey) + ) + } + for (i in 0 until mastodonTrendCount) { + trends.add( + MastodonTrend( + name = "mastodon $i", + url = "https://mastodon $i", + history = mutableListOf( + TrendHistory( + accounts = "10", + uses = "20", + day = "${System.currentTimeMillis()}" + ) + ) + ).toUiTrend(mastodonAccountKey) + ) + } + } + + @After + fun tearDown() { + roomDatabase.close() + } + + @Test + fun insert_SaveBothTrendAndHistory() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.trendDao().insertAll(trends) + val twitterTrend = roomDatabase.trendDao().getPagingList(accountKey = twitterAccountKey, limit = twitterTrendCount, offset = 0) + val mastodonTrend = roomDatabase.trendDao().getPagingList(accountKey = mastodonAccountKey, limit = mastodonTrendCount, offset = 0) + assertEquals(twitterTrendCount, twitterTrend.size) + assertEquals(mastodonTrendCount, mastodonTrend.size) + mastodonTrend.forEach { + assert(it.history.isNotEmpty()) + } + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.trendDao().insertAll(trends) + val pagingSource = cacheDatabase.trendDao().getPagingSource( + accountKey = twitterAccountKey + ) + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, twitterTrendCount / 2, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(twitterTrendCount / 2, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(twitterTrendCount / 2, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, twitterTrendCount / 2, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(twitterTrendCount, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + assertEquals(twitterTrendCount / 2, loadMoreResult.data.size) + + val noMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(loadMoreResult.nextKey ?: 0, twitterTrendCount / 2, false)) + assert(noMoreResult is PagingSource.LoadResult.Page) + assertNull((noMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + var invalidate = false + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.trendDao().getPagingSource( + accountKey = twitterAccountKey + ).registerInvalidatedCallback { + invalidate = true + } + cacheDatabase.trendDao().insertAll(listOf(mockITrend().toUi(twitterAccountKey))) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun clearAll_ClearBothTrendAndTrendHistory() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.trendDao().insertAll(trends) + cacheDatabase.trendDao().clear(twitterAccountKey) + assert(roomDatabase.trendDao().getAll(twitterAccountKey).isEmpty()) + + cacheDatabase.trendDao().clear(mastodonAccountKey) + assert(roomDatabase.trendDao().getAll(mastodonAccountKey).isEmpty()) + assert(roomDatabase.trendHistoryDao().getAll(mastodonAccountKey).isEmpty()) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt index 66e3f317b..e181e6f65 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt @@ -33,7 +33,7 @@ import java.io.IOException import java.util.UUID @RunWith(AndroidJUnit4::class) -class RoomAppDatabaseMigrationTest { +internal class RoomAppDatabaseMigrationTest { private val TEST_DB = "migration-test" @get:Rule diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt similarity index 96% rename from common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt index 964ef267a..0e5f6af8e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDataBaseImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.db.dao.DraftDao import com.twidere.twiderex.db.dao.SearchDao import com.twidere.twiderex.room.db.RoomAppDatabase -internal class AppDataBaseImpl(private val roomDatabase: RoomAppDatabase) : AppDatabase { +internal class AppDatabaseImpl(private val roomDatabase: RoomAppDatabase) : AppDatabase { private val draftDao = DraftDaoImpl(roomDatabase.draftDao()) private val searchDao = SearchDaoImpl(roomDatabase.searchDao()) override fun draftDao(): DraftDao { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt index 46ed958a7..a1ed467b1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt @@ -45,7 +45,7 @@ import com.twidere.twiderex.room.db.model.converter.StringListConverter ComposeTypeConverter::class, StringListConverter::class, ) -abstract class RoomAppDatabase : RoomDatabase() { +internal abstract class RoomAppDatabase : RoomDatabase() { abstract fun draftDao(): RoomDraftDao abstract fun searchDao(): RoomSearchDao } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt index cba1009f2..aa54595d5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt @@ -90,7 +90,7 @@ import com.twidere.twiderex.room.db.model.converter.UserTimelineTypeConverter TwitterReplySettingsConverter::class, MastodonVisibilityConverter::class ) -abstract class RoomCacheDatabase : RoomDatabase() { +internal abstract class RoomCacheDatabase : RoomDatabase() { abstract fun statusDao(): RoomStatusDao abstract fun mediaDao(): RoomMediaDao abstract fun userDao(): RoomUserDao diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index 2be453eeb..7656e6fdb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage import kotlinx.coroutines.flow.Flow @Dao -interface RoomDirectMessageConversationDao { +internal interface RoomDirectMessageConversationDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(conversations: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index edcce33ab..30a707714 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.room.db.model.DbDMEvent import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments @Dao -interface RoomDirectMessageEventDao { +internal interface RoomDirectMessageEventDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(messages: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt index 41137903e..b7f1d0b3c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.room.db.model.DbDraft import kotlinx.coroutines.flow.Flow @Dao -interface RoomDraftDao { +internal interface RoomDraftDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(vararg draft: DbDraft) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index ff6525a27..f3cbb3647 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.room.db.model.DbList import kotlinx.coroutines.flow.Flow @Dao -interface RoomListsDao { +internal interface RoomListsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(lists: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt index 9c6261f83..19158c6fc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.room.db.model.DbMedia @Dao -interface RoomMediaDao { +internal interface RoomMediaDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt index e57248124..7228df37c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.room.db.model.DbNotificationCursor import com.twidere.twiderex.room.db.model.DbNotificationCursorType @Dao -interface RoomNotificationCursorDao { +internal interface RoomNotificationCursorDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(lists: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index bce0576e1..32fc4be66 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.room.db.model.DbPagingTimeline import com.twidere.twiderex.room.db.model.DbPagingTimelineWithStatus @Dao -interface RoomPagingTimelineDao { +internal interface RoomPagingTimelineDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(timeline: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt index d0360f0d3..31a517bf3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.room.db.model.DbStatusReaction @Dao -interface RoomReactionDao { +internal interface RoomReactionDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt index a03243fc9..4e0f1d3d2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.room.db.model.DbSearch import kotlinx.coroutines.flow.Flow @Dao -interface RoomSearchDao { +internal interface RoomSearchDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(search: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt index a109c9ecc..97d057c9c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.room.db.model.DbStatusWithReference import kotlinx.coroutines.flow.Flow @Dao -interface RoomStatusDao { +internal interface RoomStatusDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(status: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt index fe2e5fd9a..53925d686 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.room.db.model.DbStatusReference import com.twidere.twiderex.room.db.model.DbStatusReferenceWithStatus @Dao -interface RoomStatusReferenceDao { +internal interface RoomStatusReferenceDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(items: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index 84ddd6ebc..2eb29f303 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.room.db.model.DbTrend import com.twidere.twiderex.room.db.model.DbTrendWithHistory @Dao -interface RoomTrendDao { +internal interface RoomTrendDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(trends: List) @@ -50,4 +50,10 @@ interface RoomTrendDao { suspend fun clearAll( accountKey: MicroBlogKey, ) + + @Transaction + @Query("SELECT * FROM trends WHERE accountKey == :accountKey") + fun getAll( + accountKey: MicroBlogKey, + ): List } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt index cde691c28..194aead83 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.room.db.model.DbTrendHistory @Dao -interface RoomTrendHistoryDao { +internal interface RoomTrendHistoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(histories: List) @@ -36,4 +36,9 @@ interface RoomTrendHistoryDao { suspend fun clearAll( accountKey: MicroBlogKey, ) + + @Query("SELECT * FROM trend_histories WHERE accountKey == :accountKey") + suspend fun getAll( + accountKey: MicroBlogKey + ): List } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt index 551da008e..fd816803f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt @@ -26,7 +26,7 @@ import androidx.room.OnConflictStrategy import com.twidere.twiderex.room.db.model.DbUrlEntity @Dao -interface RoomUrlEntityDao { +internal interface RoomUrlEntityDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt index 9c5ea1484..aae3ee94d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.room.db.model.DbUser import kotlinx.coroutines.flow.Flow @Dao -interface RoomUserDao { +internal interface RoomUserDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(user: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt index 12ad8e88f..f2a0a4dc7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt @@ -20,6 +20,6 @@ */ package com.twidere.twiderex.room.db.model -typealias Html = String -typealias Json = String -typealias Timestamp = Long +internal typealias Html = String +internal typealias Json = String +internal typealias Timestamp = Long diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt index b1bed3b25..596fe99b8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.room.db.RoomCacheDatabase tableName = "dm_conversation", indices = [Index(value = ["accountKey", "conversationKey"], unique = true)], ) -data class DbDMConversation( +internal data class DbDMConversation( @PrimaryKey val _id: String, val accountKey: MicroBlogKey, @@ -57,7 +57,7 @@ data class DbDMConversation( } } -data class DbDirectMessageConversationWithMessage( +internal data class DbDirectMessageConversationWithMessage( @Relation(parentColumn = "conversationKey", entityColumn = "conversationKey") val conversation: DbDMConversation, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt index 40b6273f7..fcb04d217 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.room.db.model.DbDMEvent.Companion.saveToDb tableName = "dm_event", indices = [Index(value = ["accountKey", "conversationKey", "messageKey"], unique = true)], ) -data class DbDMEvent( +internal data class DbDMEvent( @PrimaryKey val _id: String, val accountKey: MicroBlogKey, @@ -67,7 +67,7 @@ data class DbDMEvent( } } -data class DbDMEventWithAttachments( +internal data class DbDMEventWithAttachments( @Embedded val message: DbDMEvent, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt index 6a3c0652a..f89fab264 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.enums.ComposeType @Entity( tableName = "draft", ) -data class DbDraft( +internal data class DbDraft( /** * Id that being used in the database */ diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt index 196448c36..844fc5930 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey tableName = "lists", indices = [Index(value = ["accountKey", "listKey"], unique = true)], ) -data class DbList( +internal data class DbList( @PrimaryKey var _id: String, val listId: String, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt index 6e6a2ae5f..1434e8f32 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.model.enums.MediaType tableName = "media", indices = [Index(value = ["belongToKey", "order"], unique = true)], ) -data class DbMedia( +internal data class DbMedia( /** * Id that being used in the database */ diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt index b25cb05b2..f3fc2f17f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt @@ -34,7 +34,7 @@ import com.twidere.twiderex.model.MicroBlogKey ) ], ) -data class DbNotificationCursor( +internal data class DbNotificationCursor( /** * Id that being used in the database */ diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt index 31d2fec65..74158451c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt @@ -36,7 +36,7 @@ import com.twidere.twiderex.model.MicroBlogKey ) ], ) -data class DbPagingTimeline( +internal data class DbPagingTimeline( @PrimaryKey val _id: String, val accountKey: MicroBlogKey, @@ -47,7 +47,7 @@ data class DbPagingTimeline( var isGap: Boolean, ) -data class DbPagingTimelineWithStatus( +internal data class DbPagingTimelineWithStatus( @Embedded val timeline: DbPagingTimeline, @@ -59,10 +59,10 @@ data class DbPagingTimelineWithStatus( val status: DbStatusWithReference, ) -enum class UserTimelineType { +internal enum class UserTimelineType { Status, Media, Favourite } -fun UserTimelineType.pagingKey(accountKey: MicroBlogKey) = "user:$accountKey:$this" +internal fun UserTimelineType.pagingKey(accountKey: MicroBlogKey) = "user:$accountKey:$this" diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt index e2c511551..73c473667 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey tableName = "search", indices = [Index(value = ["content", "accountKey"], unique = true)], ) -data class DbSearch( +internal data class DbSearch( /** * Id that being used in the database */ diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt index 3af2ce9e3..b7dda0df7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt @@ -40,7 +40,7 @@ import kotlinx.serialization.Serializable tableName = "status", indices = [Index(value = ["statusKey"], unique = true)], ) -data class DbStatusV2( +internal data class DbStatusV2( /** * Id that being used in the database */ @@ -72,11 +72,11 @@ data class DbStatusV2( var extra: Json ) -interface DbStatusExtra +internal interface DbStatusExtra @Immutable @Serializable -data class DbPreviewCard( +internal data class DbPreviewCard( val link: String, val displayLink: String?, val title: String?, @@ -86,7 +86,7 @@ data class DbPreviewCard( @Immutable @Serializable -data class DbPoll( +internal data class DbPoll( val id: String, val options: List, val expiresAt: Long?, @@ -100,28 +100,28 @@ data class DbPoll( @Immutable @Serializable -data class DbPollOption( +internal data class DbPollOption( val text: String, val count: Long, ) @Immutable @Serializable -data class DbTwitterStatusExtra( +internal data class DbTwitterStatusExtra( val reply_settings: TwitterReplySettings, val quoteCount: Long? = null, ) : DbStatusExtra @Immutable @Serializable -data class DbMastodonStatusExtra( +internal data class DbMastodonStatusExtra( val type: MastodonStatusType, val emoji: List, val visibility: MastodonVisibility, val mentions: List?, ) : DbStatusExtra -data class DbStatusWithMediaAndUser( +internal data class DbStatusWithMediaAndUser( @Embedded val data: DbStatusV2, @Relation(parentColumn = "statusKey", entityColumn = "belongToKey") @@ -134,7 +134,7 @@ data class DbStatusWithMediaAndUser( val url: List, ) -suspend fun List.saveToDb( +internal suspend fun List.saveToDb( database: RoomCacheDatabase ) { map { it.user }.let { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt index e4561f55b..54658ca8c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey tableName = "status_reactions", indices = [Index(value = ["statusKey", "accountKey"], unique = true)], ) -data class DbStatusReaction( +internal data class DbStatusReaction( /** * Id that being used in the database */ diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt index ee8bdf488..5f4bd93f2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt @@ -43,7 +43,7 @@ import java.util.UUID ), ], ) -data class DbStatusReference( +internal data class DbStatusReference( /** * Id that being used in the database */ @@ -54,7 +54,7 @@ data class DbStatusReference( val referenceStatusKey: MicroBlogKey, ) -data class DbStatusReferenceWithStatus( +internal data class DbStatusReferenceWithStatus( @Embedded val reference: DbStatusReference, @Relation( @@ -65,7 +65,7 @@ data class DbStatusReferenceWithStatus( val status: DbStatusWithMediaAndUser ) -fun DbStatusWithMediaAndUser?.toDbStatusReference( +internal fun DbStatusWithMediaAndUser?.toDbStatusReference( statusKey: MicroBlogKey, referenceType: ReferenceType, ): DbStatusReferenceWithStatus? { @@ -84,7 +84,7 @@ fun DbStatusWithMediaAndUser?.toDbStatusReference( } } -data class DbStatusWithReference( +internal data class DbStatusWithReference( @Embedded val status: DbStatusWithMediaAndUser, @Relation( @@ -95,7 +95,7 @@ data class DbStatusWithReference( val references: List, ) -suspend fun List.saveToDb( +internal suspend fun List.saveToDb( database: RoomCacheDatabase ) { this.map { it.references.map { it.status } + it.status } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt index 543087e6e..73575d350 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.room.db.RoomCacheDatabase tableName = "trends", indices = [Index(value = ["trendKey", "url"], unique = true)], ) -data class DbTrend( +internal data class DbTrend( @PrimaryKey val _id: String, val trendKey: MicroBlogKey, @@ -43,7 +43,7 @@ data class DbTrend( val volume: Long, ) -data class DbTrendWithHistory( +internal data class DbTrendWithHistory( @Embedded val trend: DbTrend, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt index 3267e8644..718bb59c0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey tableName = "trend_histories", indices = [Index(value = ["trendKey", "day"], unique = true)], ) -data class DbTrendHistory( +internal data class DbTrendHistory( @PrimaryKey val _id: String, val trendKey: MicroBlogKey, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt index 48eb8ccad..47c802ab2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey tableName = "url_entity", indices = [Index(value = ["statusKey", "url"], unique = true)], ) -data class DbUrlEntity( +internal data class DbUrlEntity( @PrimaryKey val _id: String, val statusKey: MicroBlogKey, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt index 9edb48ddb..36879c343 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt @@ -32,7 +32,7 @@ import kotlinx.serialization.Serializable tableName = "user", indices = [Index(value = ["userKey"], unique = true)], ) -data class DbUser( +internal data class DbUser( /** * Id that being used in the database */ @@ -61,14 +61,14 @@ data class DbUser( @Immutable @Serializable -data class DbTwitterUserExtra( +internal data class DbTwitterUserExtra( val pinned_tweet_id: String?, val url: List, ) @Immutable @Serializable -data class TwitterUrlEntity( +internal data class TwitterUrlEntity( val url: String, val expandedUrl: String, val displayUrl: String, @@ -76,7 +76,7 @@ data class TwitterUrlEntity( @Immutable @Serializable -data class DbMastodonUserExtra( +internal data class DbMastodonUserExtra( val fields: List, val emoji: List, val bot: Boolean, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt index 8f203c612..9fe8e41bb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.ComposeType -class ComposeTypeConverter { +internal class ComposeTypeConverter { @TypeConverter fun fromString(value: String?): ComposeType? { return value?.let { ComposeType.valueOf(it) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt index e9e8f9f05..dd413b40c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.room.db.model.DbTwitterUserExtra import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json -class ExtraConverter { +internal class ExtraConverter { @TypeConverter fun fromDbTwitterStatusExtraString(value: String?): DbTwitterStatusExtra? { return value?.fromJson() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt index ba7c7b3a3..7f8e1edf0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.MastodonVisibility -class MastodonVisibilityConverter { +internal class MastodonVisibilityConverter { @TypeConverter fun fromString(target: MastodonVisibility?): String? { return target?.name diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt index 5175afb10..ac7cc12dc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.MediaType -class MediaTypeConverter { +internal class MediaTypeConverter { @TypeConverter fun fromMediaType(mediaType: MediaType?): String? { return mediaType?.name diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt index 18a47d23a..5c9f14231 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.MicroBlogKey -class MicroBlogKeyConverter { +internal class MicroBlogKeyConverter { @TypeConverter fun fromMicroBlogKey(microBlogKey: MicroBlogKey?): String? { return microBlogKey?.toString() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt index b9eea656e..73a63f878 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.room.db.model.DbNotificationCursorType -class NotificationCursorTypeConverter { +internal class NotificationCursorTypeConverter { @TypeConverter fun fromString(value: String?): DbNotificationCursorType? { return value?.let { DbNotificationCursorType.valueOf(it) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt index 10ec6a967..e531112d9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.MastodonStatusType -class NotificationTypeConverter { +internal class NotificationTypeConverter { @TypeConverter fun fromString(value: String?): MastodonStatusType? { return value?.let { MastodonStatusType.valueOf(it) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt index 96db32ee9..a5fdee325 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.PlatformType -class PlatformTypeConverter { +internal class PlatformTypeConverter { @TypeConverter fun fromPlatformType(platformType: PlatformType?): String? { return platformType?.name diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt index a2309dc21..d71a37a48 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt @@ -24,7 +24,7 @@ import androidx.room.TypeConverter import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json -class StringListConverter { +internal class StringListConverter { @TypeConverter fun fromString(value: String?): List? { return value?.let { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt index 932f22ea0..c803d6013 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.model.enums.TwitterReplySettings -class TwitterReplySettingsConverter { +internal class TwitterReplySettingsConverter { @TypeConverter fun fromString(target: TwitterReplySettings?): String? { return target?.name diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt index b057acc3e..abfebb485 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.room.db.model.converter import androidx.room.TypeConverter import com.twidere.twiderex.room.db.model.UserTimelineType -class UserTimelineTypeConverter { +internal class UserTimelineTypeConverter { @TypeConverter fun fromPlatformType(timelineType: UserTimelineType?): String? { return timelineType?.name diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt index b5d3f2648..c8831feb4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt @@ -47,12 +47,16 @@ internal class DbPagingSource( } override suspend fun load(params: LoadParams): LoadResult { - val list = loadFromDb(params.key ?: 0, params.loadSize) - val nextKey = (params.key ?: 0) + list.size - return LoadResult.Page( - data = list, - prevKey = null, - nextKey = nextKey - ) + return try { + val list = loadFromDb(params.key ?: 0, params.loadSize) + val nextKey = if (list.size < params.loadSize) null else (params.key ?: 0) + list.size + LoadResult.Page( + data = list, + prevKey = null, + nextKey = nextKey + ) + } catch (e: Throwable) { + LoadResult.Error(e) + } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt index 4aad0a455..88d46e588 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt @@ -23,11 +23,11 @@ package com.twidere.twiderex.room.db.transform import android.accounts.Account import com.twidere.twiderex.model.TwidereAccount -fun Account.toTwidere() = TwidereAccount( +internal fun Account.toTwidere() = TwidereAccount( name = name, type = type ) -fun TwidereAccount.toAndroid() = Account( +internal fun TwidereAccount.toAndroid() = Account( name, type ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt index f7200cffa..a6c55a9d0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments import com.twidere.twiderex.room.db.model.DbDirectMessageConversationWithMessage import java.util.UUID -fun DbDMConversation.toUi() = UiDMConversation( +internal fun DbDMConversation.toUi() = UiDMConversation( accountKey = accountKey, conversationId = conversationId, conversationKey = conversationKey, @@ -43,12 +43,12 @@ fun DbDMConversation.toUi() = UiDMConversation( recipientKey = recipientKey, ) -fun DbDirectMessageConversationWithMessage.toUi() = UiDMConversationWithLatestMessage( +internal fun DbDirectMessageConversationWithMessage.toUi() = UiDMConversationWithLatestMessage( conversation = conversation.toUi(), latestMessage = latestMessage.toUi() ) -fun DbDMEventWithAttachments.toUi() = UiDMEvent( +internal fun DbDMEventWithAttachments.toUi() = UiDMEvent( accountKey = message.accountKey, sortId = message.sortId, conversationKey = message.conversationKey, @@ -70,7 +70,7 @@ fun DbDMEventWithAttachments.toUi() = UiDMEvent( sender = sender.toUi() ) -fun UiDMConversation.toDbDMConversation() = DbDMConversation( +internal fun UiDMConversation.toDbDMConversation() = DbDMConversation( accountKey = accountKey, conversationId = conversationId, conversationKey = conversationKey, @@ -85,7 +85,7 @@ fun UiDMConversation.toDbDMConversation() = DbDMConversation( _id = UUID.randomUUID().toString() ) -fun UiDMEvent.toDbMEventWithAttachments() = DbDMEventWithAttachments( +internal fun UiDMEvent.toDbMEventWithAttachments() = DbDMEventWithAttachments( message = DbDMEvent( _id = UUID.randomUUID().toString(), accountKey = accountKey, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt index a07d6d700..426607a1c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt @@ -24,7 +24,7 @@ import com.twidere.services.mastodon.model.Emoji import com.twidere.twiderex.model.ui.UiEmoji import com.twidere.twiderex.model.ui.UiEmojiCategory -fun List.toUi(): List = groupBy({ it.category }, { it }).map { +internal fun List.toUi(): List = groupBy({ it.category }, { it }).map { UiEmojiCategory( if (it.key.isNullOrEmpty()) null else it.key, it.value.map { emoji -> diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt index 37dd1fe2d..fa7413694 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt @@ -24,7 +24,7 @@ import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.room.db.model.DbList import java.util.UUID -fun DbList.toUi() = +internal fun DbList.toUi() = UiList( id = listId, ownerId = ownerId, @@ -38,7 +38,7 @@ fun DbList.toUi() = allowToSubscribe = allowToSubscribe, ) -fun UiList.toDbList() = +internal fun UiList.toDbList() = DbList( listId = id, ownerId = ownerId, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt index b187b50e6..786c5ae2a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt @@ -24,7 +24,7 @@ import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.room.db.model.DbMedia import java.util.UUID -fun List.toUi() = sortedBy { it.order }.map { +internal fun List.toUi() = sortedBy { it.order }.map { UiMedia( url = it.url, belongToKey = it.belongToKey, @@ -39,7 +39,7 @@ fun List.toUi() = sortedBy { it.order }.map { ) } -fun List.toDbMedia() = map { +internal fun List.toDbMedia() = map { DbMedia( url = it.url, belongToKey = it.belongToKey, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt index efb919555..385e1fa55 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.model.paging.NotificationCursor import com.twidere.twiderex.room.db.model.DbNotificationCursor import com.twidere.twiderex.room.db.model.DbNotificationCursorType -fun DbNotificationCursor.toUi() = NotificationCursor( +internal fun DbNotificationCursor.toUi() = NotificationCursor( _id = _id, accountKey = accountKey, type = type.toUi(), @@ -33,7 +33,7 @@ fun DbNotificationCursor.toUi() = NotificationCursor( timestamp = timestamp ) -fun NotificationCursor.toDbCursor() = DbNotificationCursor( +internal fun NotificationCursor.toDbCursor() = DbNotificationCursor( _id = _id, accountKey = accountKey, type = type.toDb(), @@ -41,13 +41,13 @@ fun NotificationCursor.toDbCursor() = DbNotificationCursor( timestamp = timestamp ) -fun DbNotificationCursorType.toUi() = when (this) { +internal fun DbNotificationCursorType.toUi() = when (this) { DbNotificationCursorType.General -> NotificationCursorType.General DbNotificationCursorType.Mentions -> NotificationCursorType.Mentions DbNotificationCursorType.Follower -> NotificationCursorType.Follower } -fun NotificationCursorType.toDb() = when (this) { +internal fun NotificationCursorType.toDb() = when (this) { NotificationCursorType.General -> DbNotificationCursorType.General NotificationCursorType.Mentions -> DbNotificationCursorType.Mentions NotificationCursorType.Follower -> DbNotificationCursorType.Follower diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt index 66caf8d7f..6c976b587 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt @@ -57,7 +57,7 @@ import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import java.util.UUID -fun DbStatusV2.toUi( +internal fun DbStatusV2.toUi( user: UiUser, media: List, url: List, @@ -107,7 +107,7 @@ fun DbStatusV2.toUi( ) } -fun DbStatusWithMediaAndUser.toUi( +internal fun DbStatusWithMediaAndUser.toUi( accountKey: MicroBlogKey, ): UiStatus { val reaction = reactions.firstOrNull { it.accountKey == accountKey } @@ -120,7 +120,7 @@ fun DbStatusWithMediaAndUser.toUi( ) } -fun DbStatusWithReference.toUi( +internal fun DbStatusWithReference.toUi( accountKey: MicroBlogKey, ) = with(status) { val reaction = reactions.firstOrNull { it.accountKey == accountKey } @@ -138,7 +138,7 @@ fun DbStatusWithReference.toUi( ) } -fun UiStatus.toDbStatusWithReference(accountKey: MicroBlogKey) = DbStatusWithReference( +internal fun UiStatus.toDbStatusWithReference(accountKey: MicroBlogKey) = DbStatusWithReference( status = toDbStatusWithMediaAndUser(accountKey), references = referenceStatus.map { entry -> DbStatusReferenceWithStatus( @@ -153,7 +153,7 @@ fun UiStatus.toDbStatusWithReference(accountKey: MicroBlogKey) = DbStatusWithRef } ) -fun UiStatus.toDbStatusWithMediaAndUser(accountKey: MicroBlogKey) = DbStatusWithMediaAndUser( +internal fun UiStatus.toDbStatusWithMediaAndUser(accountKey: MicroBlogKey) = DbStatusWithMediaAndUser( data = toDbStatusV2(), media = media.toDbMedia(), user = user.toDbUser(), @@ -168,7 +168,7 @@ fun UiStatus.toDbStatusWithMediaAndUser(accountKey: MicroBlogKey) = DbStatusWith ), url = url.toDbUrl(statusKey) ) -fun UiStatus.toDbStatusV2() = DbStatusV2( +internal fun UiStatus.toDbStatusV2() = DbStatusV2( _id = UUID.randomUUID().toString(), statusId = statusId, htmlText = htmlText, @@ -252,14 +252,14 @@ private fun DbPoll.toUi() = UiPoll( ownVotes = ownVotes ) -fun DbPagingTimelineWithStatus.toPagingTimeline(accountKey: MicroBlogKey) = with(status) { +internal fun DbPagingTimelineWithStatus.toPagingTimeline(accountKey: MicroBlogKey) = with(status) { PagingTimeLineWithStatus( timeline = timeline.toUi(), status = status.toUi(accountKey = accountKey) ) } -fun DbPagingTimeline.toUi() = PagingTimeLine( +internal fun DbPagingTimeline.toUi() = PagingTimeLine( accountKey = accountKey, pagingKey = pagingKey, statusKey = statusKey, @@ -268,7 +268,7 @@ fun DbPagingTimeline.toUi() = PagingTimeLine( isGap = isGap ) -fun PagingTimeLine.toDbPagingTimeline() = DbPagingTimeline( +internal fun PagingTimeLine.toDbPagingTimeline() = DbPagingTimeline( accountKey = accountKey, pagingKey = pagingKey, statusKey = statusKey, @@ -278,7 +278,7 @@ fun PagingTimeLine.toDbPagingTimeline() = DbPagingTimeline( _id = UUID.randomUUID().toString() ) -fun DbPagingTimelineWithStatus.toUi( +internal fun DbPagingTimelineWithStatus.toUi( accountKey: MicroBlogKey, ) = with(status.status) { val reaction = reactions.firstOrNull { it.accountKey == accountKey } @@ -296,19 +296,19 @@ fun DbPagingTimelineWithStatus.toUi( ) } -fun DbTwitterStatusExtra.toUi() = TwitterStatusExtra( +internal fun DbTwitterStatusExtra.toUi() = TwitterStatusExtra( reply_settings = reply_settings, quoteCount = quoteCount ) -fun DbMastodonStatusExtra.toUi() = MastodonStatusExtra( +internal fun DbMastodonStatusExtra.toUi() = MastodonStatusExtra( type = type, emoji = emoji.toUi(), visibility = visibility, mentions = mentions?.toUi() ) -fun List.toUi() = map { +internal fun List.toUi() = map { MastodonMention( id = it.id, username = it.username, @@ -317,7 +317,7 @@ fun List.toUi() = map { ) } -fun DbPreviewCard.toUi() = UiCard( +internal fun DbPreviewCard.toUi() = UiCard( link = link, displayLink = displayLink, title = title, @@ -325,7 +325,7 @@ fun DbPreviewCard.toUi() = UiCard( image = image ) -fun Poll.toUi() = id?.let { +internal fun Poll.toUi() = id?.let { UiPoll( id = it, options = options?.map { option -> diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt index 425ec10bb..39b3c3ada 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.room.db.model.DbTrendHistory import com.twidere.twiderex.room.db.model.DbTrendWithHistory import java.util.UUID -fun DbTrendWithHistory.toUi(accountKey: MicroBlogKey) = UiTrend( +internal fun DbTrendWithHistory.toUi(accountKey: MicroBlogKey) = UiTrend( trendKey = trend.trendKey, displayName = trend.displayName, url = trend.url, @@ -40,21 +40,21 @@ fun DbTrendWithHistory.toUi(accountKey: MicroBlogKey) = UiTrend( accountKey = accountKey ) -fun DbTrendHistory.toUi() = UiTrendHistory( +internal fun DbTrendHistory.toUi() = UiTrendHistory( trendKey = trendKey, day = day, uses = uses, accounts = accounts ) -fun List.toDbTrendWithHistory() = map { +internal fun List.toDbTrendWithHistory() = map { DbTrendWithHistory( trend = it.toDbTrend(), history = it.history.map { history -> history.toDbTrendHistory(it.accountKey) } ) } -fun UiTrend.toDbTrend() = DbTrend( +internal fun UiTrend.toDbTrend() = DbTrend( _id = UUID.randomUUID().toString(), trendKey = trendKey, displayName = displayName, @@ -64,7 +64,7 @@ fun UiTrend.toDbTrend() = DbTrend( accountKey = accountKey ) -fun UiTrendHistory.toDbTrendHistory(accountKey: MicroBlogKey) = DbTrendHistory( +internal fun UiTrendHistory.toDbTrendHistory(accountKey: MicroBlogKey) = DbTrendHistory( trendKey = trendKey, day = day, uses = uses, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt index 1fe1c0b14..9c0ec2c68 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.room.db.model.DbUrlEntity import java.util.UUID -fun DbUrlEntity.toUi() = UiUrlEntity( +internal fun DbUrlEntity.toUi() = UiUrlEntity( url = url, expandedUrl = expandedUrl, displayUrl = displayUrl, @@ -33,9 +33,9 @@ fun DbUrlEntity.toUi() = UiUrlEntity( description = description, image = image ) -fun List.toUi() = map { it.toUi() } +internal fun List.toUi() = map { it.toUi() } -fun List.toDbUrl(belongToKey: MicroBlogKey) = map { +internal fun List.toDbUrl(belongToKey: MicroBlogKey) = map { DbUrlEntity( url = it.url, _id = UUID.randomUUID().toString(), diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt index c0ed0d7bc..ce5e79523 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import java.util.UUID -fun DbUser.toAmUser() = +internal fun DbUser.toAmUser() = AmUser( userId = userId, name = name, @@ -55,7 +55,7 @@ fun DbUser.toAmUser() = isProtected = isProtected, ) -fun DbUser.toUi() = +internal fun DbUser.toUi() = UiUser( id = userId, name = name, @@ -85,7 +85,7 @@ fun DbUser.toUi() = acct = acct, ) -fun UiUser.toDbUser() = +internal fun UiUser.toDbUser() = DbUser( _id = UUID.randomUUID().toString(), name = name, @@ -141,7 +141,7 @@ fun UiUser.toDbUser() = statusesCount = metrics.status ) -fun DbTwitterUserExtra.toUi() = TwitterUserExtra( +internal fun DbTwitterUserExtra.toUi() = TwitterUserExtra( pinned_tweet_id = pinned_tweet_id, url = url.map { url -> UiUrlEntity( @@ -155,7 +155,7 @@ fun DbTwitterUserExtra.toUi() = TwitterUserExtra( } ) -fun DbMastodonUserExtra.toUi() = MastodonUserExtra( +internal fun DbMastodonUserExtra.toUi() = MastodonUserExtra( emoji = emoji.toUi(), bot = bot, locked = locked, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 3264024a7..e9dcbeef0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -43,7 +43,9 @@ import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging import com.twidere.services.twitter.model.UserV2 import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch import org.jetbrains.annotations.TestOnly @@ -64,6 +66,22 @@ fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty order = 0 ) +@TestOnly +fun mockUiDraft( + draftId: String = "", + content: String = "", + composeType: ComposeType = ComposeType.New, + statusKey: MicroBlogKey = MicroBlogKey.twitter(UUID.randomUUID().toString()) +) = UiDraft( + draftId = draftId, + content = content, + media = emptyList(), + createdAt = System.currentTimeMillis(), + composeType = composeType, + statusKey = statusKey, + excludedReplyUserIds = null +) + @TestOnly fun mockUiSearch(content: String = "", accountKey: MicroBlogKey = MicroBlogKey.Empty, saved: Boolean = false) = UiSearch( content = content, From aaf0e8cbb8c317b62721293d52677ea41e377713 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 12:22:36 +0800 Subject: [PATCH 072/615] add BaseDaoTest for androidAndroidTest --- .../twidere/twiderex/db/TrendDaoImplTest.kt | 26 ++-------- .../twiderex/db/base/AppDatabaseDaoTest.kt | 27 ++++++++++ .../twidere/twiderex/db/base/BaseDaoTest.kt | 52 +++++++++++++++++++ .../twiderex/db/base/CacheDatabaseDaoTest.kt | 27 ++++++++++ 4 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt index dd1513519..e79138f31 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt @@ -20,26 +20,19 @@ */ package com.twidere.twiderex.db -import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.paging.PagingSource -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.twidere.services.mastodon.model.TrendHistory import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.dataprovider.mapper.toUiTrend +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest import com.twidere.twiderex.mock.model.mockITrend import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiTrend -import com.twidere.twiderex.room.db.RoomCacheDatabase import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import java.util.concurrent.Executors import kotlin.test.assertEquals import kotlin.test.assertNull @@ -47,8 +40,7 @@ typealias TwitterTrend = com.twidere.services.twitter.model.Trend typealias MastodonTrend = com.twidere.services.mastodon.model.Trend @RunWith(AndroidJUnit4::class) -internal class TrendDaoImplTest { - private lateinit var roomDatabase: RoomCacheDatabase +internal class TrendDaoImplTest : CacheDatabaseDaoTest() { private val twitterAccountKey = MicroBlogKey.twitter("123") private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") private val trends = mutableListOf() @@ -56,13 +48,8 @@ internal class TrendDaoImplTest { private val twitterTrendCount = 10 private val mastodonTrendCount = 10 - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - roomDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() + override fun setUp() { + super.setUp() for (i in 0 until twitterTrendCount) { trends.add( TwitterTrend( @@ -88,11 +75,6 @@ internal class TrendDaoImplTest { } } - @After - fun tearDown() { - roomDatabase.close() - } - @Test fun insert_SaveBothTrendAndHistory() = runBlocking { val cacheDatabase = CacheDatabaseImpl(roomDatabase) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt new file mode 100644 index 000000000..061706ad2 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.base + +import com.twidere.twiderex.room.db.RoomAppDatabase + +internal open class AppDatabaseDaoTest : BaseDaoTest() { + override fun getDBClass() = RoomAppDatabase::class.java +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt new file mode 100644 index 000000000..02777a5d5 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt @@ -0,0 +1,52 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.base + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.runner.RunWith +import java.util.concurrent.Executors + +@RunWith(AndroidJUnit4::class) +internal abstract class BaseDaoTest { + protected lateinit var roomDatabase: DB + @get:Rule + val rule = InstantTaskExecutorRule() + + @Before + open fun setUp() { + roomDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), getDBClass()) + .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() + } + + @After + open fun tearDown() { + roomDatabase.close() + } + + abstract fun getDBClass(): Class +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt new file mode 100644 index 000000000..83c3f12b8 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.base + +import com.twidere.twiderex.room.db.RoomCacheDatabase + +internal open class CacheDatabaseDaoTest : BaseDaoTest() { + override fun getDBClass() = RoomCacheDatabase::class.java +} From fa53e83a19613a40f5a3e152beeb66e1d118d2b8 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 24 Aug 2021 15:45:12 +0800 Subject: [PATCH 073/615] add window lifecycle --- .../tlaster/precompose/PreComposeWindow.kt | 66 +++++++++++++------ .../kotlin/com/twidere/twiderex/main.kt | 4 +- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt index 36b33951d..26ff6479c 100644 --- a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt +++ b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt @@ -22,13 +22,18 @@ package moe.tlaster.precompose import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged +import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.LifecycleOwner import moe.tlaster.precompose.lifecycle.LifecycleRegistry import moe.tlaster.precompose.ui.BackDispatcher @@ -55,34 +60,53 @@ fun PreComposeWindow( onKeyEvent: (KeyEvent) -> Boolean = { false }, content: @Composable FrameWindowScope.() -> Unit ) { - Window( - onCloseRequest, - state, - visible, - title, - icon, - undecorated, - resizable, - enabled, - focusable, - alwaysOnTop, - onPreviewKeyEvent, - onKeyEvent, - content = { - ProvideDesktopCompositionLocals { - content.invoke(this) + val holder = remember { + PreComposeWindowHolder() + } + LaunchedEffect(Unit) { + snapshotFlow { state.isMinimized } + .distinctUntilChanged() + .collect { + holder.lifecycle.currentState = if (it) { + Lifecycle.State.InActive + } else { + Lifecycle.State.Active + } } - }, - ) + } + ProvideDesktopCompositionLocals( + holder + ) { + Window( + onCloseRequest = { + holder.lifecycle.currentState = Lifecycle.State.Destroyed + onCloseRequest.invoke() + }, + state, + visible, + title, + icon, + undecorated, + resizable, + enabled, + focusable, + alwaysOnTop, + onPreviewKeyEvent, + onKeyEvent, + content = { + content.invoke(this) + }, + ) + } } @Composable private fun ProvideDesktopCompositionLocals( + holder: PreComposeWindowHolder = remember { + PreComposeWindowHolder() + }, content: @Composable () -> Unit, ) { - val holder = remember { - PreComposeWindowHolder() - } CompositionLocalProvider( LocalLifecycleOwner provides holder, LocalViewModelStoreOwner provides holder, diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index 03e5b7ed8..4d740b753 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -21,9 +21,9 @@ package com.twidere.twiderex import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import com.twidere.twiderex.di.setupModules +import moe.tlaster.precompose.PreComposeWindow import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -34,7 +34,7 @@ fun main() { setupModules() } application { - Window( + PreComposeWindow( onCloseRequest = { stopKoin() exitApplication() From 43a2bb51e257cbe037684ecddebdd143322a3591 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 16:08:46 +0800 Subject: [PATCH 074/615] move all android test from android module to common module --- .../twiderex/ExampleInstrumentedTest.kt | 42 --- .../com/twidere/twiderex/db/DbDMEventTest.kt | 278 ------------------ .../com/twidere/twiderex/db/DbListTest.kt | 194 ------------ .../com/twidere/twiderex/db/DbSearchTest.kt | 149 ---------- .../com/twidere/twiderex/db/DbTrendTest.kt | 139 --------- .../twidere/twiderex/db/ListsDaoImplTest.kt | 121 ++++++++ .../twidere/twiderex/db/SearchDaoImplTest.kt | 133 +++++++++ .../db/base/DirectMessageEventDaoImplTest.kt | 156 ++++++++++ .../dataprovider/db/AppDatabaseImpl.kt | 2 +- .../db/dao/DirectMessageEventDaoImpl.kt | 18 +- .../dataprovider/db/dao/ListsDaoImpl.kt | 21 +- .../dataprovider/db/dao/SearchDaoImpl.kt | 26 +- .../twiderex/room/db/dao/RoomUrlEntityDao.kt | 5 + .../db/transform/DmConversationTransform.kt | 19 +- .../room/db/transform/ListTransform.kt | 4 +- .../room/db/transform/MediaTransform.kt | 4 +- .../room/db/transform/SearchTransform.kt | 4 +- .../room/db/transform/UrlEntityTransform.kt | 4 +- .../room/db/transform/UserTransform.kt | 4 +- .../twidere/twiderex/mock/model/MockModels.kt | 23 +- 20 files changed, 511 insertions(+), 835 deletions(-) delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt delete mode 100644 android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt diff --git a/android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt b/android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt deleted file mode 100644 index 9f35d97f4..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.twidere.twiderex", appContext.packageName) - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt deleted file mode 100644 index 7d98b2a58..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbDMEventTest.kt +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.paging.PagingSource -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.services.twitter.model.Attachment -import com.twidere.services.twitter.model.DirectMessageEvent -import com.twidere.services.twitter.model.Entities -import com.twidere.services.twitter.model.EntitiesURL -import com.twidere.services.twitter.model.MessageCreate -import com.twidere.services.twitter.model.MessageData -import com.twidere.services.twitter.model.MessageTarget -import com.twidere.services.twitter.model.PurpleMedia -import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.mapper.toDbDirectMessage -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbDMEventWithAttachments.Companion.saveToDb -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.RoomCacheDatabase -import com.twidere.twiderex.room.db.model.DbDMConversation -import com.twidere.twiderex.room.db.model.DbDMConversation.Companion.saveToDb -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.UUID -import java.util.concurrent.Executors - -@RunWith(AndroidJUnit4::class) -class DbDMEventTest { - private lateinit var cacheDatabase: RoomCacheDatabase - private val user1AccountKey = MicroBlogKey.twitter("1") - private val user2AccountKey = MicroBlogKey.twitter("2") - private val conversationCount = 5 - - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() - runBlocking { - for (i in 0 until conversationCount) { - generateDirectMessage( - accountKey = user1AccountKey, - System.currentTimeMillis().toString(), - user1AccountKey.id - ).also { - it.saveToDb(cacheDatabase) - }.toConversation() - .saveToDb(cacheDatabase) - generateDirectMessage( - accountKey = user2AccountKey, - System.currentTimeMillis().toString(), - user2AccountKey.id - ).also { - it.saveToDb(cacheDatabase) - }.toConversation() - .saveToDb(cacheDatabase) - } - } - } - - private fun List.toConversation() = - groupBy { - it.message.conversationKey - }.map { - it.value.maxByOrNull { msg -> msg.message.sortId }?.message!! - }.map { - DbDMConversation( - _id = UUID.randomUUID().toString(), - accountKey = it.accountKey, - conversationId = it.conversationKey.id, - conversationKey = it.conversationKey, - conversationAvatar = "", - conversationName = it.htmlText, - conversationType = DbDMConversation.Type.ONE_TO_ONE, - conversationSubName = "", - recipientKey = it.conversationUserKey, - ) - } - - private suspend fun generateDirectMessage(accountKey: MicroBlogKey, senderId: String, recipientId: String): List { - val messageList = mutableListOf() - val count = 5 - for (i in 0 until count) { - messageList.add( - DirectMessageEvent( - createdTimestamp = System.currentTimeMillis().toString(), - id = UUID.randomUUID().toString(), - type = "message_create", - messageCreate = MessageCreate( - messageData = MessageData( - text = "message:$count", - entities = Entities( - urls = listOf( - EntitiesURL( - display_url = "url$count", - expanded_url = "expanded:$count", - url = "url:$count", - indices = listOf(0, 1) - ) - ) - ), - attachment = Attachment( - type = "media", - media = PurpleMedia( - id = count.toLong(), - idStr = count.toString(), - ) - ) - ), - senderId = senderId, - target = MessageTarget( - recipientId - ) - ) - ).toDbDirectMessage( - accountKey, - User( - id = senderId.toLong(), - idStr = senderId, - - ).toDbUser() - ) - ) - delay(1) - } - return messageList - } - - @After - fun tearDown() { - cacheDatabase.close() - } - - @Test - fun insertMessages_GetAllMessagesByAccountKey() = runBlocking { - val user1Messages = cacheDatabase.directMessageDao().getAll(accountKey = user1AccountKey) - user1Messages.forEach { - Assert.assertEquals(user1AccountKey, it.message.accountKey) - } - val user2Messages = cacheDatabase.directMessageDao().getAll(accountKey = user2AccountKey) - user2Messages.forEach { - Assert.assertEquals(user2AccountKey, it.message.accountKey) - } - } - - @Test - fun getAllMessages_ContainsDbMediaAndUrl() = runBlocking { - val user1Messages = cacheDatabase.directMessageDao().getAll(accountKey = user1AccountKey) - user1Messages.forEach { - assert(it.media.isNotEmpty()) - assert(it.urlEntity.isNotEmpty()) - it.media.forEach { media -> - Assert.assertEquals(it.message.messageKey, media.belongToKey) - } - it.urlEntity.forEach { url -> - Assert.assertEquals(it.message.messageKey, url.statusKey) - } - } - } - - @Test - fun insertAllConversations_GetConversationsByAccountKey() = runBlocking { - val user1Conversation = cacheDatabase.directMessageConversationDao().find(accountKey = user1AccountKey) - Assert.assertEquals(conversationCount, user1Conversation.size) - user1Conversation.forEach { - Assert.assertEquals(user1AccountKey, it.conversation.accountKey) - } - val user2Conversation = cacheDatabase.directMessageConversationDao().find(accountKey = user2AccountKey) - user2Conversation.forEach { - Assert.assertEquals(user2AccountKey, it.conversation.accountKey) - } - } - - @Test - fun foundConversations_ContainsLatestMessages() = runBlocking { - val conversations = cacheDatabase.directMessageConversationDao().find(accountKey = user1AccountKey) - Assert.assertEquals(conversationCount, conversations.size) - conversations.forEach { - // check if latest message belong to this conversation - Assert.assertEquals(it.conversation.conversationKey, it.latestMessage.message.conversationKey) - // check if it is the latest message - val latestMessage = cacheDatabase.directMessageDao().find(it.conversation.accountKey, it.conversation.conversationKey) - .maxByOrNull { msg -> msg.message.sortId } - Assert.assertEquals(it.latestMessage.message.messageId, latestMessage?.message?.messageId) - } - } - - @Test - fun deleteConversation() = runBlocking { - val result = cacheDatabase.directMessageConversationDao().find(accountKey = user1AccountKey) - Assert.assertEquals(conversationCount, result.size) - cacheDatabase.directMessageDao().clearConversation(user1AccountKey, result[0].conversation.conversationKey) - cacheDatabase.directMessageConversationDao().delete(result[0].conversation) - Assert.assertEquals(result.size - 1, cacheDatabase.directMessageConversationDao().find(user1AccountKey).size) - } - - @Test - fun deleteAndClearMessages() = runBlocking { - val result = cacheDatabase.directMessageDao().getAll(user1AccountKey) - cacheDatabase.directMessageDao().delete(result[0].message) - Assert.assertEquals(result.size - 1, cacheDatabase.directMessageDao().getAll(user1AccountKey).size) - - cacheDatabase.directMessageDao().clearAll(user1AccountKey) - assert(cacheDatabase.directMessageDao().find(user1AccountKey, result[1].message.conversationKey).isEmpty()) - } - - @Test - fun clearConversation() = runBlocking { - cacheDatabase.directMessageDao().clearAll(user1AccountKey) - cacheDatabase.directMessageConversationDao().clearAll(user1AccountKey) - assert(cacheDatabase.directMessageConversationDao().find(user1AccountKey).isEmpty()) - } - - @Test - fun testMessagePagingSource() = runBlocking { - val conversation = cacheDatabase.directMessageConversationDao().find(user1AccountKey)[0] - val pagingSource = cacheDatabase.directMessageDao().getPagingSource(user1AccountKey, conversation.conversation.conversationKey) - val resultFirst = pagingSource.load(PagingSource.LoadParams.Refresh(null, loadSize = 2, false)) - Assert.assertEquals(2, (resultFirst as PagingSource.LoadResult.Page).data.size) - - val resultLoadMore = pagingSource.load(PagingSource.LoadParams.Append(resultFirst.nextKey ?: 2, loadSize = 2, false)) - Assert.assertEquals(2, (resultLoadMore as PagingSource.LoadResult.Page).data.size) - } - - @Test - fun testConversationPagingSource() = runBlocking { - val conversationPagingSource = cacheDatabase.directMessageConversationDao().getPagingSource(user1AccountKey) - val resultFirst = conversationPagingSource.load(PagingSource.LoadParams.Refresh(null, loadSize = 2, false)) - Assert.assertEquals(2, (resultFirst as PagingSource.LoadResult.Page).data.size) - - val resultLoadMore = conversationPagingSource.load(PagingSource.LoadParams.Append(resultFirst.nextKey ?: 2, loadSize = 2, false)) - Assert.assertEquals(2, (resultLoadMore as PagingSource.LoadResult.Page).data.size) - } - - @Test - fun get_OrderBySortId() = runBlocking { - val result = cacheDatabase.directMessageConversationDao().find(accountKey = user1AccountKey) - result.forEachIndexed { index, con -> - if (index < result.size - 2) assert(con.latestMessage.message.sortId > result[index + 1].latestMessage.message.sortId) - } - - val message = cacheDatabase.directMessageDao().find(accountKey = user1AccountKey, conversationKey = result[0].conversation.conversationKey) - message.forEachIndexed { index, msg -> - if (index < result.size - 2) assert(msg.message.sortId > message[index + 1].message.sortId) - } - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt deleted file mode 100644 index b299aa83d..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbListTest.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.paging.PagingSource -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.services.mastodon.model.MastodonList -import com.twidere.services.microblog.model.IListModel -import com.twidere.services.twitter.model.TwitterList -import com.twidere.services.twitter.model.User -import com.twidere.twiderex.db.dao.ListsDao -import com.twidere.twiderex.db.mapper.toDbList -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.RoomCacheDatabase -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.concurrent.Executors - -@RunWith(AndroidJUnit4::class) -class DbListTest { - private lateinit var listsDao: ListsDao - private lateinit var cacheDatabase: RoomCacheDatabase - private val twitterAccountKey = MicroBlogKey.twitter("123") - private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") - private val originData = mutableListOf() - private val twitterCount = 10 - private val mastodonCount = 10 - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() - listsDao = cacheDatabase.listsDao() - for (i in 0 until twitterCount) { - val ownerId = if (i % 2 == 0) twitterAccountKey.id else "789" - originData.add( - TwitterList( - id = i.toLong(), - name = "twitter name $i", - description = "description $i", - mode = "private", - idStr = i.toString(), - user = User(id = ownerId.toLong(), idStr = ownerId) - ) - ) - } - for (i in 0 until mastodonCount) { - originData.add( - MastodonList( - id = i.toString(), - title = "mastodon name $i", - ) - ) - } - runBlocking { - val dbLists = originData.map { - it.toDbList(if (it is TwitterList) twitterAccountKey else mastodonAccountKey) - } - Assert.assertEquals(originData.size, dbLists.size) - listsDao.insertAll(dbLists) - } - } - - @After - fun tearDown() { - cacheDatabase.close() - } - - @Test - fun checkInsertDbLists() { - runBlocking { - val result = listsDao.findAll() - Assert.assertEquals(originData.size, result?.size) - } - } - - @Test - fun findDbListWithListKey() { - runBlocking { - val twitterList = listsDao.findWithListKey(MicroBlogKey.twitter("0"), twitterAccountKey) - Assert.assertEquals("0", twitterList?.listId) - twitterList?.let { assert(it.title.startsWith("twitter")) } - - val mastodonList = listsDao.findWithListKey(MicroBlogKey("0", mastodonAccountKey.host), mastodonAccountKey) - Assert.assertEquals("0", mastodonList?.listId) - mastodonList?.let { assert(it.title.startsWith("mastodon")) } - } - } - - @Test - fun findDbListWithListKeyWithFlow_AutoUpdateAfterDbUpdate() { - runBlocking { - val source = listsDao.findWithListKeyWithFlow(MicroBlogKey.twitter("0"), twitterAccountKey) - var data = source.firstOrNull() - Assert.assertEquals("description 0", data?.description) - data?.let { - listsDao.update(listOf(it.copy(description = "Update 0"))) - } - data = source.firstOrNull() - Assert.assertEquals("Update 0", data?.description) - } - } - - @Test - fun findDbListsWithAccountKey() { - runBlocking { - val twitterLists = listsDao.findWithAccountKey(twitterAccountKey) - Assert.assertEquals(twitterCount, twitterLists?.size) - twitterLists?.forEach { - assert(it.title.startsWith("twitter")) - } - val mastodonList = listsDao.findWithAccountKey(mastodonAccountKey) - Assert.assertEquals(mastodonCount, mastodonList?.size) - mastodonList?.forEach { - assert(it.title.startsWith("mastodon")) - } - } - } - - @Test - fun updateDbList() { - runBlocking { - var updateList = listsDao.findWithListKey(MicroBlogKey.twitter("0"), twitterAccountKey) - Assert.assertNotNull(updateList) - listsDao.update(listOf(updateList!!.copy(title = "updated title"))) - updateList = listsDao.findWithListKey(MicroBlogKey.twitter("0"), twitterAccountKey) - Assert.assertNotNull(updateList) - Assert.assertEquals("updated title", updateList?.title) - } - } - - @Test - fun deleteDbList() { - runBlocking { - var deleteList = listsDao.findWithListKey(MicroBlogKey.twitter("0"), twitterAccountKey) - Assert.assertNotNull(deleteList) - listsDao.delete(listOf(deleteList!!)) - deleteList = listsDao.findWithListKey(MicroBlogKey.twitter("0"), twitterAccountKey) - Assert.assertNull(deleteList) - } - } - - @Test - fun testPagingSource() { - runBlocking { - val pagingSource = listsDao.getPagingSource(twitterAccountKey) - val resultFirst = pagingSource.load(PagingSource.LoadParams.Refresh(null, loadSize = 2, false)) - Assert.assertEquals(2, (resultFirst as PagingSource.LoadResult.Page).data.size) - Assert.assertEquals("0", resultFirst.data[0].listId) - - val resultLoadMore = pagingSource.load(PagingSource.LoadParams.Append(resultFirst.nextKey ?: 2, loadSize = 2, false)) - Assert.assertEquals(2, (resultLoadMore as PagingSource.LoadResult.Page).data.size) - Assert.assertEquals("2", resultLoadMore.data[0].listId) - } - } - - @Test - fun clearDbList() { - runBlocking { - listsDao.clearAll(twitterAccountKey) - val restLists = listsDao.findAll() - Assert.assertEquals(mastodonCount, restLists?.size) - } - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt deleted file mode 100644 index 37d50cf86..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbSearchTest.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.twiderex.db.dao.SearchDao -import com.twidere.twiderex.db.model.DbSearch -import com.twidere.twiderex.model.MicroBlogKey -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.UUID - -@RunWith(AndroidJUnit4::class) -class DbSearchTest { - private lateinit var appDatabase: AppDatabase - private val twitterAccountKey = MicroBlogKey.twitter("123") - private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") - - private val searches = mutableListOf() - private lateinit var searchDao: SearchDao - private val twitterSearchCount = 10 - private val mastodonSearchCount = 10 - - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - appDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - searchDao = appDatabase.searchDao() - for (i in 0 until twitterSearchCount) { - searches.add( - DbSearch( - _id = UUID.randomUUID().toString(), - content = "twitter $i", - lastActive = System.currentTimeMillis(), - saved = i % 2 == 0, - accountKey = twitterAccountKey - ) - ) - } - for (i in 0 until mastodonSearchCount) { - searches.add( - DbSearch( - _id = UUID.randomUUID().toString(), - content = "mastodon $i", - lastActive = System.currentTimeMillis(), - saved = i % 2 == 0, - accountKey = mastodonAccountKey - ) - ) - } - - runBlocking { - searchDao.insertAll(searches) - } - } - - @After - fun tearDown() { - appDatabase.close() - } - - @Test - fun getAll_returnResultByAccountKey() = runBlocking { - val result = searchDao.getAll(twitterAccountKey) - val value = result.firstOrNull() - Assert.assertEquals(twitterSearchCount, value?.size) - value?.forEach { - assert(it.content.startsWith("twitter")) - } ?: assert(false) - } - - @Test - fun getAllHistory_returnResultsMatchAccountKeyAndNotSaved() = runBlocking { - val result = searchDao.getAllHistory(mastodonAccountKey).firstOrNull() - result?.forEach { - assert(it.content.startsWith("mastodon")) - assert(!it.saved) - } ?: assert(false) - } - - @Test - fun getAllSaved_returnResultsMatchAccountKeyAndSaved() = runBlocking { - val result = searchDao.getAllSaved(twitterAccountKey).firstOrNull() - result?.forEach { - assert(it.content.startsWith("twitter")) - assert(it.saved) - } ?: assert(false) - } - - @Test - fun getSearch_returnResultsMatchContentAndAccountKey() = runBlocking { - val result = searchDao.get("twitter 0", twitterAccountKey) - Assert.assertEquals("twitter 0", result?.content) - val errorResult = searchDao.get("twitter 0", mastodonAccountKey) - assert(errorResult == null) - } - - @Test - fun deleteSearch_getNullAfterDelete() = runBlocking { - val result = searchDao.get("twitter 0", twitterAccountKey) - Assert.assertEquals("twitter 0", result?.content) - searchDao.remove(result!!) - assert(searchDao.get("twitter 0", twitterAccountKey) == null) - } - - @Test - fun clearHistory_deleteSearchNotSaved() = runBlocking { - searchDao.clear() - val twitterResult = searchDao.getAll(twitterAccountKey).firstOrNull() - val mastodonResult = searchDao.getAll(mastodonAccountKey).firstOrNull() - twitterResult?.forEach { - assert(it.saved) - } ?: assert(false) - - mastodonResult?.forEach { - assert(it.saved) - } ?: assert(false) - } -} diff --git a/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt b/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt deleted file mode 100644 index 70a695825..000000000 --- a/android/src/androidTest/java/com/twidere/twiderex/db/DbTrendTest.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.db - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.paging.PagingSource -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.twidere.services.mastodon.model.TrendHistory -import com.twidere.twiderex.db.mapper.toDbTrend -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.RoomCacheDatabase -import com.twidere.twiderex.room.db.model.DbTrendWithHistory -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.util.concurrent.Executors - -typealias TwitterTrend = com.twidere.services.twitter.model.Trend -typealias MastodonTrend = com.twidere.services.mastodon.model.Trend - -@RunWith(AndroidJUnit4::class) -class DbTrendTest { - private lateinit var cacheDatabase: RoomCacheDatabase - private val twitterAccountKey = MicroBlogKey.twitter("123") - private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") - private val trends = mutableListOf() - - private val twitterTrendCount = 10 - private val mastodonTrendCount = 10 - - @get:Rule - val rule = InstantTaskExecutorRule() - - @Before - fun setUp() { - cacheDatabase = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java) - .setTransactionExecutor(Executors.newSingleThreadExecutor()).build() - for (i in 0 until twitterTrendCount) { - trends.add( - TwitterTrend( - name = "tweet $i", - url = "https://tweet $i" - ).toDbTrend(twitterAccountKey) - ) - } - for (i in 0 until mastodonTrendCount) { - trends.add( - MastodonTrend( - name = "mastodon $i", - url = "https://mastodon $i", - history = mutableListOf( - TrendHistory( - accounts = "10", - uses = "20", - day = "${System.currentTimeMillis()}" - ) - ) - ).toDbTrend(mastodonAccountKey) - ) - } - } - - @After - fun tearDown() { - cacheDatabase.close() - } - - @Test - fun findAll_matchTheAccountKeyAndLimit() = runBlocking { - trends.saveToDb(cacheDatabase) - - val twitterTrends = cacheDatabase.trendDao().find(twitterAccountKey, 5) - Assert.assertEquals(5, twitterTrends.size) - assert(twitterTrends[0].trend.displayName.startsWith("tweet")) - assert(twitterTrends[0].trend.url.startsWith("https://tweet")) - Assert.assertEquals(0, twitterTrends[0].history.size) - - val mastodonTrends = cacheDatabase.trendDao().find(mastodonAccountKey, 7) - Assert.assertEquals(7, mastodonTrends.size) - assert(mastodonTrends[0].trend.displayName.startsWith("mastodon")) - assert(mastodonTrends[0].trend.url.startsWith("https://mastodon")) - // check if the history trend key is the same to trend - Assert.assertEquals(mastodonTrends[0].trend.trendKey, mastodonTrends[0].history[0].trendKey) - } - - @Test - fun testPagingSource() = runBlocking { - trends.saveToDb(cacheDatabase) - val pagingSource = cacheDatabase.trendDao().getPagingSource(twitterAccountKey) - val resultFirst = pagingSource.load(PagingSource.LoadParams.Refresh(null, loadSize = 2, false)) - Assert.assertEquals(2, (resultFirst as PagingSource.LoadResult.Page).data.size) - Assert.assertEquals("tweet 0", resultFirst.data[0].trend.displayName) - - val resultLoadMore = pagingSource.load(PagingSource.LoadParams.Append(resultFirst.nextKey ?: 2, loadSize = 2, false)) - Assert.assertEquals(2, (resultLoadMore as PagingSource.LoadResult.Page).data.size) - Assert.assertEquals("tweet 2", resultLoadMore.data[0].trend.displayName) - } - - @Test - fun clearAll_matchTheAccountKey() = runBlocking { - trends.saveToDb(cacheDatabase) - // make sure twitter trends is in database - Assert.assertEquals(twitterTrendCount, cacheDatabase.trendDao().find(twitterAccountKey, twitterTrendCount).size) - cacheDatabase.trendDao().clearAll(twitterAccountKey) - // check whether twitter trends has been deleted - Assert.assertEquals(0, cacheDatabase.trendDao().find(twitterAccountKey, twitterTrendCount).size) - - // make sure mastodon trends and history is in database - Assert.assertEquals(mastodonTrendCount, cacheDatabase.trendDao().find(mastodonAccountKey, mastodonTrendCount).size) - Assert.assertEquals(1, cacheDatabase.trendDao().find(mastodonAccountKey, mastodonTrendCount)[0].history.size) - // clear history - cacheDatabase.trendHistoryDao().clearAll(mastodonAccountKey) - Assert.assertEquals(0, cacheDatabase.trendDao().find(mastodonAccountKey, mastodonTrendCount)[0].history.size) - } -} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt new file mode 100644 index 000000000..7df0794db --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt @@ -0,0 +1,121 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import androidx.paging.PagingSource +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockIListModel +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class ListsDaoImplTest : CacheDatabaseDaoTest() { + val accountKey = MicroBlogKey.twitter("test") + + private val insertData = listOf( + mockIListModel().toUi(accountKey), + mockIListModel().toUi(accountKey), + mockIListModel().toUi(accountKey), + mockIListModel().toUi(accountKey), + mockIListModel().toUi(accountKey) + ) + @Test + fun checkInsertDbLists() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + val result = roomDatabase.listsDao().findAll() + assertEquals(insertData.size, result?.size) + } + + @Test + fun findDbListWithListKey() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + val result = cacheDatabase.listsDao().findWithListKey(insertData.first().listKey, accountKey) + assertEquals(insertData.first().listKey, result?.listKey) + val errorResult = cacheDatabase.listsDao().findWithListKey(insertData.first().listKey, MicroBlogKey.Empty) + assertNull(errorResult) + } + + @Test + fun findDbListWithListKeyWithFlow_AutoUpdateAfterDbUpdate() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + val source = cacheDatabase.listsDao().findWithListKeyWithFlow(insertData.first().listKey, accountKey) + assertEquals(insertData.first().descriptions, source.firstOrNull()?.descriptions) + cacheDatabase.listsDao().update( + listOf(insertData.first().copy(descriptions = "update")) + ) + assertEquals("update", source.firstOrNull()?.descriptions) + + cacheDatabase.listsDao().delete(listOf(insertData.first())) + assertNull(source.firstOrNull()) + } + + @Test + fun clearDbList() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + cacheDatabase.listsDao().clearAll(accountKey) + assert(roomDatabase.listsDao().findAll().isNullOrEmpty()) + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + val pagingSource = cacheDatabase.listsDao().getPagingSource( + accountKey = accountKey + ) + val limit = 3 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData) + var invalidate = false + cacheDatabase.listsDao().getPagingSource( + accountKey = accountKey + ).registerInvalidatedCallback { + invalidate = true + } + cacheDatabase.listsDao().insertAll(listOf(mockIListModel().toUi(accountKey))) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt new file mode 100644 index 000000000..0a0b5b6f7 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt @@ -0,0 +1,133 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.AppDatabaseImpl +import com.twidere.twiderex.db.base.AppDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockUiSearch +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class SearchDaoImplTest : AppDatabaseDaoTest() { + private val twitterAccountKey = MicroBlogKey.twitter("123") + private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") + + @Test + fun getAll_returnResultByAccountKey(): Unit = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll( + listOf( + mockUiSearch(content = "twitter", accountKey = twitterAccountKey), + mockUiSearch(content = "mastodon", accountKey = mastodonAccountKey) + ) + ) + val twitterSearch = appDatabase.searchDao().getAll(twitterAccountKey) + twitterSearch.firstOrNull()?.forEach { + assertEquals("twitter", it.content) + } ?: assert(false) + + val mastodonSearch = appDatabase.searchDao().getAll(mastodonAccountKey) + mastodonSearch.firstOrNull()?.forEach { + assertEquals("mastodon", it.content) + } ?: assert(false) + } + + @Test + fun getAllHistory_returnResultsMatchAccountKeyAndNotSaved() = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll( + listOf( + mockUiSearch(content = "saved", accountKey = twitterAccountKey, saved = true), + mockUiSearch(content = "not saved", accountKey = twitterAccountKey) + ) + ) + val result = appDatabase.searchDao().getAllHistory(mastodonAccountKey).firstOrNull() + result?.forEach { + assert(!it.saved) + assertEquals("not saved", it.content) + } ?: assert(false) + } + + @Test + fun getAllSaved_returnResultsMatchAccountKeyAndSaved() = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll( + listOf( + mockUiSearch(content = "saved", accountKey = twitterAccountKey, saved = true), + mockUiSearch(content = "not saved", accountKey = twitterAccountKey) + ) + ) + val result = appDatabase.searchDao().getAllSaved(twitterAccountKey).firstOrNull() + result?.forEach { + assert(it.saved) + assertEquals("saved", it.content) + } ?: assert(false) + } + + @Test + fun getSearch_returnResultsMatchContentAndAccountKey() = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll(listOf(mockUiSearch(content = "twitter", accountKey = twitterAccountKey))) + val result = appDatabase.searchDao().get("twitter", twitterAccountKey) + assertEquals("twitter", result?.content) + + val errorResult = appDatabase.searchDao().get("twitter", mastodonAccountKey) + assert(errorResult == null) + } + + @Test + fun deleteSearch_getNullAfterDelete() = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll(listOf(mockUiSearch(content = "twitter", accountKey = twitterAccountKey))) + val result = appDatabase.searchDao().get("twitter", twitterAccountKey) + assertEquals("twitter", result?.content) + appDatabase.searchDao().remove(result!!) + assert(appDatabase.searchDao().get("twitter", twitterAccountKey) == null) + } + + @Test + fun clearHistory_deleteSearchNotSaved() = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + appDatabase.searchDao().insertAll( + listOf( + mockUiSearch(content = "twitter", accountKey = twitterAccountKey), + mockUiSearch(content = "twitter saved", accountKey = twitterAccountKey, saved = true), + mockUiSearch(content = "mastodon", accountKey = mastodonAccountKey), + mockUiSearch(content = "mastodon saved", accountKey = mastodonAccountKey, saved = true) + ) + ) + appDatabase.searchDao().clear() + val twitterResult = appDatabase.searchDao().getAll(twitterAccountKey).firstOrNull() + val mastodonResult = appDatabase.searchDao().getAll(mastodonAccountKey).firstOrNull() + twitterResult?.forEach { + assert(it.saved) + assertEquals("twitter saved", it.content) + } ?: assert(false) + + mastodonResult?.forEach { + assert(it.saved) + assertEquals("mastodon saved", it.content) + } ?: assert(false) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt new file mode 100644 index 000000000..301487ca4 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt @@ -0,0 +1,156 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.base + +import androidx.paging.PagingSource +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class DirectMessageEventDaoImplTest : CacheDatabaseDaoTest() { + val accountKey = MicroBlogKey.twitter("test") + @Test + fun insertAll_SaveBothMessageAndAttachmentsToDatabase() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + cacheDatabase.directMessageDao().insertAll(listOf(message)) + assert(roomDatabase.directMessageDao().getAll(accountKey).isNotEmpty()) + assertNotNull(roomDatabase.userDao().findWithUserKey(MicroBlogKey.twitter("other"))) + assert(roomDatabase.mediaDao().findMediaByBelongToKey(message.messageKey).isNotEmpty()) + assert(roomDatabase.urlEntityDao().findWithBelongToKey(message.messageKey).isNotEmpty()) + } + + @Test + fun getMessageCount_ReturnCorrectCountOfMessage() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + assertEquals( + 0, + cacheDatabase.directMessageDao().getMessageCount( + accountKey, + message.conversationKey + ) + ) + cacheDatabase.directMessageDao().insertAll(listOf(message)) + assertEquals( + 1, + cacheDatabase.directMessageDao().getMessageCount( + accountKey, + message.conversationKey + ) + ) + } + + @Test + fun findWithMessageKey() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + assertNull( + cacheDatabase.directMessageDao().findWithMessageKey( + accountKey = accountKey, + conversationKey = message.conversationKey, + messageKey = message.messageKey + ) + ) + cacheDatabase.directMessageDao().insertAll(listOf(message)) + assertEquals( + message.messageKey, + cacheDatabase.directMessageDao().findWithMessageKey( + accountKey = accountKey, + conversationKey = message.conversationKey, + messageKey = message.messageKey + )?.messageKey + ) + } + + @Test + fun delete() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + cacheDatabase.directMessageDao().insertAll(listOf(message)) + cacheDatabase.directMessageDao().delete(message) + assertNull( + cacheDatabase.directMessageDao().findWithMessageKey( + accountKey = accountKey, + conversationKey = message.conversationKey, + messageKey = message.messageKey + ) + ) + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + ) + cacheDatabase.directMessageDao().insertAll(list) + val pagingSource = cacheDatabase.directMessageDao().getPagingSource( + accountKey = accountKey, + conversationKey = list.first().conversationKey + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + var invalidate = false + cacheDatabase.directMessageDao().getPagingSource( + accountKey = accountKey, + conversationKey = message.conversationKey + ).registerInvalidatedCallback { + invalidate = true + } + cacheDatabase.directMessageDao().insertAll(listOf(message)) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt index 0e5f6af8e..d1e7ed560 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.room.db.RoomAppDatabase internal class AppDatabaseImpl(private val roomDatabase: RoomAppDatabase) : AppDatabase { private val draftDao = DraftDaoImpl(roomDatabase.draftDao()) - private val searchDao = SearchDaoImpl(roomDatabase.searchDao()) + private val searchDao = SearchDaoImpl(roomDatabase) override fun draftDao(): DraftDao { return draftDao } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt index 9fbbda229..2a392ffc8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt @@ -21,11 +21,13 @@ package com.twidere.twiderex.dataprovider.db.dao import androidx.paging.PagingSource +import androidx.room.withTransaction import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.room.db.model.DbDMEventWithAttachments.Companion.saveToDb +import com.twidere.twiderex.room.db.paging.getPagingSource import com.twidere.twiderex.room.db.transform.toDbMEventWithAttachments import com.twidere.twiderex.room.db.transform.toUi @@ -36,7 +38,11 @@ internal class DirectMessageEventDaoImpl( accountKey: MicroBlogKey, conversationKey: MicroBlogKey ): PagingSource { - TODO("Not yet implemented") + return roomCacheDatabase.directMessageDao().getPagingSource( + cacheDatabase = roomCacheDatabase, + accountKey = accountKey, + conversationKey = conversationKey + ) } override suspend fun findWithMessageKey( @@ -46,7 +52,15 @@ internal class DirectMessageEventDaoImpl( ) = roomCacheDatabase.directMessageDao().findWithMessageKey(accountKey, conversationKey, messageKey)?.toUi() override suspend fun delete(message: UiDMEvent) { - roomCacheDatabase.directMessageDao().delete(message.toDbMEventWithAttachments().message) + roomCacheDatabase.withTransaction { + roomCacheDatabase.directMessageDao().findWithMessageKey( + accountKey = message.accountKey, + conversationKey = message.conversationKey, + messageKey = message.messageKey + )?.let { + roomCacheDatabase.directMessageDao().delete(message.toDbMEventWithAttachments(dbId = it.message._id).message) + } + } } override suspend fun getMessageCount( diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt index 0f6b67e57..f1d15a285 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.dataprovider.db.dao import androidx.paging.PagingSource +import androidx.room.withTransaction import com.twidere.twiderex.db.dao.ListsDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList @@ -50,11 +51,27 @@ internal class ListsDaoImpl(private val database: RoomCacheDatabase) : ListsDao ) = database.listsDao().findWithListKey(listKey, accountKey)?.toUi() override suspend fun update(listOf: List) { - database.listsDao().update(listOf.map { it.toDbList() }) + database.withTransaction { + listOf.mapNotNull { + database.listsDao().findWithListKey(it.listKey, it.accountKey)?.let { dbList -> + it.toDbList(dbId = dbList._id) + } + }.let { + database.listsDao().update(it) + } + } } override suspend fun delete(listOf: List) { - database.listsDao().delete(listOf.map { it.toDbList() }) + database.withTransaction { + listOf.mapNotNull { + database.listsDao().findWithListKey(it.listKey, it.accountKey)?.let { dbList -> + it.toDbList(dbId = dbList._id) + } + }.let { + database.listsDao().delete(it) + } + } } override suspend fun clearAll(accountKey: MicroBlogKey) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt index e3ab1c9b8..f5b6761c7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt @@ -20,26 +20,34 @@ */ package com.twidere.twiderex.dataprovider.db.dao +import androidx.room.withTransaction import com.twidere.twiderex.db.dao.SearchDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch -import com.twidere.twiderex.room.db.dao.RoomSearchDao +import com.twidere.twiderex.room.db.RoomAppDatabase import com.twidere.twiderex.room.db.transform.toDbSearch import com.twidere.twiderex.room.db.transform.toUiSearch import kotlinx.coroutines.flow.map -internal class SearchDaoImpl(private val roomSearchDao: RoomSearchDao) : SearchDao { - override suspend fun insertAll(search: List) = roomSearchDao.insertAll(search.map { it.toDbSearch() }) +internal class SearchDaoImpl(private val roomAppDatabase: RoomAppDatabase) : SearchDao { + override suspend fun insertAll(search: List) = roomAppDatabase.searchDao().insertAll(search.map { it.toDbSearch() }) - override fun getAll(accountKey: MicroBlogKey) = roomSearchDao.getAll(accountKey).map { list -> list.map { it.toUiSearch() } } + override fun getAll(accountKey: MicroBlogKey) = roomAppDatabase.searchDao().getAll(accountKey).map { list -> list.map { it.toUiSearch() } } - override fun getAllHistory(accountKey: MicroBlogKey) = roomSearchDao.getAllHistory(accountKey).map { list -> list.map { it.toUiSearch() } } + override fun getAllHistory(accountKey: MicroBlogKey) = roomAppDatabase.searchDao().getAllHistory(accountKey).map { list -> list.map { it.toUiSearch() } } - override fun getAllSaved(accountKey: MicroBlogKey) = roomSearchDao.getAllSaved(accountKey).map { list -> list.map { it.toUiSearch() } } + override fun getAllSaved(accountKey: MicroBlogKey) = roomAppDatabase.searchDao().getAllSaved(accountKey).map { list -> list.map { it.toUiSearch() } } - override suspend fun get(content: String, accountKey: MicroBlogKey) = roomSearchDao.get(content, accountKey)?.toUiSearch() + override suspend fun get(content: String, accountKey: MicroBlogKey) = roomAppDatabase.searchDao().get(content, accountKey)?.toUiSearch() - override suspend fun remove(search: UiSearch) = roomSearchDao.remove(search.toDbSearch()) + override suspend fun remove(search: UiSearch) { + roomAppDatabase.withTransaction { + roomAppDatabase.searchDao().get(content = search.content, accountKey = search.accountKey) + ?.let { + roomAppDatabase.searchDao().remove(search.toDbSearch(id = it._id)) + } + } + } - override suspend fun clear() = roomSearchDao.clear() + override suspend fun clear() = roomAppDatabase.searchDao().clear() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt index fd816803f..4033a10ce 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt @@ -23,10 +23,15 @@ package com.twidere.twiderex.room.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.room.db.model.DbUrlEntity @Dao internal interface RoomUrlEntityDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(media: List) + + @Query("SELECT * FROM url_entity WHERE statusKey == :belongToKey") + suspend fun findWithBelongToKey(belongToKey: MicroBlogKey): List } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt index a6c55a9d0..f9364e937 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt @@ -70,7 +70,7 @@ internal fun DbDMEventWithAttachments.toUi() = UiDMEvent( sender = sender.toUi() ) -internal fun UiDMConversation.toDbDMConversation() = DbDMConversation( +internal fun UiDMConversation.toDbDMConversation(dbId: String = UUID.randomUUID().toString()) = DbDMConversation( accountKey = accountKey, conversationId = conversationId, conversationKey = conversationKey, @@ -82,12 +82,17 @@ internal fun UiDMConversation.toDbDMConversation() = DbDMConversation( UiDMConversation.Type.GROUP -> DbDMConversation.Type.GROUP }, recipientKey = recipientKey, - _id = UUID.randomUUID().toString() + _id = dbId ) -internal fun UiDMEvent.toDbMEventWithAttachments() = DbDMEventWithAttachments( +internal fun UiDMEvent.toDbMEventWithAttachments( + dbId: String = UUID.randomUUID().toString(), + dbMediaId: String = UUID.randomUUID().toString(), + dbUrlId: String = UUID.randomUUID().toString(), + dbSenderId: String = UUID.randomUUID().toString(), +) = DbDMEventWithAttachments( message = DbDMEvent( - _id = UUID.randomUUID().toString(), + _id = dbId, accountKey = accountKey, sortId = sortId, conversationKey = conversationKey, @@ -105,7 +110,7 @@ internal fun UiDMEvent.toDbMEventWithAttachments() = DbDMEventWithAttachments( UiDMEvent.SendStatus.FAILED -> DbDMEvent.SendStatus.FAILED }, ), - media = media.toDbMedia(), - urlEntity = urlEntity.toDbUrl(messageKey), - sender = sender.toDbUser() + media = media.toDbMedia(dbId = dbMediaId), + urlEntity = urlEntity.toDbUrl(belongToKey = messageKey, dbId = dbUrlId), + sender = sender.toDbUser(dbId = dbSenderId) ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt index fa7413694..d03045a64 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt @@ -38,7 +38,7 @@ internal fun DbList.toUi() = allowToSubscribe = allowToSubscribe, ) -internal fun UiList.toDbList() = +internal fun UiList.toDbList(dbId: String = UUID.randomUUID().toString()) = DbList( listId = id, ownerId = ownerId, @@ -50,5 +50,5 @@ internal fun UiList.toDbList() = replyPolicy = replyPolicy, isFollowed = isFollowed, allowToSubscribe = allowToSubscribe, - _id = UUID.randomUUID().toString() + _id = dbId ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt index 786c5ae2a..cee282ca8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt @@ -39,7 +39,7 @@ internal fun List.toUi() = sortedBy { it.order }.map { ) } -internal fun List.toDbMedia() = map { +internal fun List.toDbMedia(dbId: String = UUID.randomUUID().toString()) = map { DbMedia( url = it.url, belongToKey = it.belongToKey, @@ -51,6 +51,6 @@ internal fun List.toDbMedia() = map { pageUrl = it.pageUrl, altText = it.altText, order = it.order, - _id = UUID.randomUUID().toString() + _id = dbId ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt index c6da15c56..6d15fa632 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt @@ -31,8 +31,8 @@ internal fun DbSearch.toUiSearch() = UiSearch( accountKey = accountKey ) -internal fun UiSearch.toDbSearch() = DbSearch( - _id = UUID.randomUUID().toString(), +internal fun UiSearch.toDbSearch(id: String = UUID.randomUUID().toString()) = DbSearch( + _id = id, content = content, lastActive = lastActive, saved = saved, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt index 9c0ec2c68..ff802fb7b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt @@ -35,10 +35,10 @@ internal fun DbUrlEntity.toUi() = UiUrlEntity( ) internal fun List.toUi() = map { it.toUi() } -internal fun List.toDbUrl(belongToKey: MicroBlogKey) = map { +internal fun List.toDbUrl(belongToKey: MicroBlogKey, dbId: String = UUID.randomUUID().toString()) = map { DbUrlEntity( url = it.url, - _id = UUID.randomUUID().toString(), + _id = dbId, statusKey = belongToKey, expandedUrl = it.expandedUrl, displayUrl = it.displayUrl, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt index ce5e79523..c7ffe17dc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt @@ -85,9 +85,9 @@ internal fun DbUser.toUi() = acct = acct, ) -internal fun UiUser.toDbUser() = +internal fun UiUser.toDbUser(dbId: String = UUID.randomUUID().toString()) = DbUser( - _id = UUID.randomUUID().toString(), + _id = dbId, name = name, screenName = screenName, profileImage = profileImage.toString(), diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index e9dcbeef0..cb71fbd80 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -32,12 +32,16 @@ import com.twidere.services.microblog.model.INotification import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser +import com.twidere.services.twitter.model.Attachment import com.twidere.services.twitter.model.AttachmentsV2 import com.twidere.services.twitter.model.DirectMessageEvent +import com.twidere.services.twitter.model.Entities +import com.twidere.services.twitter.model.EntitiesURL import com.twidere.services.twitter.model.MediaV2 import com.twidere.services.twitter.model.MessageCreate import com.twidere.services.twitter.model.MessageData import com.twidere.services.twitter.model.MessageTarget +import com.twidere.services.twitter.model.PurpleMedia import com.twidere.services.twitter.model.StatusV2 import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging @@ -125,7 +129,7 @@ fun mockIListModel( mode: String? = null, description: String? = "", ): IListModel { - val id = System.currentTimeMillis() + val id = UUID.randomUUID().hashCode().toLong() return TwitterList( id = id, idStr = id.toString(), @@ -183,7 +187,22 @@ fun mockIDirectMessage(id: String = UUID.randomUUID().toString(), accountId: Str id = id, type = "message_create", messageCreate = MessageCreate( - messageData = MessageData(text = "mock message"), + messageData = MessageData( + text = "mock message", + entities = Entities( + urls = listOf( + EntitiesURL( + display_url = "http://test.com", + url = "http://test.com", + expanded_url = "http://test.com", + ) + ) + ), + attachment = Attachment( + type = "media", + media = PurpleMedia() + ) + ), senderId = if (inCome) otherUserID else accountId, target = MessageTarget( recipientId = if (inCome) accountId else otherUserID From 67fb0b4f27f0770781eed241cc400cc55e4f6c0a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 16:39:30 +0800 Subject: [PATCH 075/615] add test for conversationDaoImpl --- .../com/twidere/twiderex/ExampleUnitTest.kt | 37 --- .../twiderex/viewmodel/ViewModelTestBase.kt | 51 ---- .../lists/ListsCreateViewModelTest.kt | 133 ---------- .../lists/ListsModifyViewModelTest.kt | 228 ------------------ .../viewmodel/lists/ListsViewModelTest.kt | 137 ----------- .../DirectMessageConversationDaoImplTest.kt | 126 ++++++++++ .../DirectMessageEventDaoImplTest.kt | 3 +- .../dao/DirectMessageConversationDaoImpl.kt | 10 +- 8 files changed, 137 insertions(+), 588 deletions(-) delete mode 100644 android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt delete mode 100644 android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt rename common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/{base => }/DirectMessageEventDaoImplTest.kt (98%) diff --git a/android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt b/android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt deleted file mode 100644 index 62f49f620..000000000 --- a/android/src/test/java/com/twidere/twiderex/ExampleUnitTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex - -import org.junit.Assert -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ - -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - Assert.assertEquals(4, 2 + 2L) - } -} diff --git a/android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt deleted file mode 100644 index 63d16178b..000000000 --- a/android/src/test/java/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.viewmodel - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.ObsoleteCoroutinesApi -import kotlinx.coroutines.newSingleThreadContext -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Before -import org.mockito.MockitoAnnotations - -@OptIn(ExperimentalCoroutinesApi::class) -open class ViewModelTestBase { - private lateinit var mocks: AutoCloseable - @OptIn(ObsoleteCoroutinesApi::class) - private val mainThreadSurrogate = newSingleThreadContext("UI thread") - - @Before - open fun setUp() { - Dispatchers.setMain(mainThreadSurrogate) - mocks = MockitoAnnotations.openMocks(this) - } - - @After - open fun tearDown() { - Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher - mainThreadSurrogate.close() - mocks.close() - } -} diff --git a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt deleted file mode 100644 index 799c3f102..000000000 --- a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.viewmodel.lists - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.Observer -import com.twidere.twiderex.mock.MockCenter -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.notification.NotificationEvent -import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.mockito.Mock -import org.mockito.kotlin.any -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever - -class ListsCreateViewModelTest : ViewModelTestBase() { - @get:Rule - val rule = InstantTaskExecutorRule() - - private lateinit var mockRepository: ListsRepository - - @Mock - private lateinit var mockAppNotification: InAppNotification - - @Mock - private lateinit var mockAccount: AccountDetails - - @Mock - private lateinit var mockSuccessObserver: Observer - - @Mock - private lateinit var mockLoadingObserver: Observer - - private var errorNotification: NotificationEvent? = null - - private lateinit var createViewModel: ListsCreateViewModel - - private val scope = CoroutineScope(Dispatchers.Main) - - override fun setUp() { - super.setUp() - mockRepository = ListsRepository(MockCenter.mockCacheDatabase()) - createViewModel = ListsCreateViewModel( - mockAppNotification, - mockRepository, - mockAccount - ) { success, _ -> - mockSuccessObserver.onChanged(success) - } - whenever(mockAppNotification.show(any())).then { - errorNotification = it.getArgument(0) as NotificationEvent - Unit - } - whenever(mockAccount.service).thenReturn(MockCenter.mockListsService()) - whenever(mockAccount.accountKey).thenReturn(MicroBlogKey.twitter("123")) - errorNotification = null - mockSuccessObserver.onChanged(false) - scope.launch { - createViewModel.loading.collect { - mockLoadingObserver.onChanged(it) - } - } - } - - @Test - fun createList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - async { - createViewModel.createList(title = "title", private = false) - }.await() - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) - } - - @Test - fun createList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - Assert.assertNull(errorNotification) - async { - createViewModel.createList(title = "error", private = false) - }.await() - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) - Assert.assertNotNull(errorNotification) - } - - private fun verifySuccessAndLoadingBefore( - loadingObserver: Observer, - successObserver: Observer - ) { - verify(loadingObserver, times(1)).onChanged(false) - verify(successObserver).onChanged(false) - } - - private fun verifySuccessAndLoadingAfter( - loadingObserver: Observer, - successObserver: Observer, - success: Boolean - ) { - verify(loadingObserver, times(1)).onChanged(true) - verify(loadingObserver, times(1)).onChanged(false) - verify(successObserver, if (success) times(1) else times(2)).onChanged(success) - } -} diff --git a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt deleted file mode 100644 index 8e3a20e25..000000000 --- a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.viewmodel.lists - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.Observer -import com.twidere.twiderex.mock.MockCenter -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.notification.NotificationEvent -import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.junit.Assert -import org.junit.Rule -import org.junit.Test -import org.mockito.Mock -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine - -class ListsModifyViewModelTest : ViewModelTestBase() { - @get:Rule - val rule = InstantTaskExecutorRule() - - private var mockRepository: ListsRepository = ListsRepository(MockCenter.mockCacheDatabase()) - - @Mock - private lateinit var mockAppNotification: InAppNotification - - private val mockAccount: AccountDetails = mock { - on { service }.doReturn(MockCenter.mockListsService()) - on { accountKey }.doReturn(MicroBlogKey.twitter("123")) - } - - @Mock - private lateinit var mockSuccessObserver: Observer - - @Mock - private lateinit var mockLoadingObserver: Observer - - private var errorNotification: NotificationEvent? = null - - private lateinit var modifyViewModel: ListsModifyViewModel - - private val scope = CoroutineScope(Dispatchers.Main) - - @Test - fun updateList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - suspendCoroutine { - modifyViewModel.editList( - listId = "123", - title = "title", - private = false - ) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) - } - - @Test - fun updateList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - Assert.assertNull(errorNotification) - suspendCoroutine { - modifyViewModel.editList( - listId = "error", - title = "name", - private = false - ) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) - Assert.assertNotNull(errorNotification) - } - - @Test - fun deleteList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - suspendCoroutine { - modifyViewModel.deleteList(listId = "123", MicroBlogKey.Empty) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) - } - - @Test - fun deleteList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - Assert.assertNull(errorNotification) - suspendCoroutine { - modifyViewModel.deleteList(listId = "error", MicroBlogKey.Empty) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) - Assert.assertNotNull(errorNotification) - } - - @Test - fun subscribeList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - suspendCoroutine { - modifyViewModel.subscribeList(MicroBlogKey.twitter("123")) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) - } - - @Test - fun subscribeList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - Assert.assertNull(errorNotification) - suspendCoroutine { - modifyViewModel.subscribeList(MicroBlogKey.twitter("error")) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) - Assert.assertNotNull(errorNotification) - } - - @Test - fun unsubscribeList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - suspendCoroutine { - modifyViewModel.unsubscribeList(MicroBlogKey.twitter("123")) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) - } - - @Test - fun unsubscribeList_failedExpectFalseAndShowNotification(): Unit = - runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - Assert.assertNull(errorNotification) - suspendCoroutine { - modifyViewModel.unsubscribeList(MicroBlogKey.twitter("error")) { success, _ -> - mockSuccessObserver.onChanged(success) - it.resume(success) - } - } - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) - Assert.assertNotNull(errorNotification) - } - - override fun setUp() { - super.setUp() - mockRepository = ListsRepository(MockCenter.mockCacheDatabase()) - modifyViewModel = ListsModifyViewModel( - mockRepository, - mockAppNotification, - mockAccount, - listKey = MicroBlogKey.Empty, - ) - whenever(mockAppNotification.show(any())).then { - errorNotification = it.getArgument(0) as NotificationEvent - Unit - } - errorNotification = null - mockSuccessObserver.onChanged(false) - scope.launch { - modifyViewModel.loading.collect { - mockLoadingObserver.onChanged(it) - } - } - } - - private fun verifySuccessAndLoadingBefore( - loadingObserver: Observer, - successObserver: Observer - ) { - verify(loadingObserver, times(1)).onChanged(false) - verify(successObserver).onChanged(false) - } - - private fun verifySuccessAndLoadingAfter( - loadingObserver: Observer, - successObserver: Observer, - success: Boolean - ) { - verify(loadingObserver, times(1)).onChanged(true) - verify(loadingObserver, times(1)).onChanged(false) - verify(successObserver, if (success) times(1) else times(2)).onChanged(success) - } -} diff --git a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt deleted file mode 100644 index f212594e1..000000000 --- a/android/src/test/java/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.viewmodel.lists - -import androidx.paging.CombinedLoadStates -import androidx.paging.DifferCallback -import androidx.paging.NullPaddedList -import androidx.paging.PagingData -import androidx.paging.PagingDataDiffer -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.AmUser -import com.twidere.twiderex.model.ui.UiList -import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.TestCoroutineDispatcher -import org.junit.Assert -import org.junit.Test -import org.mockito.Mock -import org.mockito.kotlin.any -import org.mockito.kotlin.whenever - -@OptIn(ExperimentalCoroutinesApi::class) -class ListsViewModelTest : ViewModelTestBase() { - - @Mock - private lateinit var mockRepository: ListsRepository - - @Mock - private lateinit var mockAccount: AccountDetails - - @Mock - private lateinit var mockUser: AmUser - - @Mock - private lateinit var ownerList: UiList - - @Mock - private lateinit var subscribeList: UiList - - private lateinit var viewModel: ListsViewModel - - override fun setUp() { - super.setUp() - whenever(mockRepository.fetchLists(any())).thenReturn( - flow { - emit(PagingData.from(listOf(ownerList, subscribeList))) - } - ) - - whenever(ownerList.isOwner(any())).thenReturn(true) - whenever(subscribeList.isOwner(any())).thenReturn(false) - whenever(ownerList.title).thenReturn("owner") - whenever(subscribeList.title).thenReturn("subscribe") - whenever(subscribeList.isFollowed).thenReturn(true) - whenever(mockAccount.user).thenReturn(mockUser) - whenever(mockUser.userId).thenReturn("123") - viewModel = ListsViewModel(mockRepository, mockAccount) - } - - @Test - fun source_containsAllLists(): Unit = runBlocking(Dispatchers.Main) { - // check the source - viewModel.source.first().let { - val sourceItems = it.collectDataForTest() - Assert.assertEquals(2, sourceItems.size) - } - } - - @Test - fun ownerSource_containsOwnedLists(): Unit = runBlocking(Dispatchers.Main) { - // make sure ownerSource only emit data which isOwner() returns true - viewModel.ownerSource.first().let { - val ownerItems = it.collectDataForTest() - Assert.assertEquals(1, ownerItems.size) - Assert.assertEquals("owner", ownerItems[0].title) - } - } - - @Test - fun subscribeSource_containsSubscribedLists(): Unit = runBlocking(Dispatchers.Main) { - // make sure ownerSource only emit data which isOwner() returns true - viewModel.subscribedSource.first().let { - val subscribeItems = it.collectDataForTest() - Assert.assertEquals(1, subscribeItems.size) - Assert.assertEquals("subscribe", subscribeItems[0].title) - } - } -} - -@OptIn(ExperimentalCoroutinesApi::class) -private suspend fun PagingData.collectDataForTest(): List { - val dcb = object : DifferCallback { - override fun onChanged(position: Int, count: Int) {} - override fun onInserted(position: Int, count: Int) {} - override fun onRemoved(position: Int, count: Int) {} - } - val items = mutableListOf() - val dif = object : PagingDataDiffer(dcb, TestCoroutineDispatcher()) { - override suspend fun presentNewList( - previousList: NullPaddedList, - newList: NullPaddedList, - newCombinedLoadStates: CombinedLoadStates, - lastAccessedIndex: Int, - onListPresentable: () -> Unit - ): Int? { - for (idx in 0 until newList.size) - items.add(newList.getFromStorage(idx)) - onListPresentable() - return null - } - } - dif.collectFrom(this) - return items -} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt new file mode 100644 index 000000000..09dc3fcd3 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt @@ -0,0 +1,126 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import androidx.paging.PagingSource +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMEvent +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class DirectMessageConversationDaoImplTest : CacheDatabaseDaoTest() { + val accountKey = MicroBlogKey.twitter("test") + + private fun UiDMEvent.toConversation() = UiDMConversation( + accountKey = accountKey, + conversationId = conversationKey.id, + conversationKey = conversationKey, + conversationAvatar = sender.profileImage.toString(), + conversationName = sender.name, + conversationSubName = sender.screenName, + conversationType = UiDMConversation.Type.ONE_TO_ONE, + recipientKey = conversationUserKey + ) + + @Test + fun delete() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val conversation = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)).toConversation() + cacheDatabase.directMessageConversationDao().insertAll(listOf(conversation)) + cacheDatabase.directMessageConversationDao().delete(conversation) + assertNull( + cacheDatabase.directMessageConversationDao().findWithConversationKey( + accountKey = accountKey, + conversationKey = conversation.conversationKey, + ) + ) + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other2") + .toUi(accountKey, mockIUser(id = "other2").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other3") + .toUi(accountKey, mockIUser(id = "other3").toUi(accountKey)), + ) + cacheDatabase.directMessageDao().insertAll(list) + cacheDatabase.directMessageConversationDao().insertAll(list.map { it.toConversation() }) + val pagingSource = cacheDatabase.directMessageConversationDao().getPagingSource( + accountKey = accountKey, + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val conversation = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)).toConversation() + var invalidate = false + cacheDatabase.directMessageConversationDao().getPagingSource( + accountKey = accountKey, + ).registerInvalidatedCallback { + invalidate = true + } + cacheDatabase.directMessageConversationDao().insertAll(listOf(conversation)) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun findWithConversationKeyFlow() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val conversation = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)).toConversation() + val conversationFlow = cacheDatabase.directMessageConversationDao().findWithConversationKeyFlow( + accountKey = accountKey, + conversationKey = conversation.conversationKey + ) + assertNull(conversationFlow.firstOrNull()) + cacheDatabase.directMessageConversationDao().insertAll(listOf(conversation)) + assertEquals(conversation.conversationKey, conversationFlow.firstOrNull()?.conversationKey) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt similarity index 98% rename from common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt rename to common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt index 301487ca4..6a87dcda5 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/DirectMessageEventDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt @@ -18,11 +18,12 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.base +package com.twidere.twiderex.db import androidx.paging.PagingSource import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest import com.twidere.twiderex.mock.model.mockIDirectMessage import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt index 231a5656d..f6339ed2f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.dataprovider.db.dao import androidx.paging.PagingSource +import androidx.room.withTransaction import com.twidere.twiderex.db.dao.DirectMessageConversationDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMConversation @@ -55,6 +56,13 @@ internal class DirectMessageConversationDaoImpl(private val database: RoomCacheD ) = database.directMessageConversationDao().find(accountKey).map { it.toUi() } override suspend fun delete(conversation: UiDMConversation) { - database.directMessageConversationDao().delete(conversation.toDbDMConversation()) + database.withTransaction { + database.directMessageConversationDao().findWithConversationKey( + accountKey = conversation.accountKey, + conversationKey = conversation.conversationKey + )?.let { + database.directMessageConversationDao().delete(it) + } + } } } From 6d7aeb4d831155b38e44f7698372976813a3b57f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 24 Aug 2021 16:55:27 +0800 Subject: [PATCH 076/615] migrate view model to precompose --- .../com/twidere/twiderex/TwidereXActivity.kt | 4 ++-- .../twiderex/di/assisted/AssistedViewModel.kt | 12 ++++------ .../twiderex/extensions/ComposeExtensions.kt | 14 ++++------- .../viewmodel/ActiveAccountViewModel.kt | 7 ++---- .../twiderex/viewmodel/DraftViewModel.kt | 2 +- .../twiderex/viewmodel/MediaViewModel.kt | 4 ++-- .../twiderex/viewmodel/PureMediaViewModel.kt | 2 +- .../twiderex/viewmodel/StatusViewModel.kt | 4 ++-- .../compose/ComposeSearchUserViewModel.kt | 4 ++-- .../viewmodel/compose/ComposeViewModel.kt | 4 ++-- .../MastodonComposeSearchHashtagViewModel.kt | 4 ++-- .../viewmodel/dm/DMConversationViewModel.kt | 2 +- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 4 ++-- .../dm/DMNewConversationViewModel.kt | 4 ++-- .../lists/ListsSearchUserViewModel.kt | 4 ++-- .../viewmodel/lists/ListsTimelineViewModel.kt | 4 ++-- .../viewmodel/lists/ListsUserViewModel.kt | 4 ++-- .../viewmodel/lists/ListsViewModel.kt | 4 ++-- .../mastodon/MastodonHashtagViewModel.kt | 4 ++-- .../MastodonSearchHashtagViewModel.kt | 4 ++-- .../mastodon/MastodonSignInViewModel.kt | 4 ++-- .../viewmodel/search/SearchInputViewModel.kt | 4 ++-- .../viewmodel/search/SearchSaveViewModel.kt | 4 ++-- .../viewmodel/search/SearchTweetsViewModel.kt | 4 ++-- .../viewmodel/search/SearchUserViewModel.kt | 4 ++-- .../settings/AccountNotificationViewModel.kt | 4 ++-- .../viewmodel/settings/AppearanceViewModel.kt | 4 ++-- .../viewmodel/settings/DisplayViewModel.kt | 4 ++-- .../viewmodel/settings/LayoutViewModel.kt | 4 ++-- .../viewmodel/settings/MiscViewModel.kt | 4 ++-- .../settings/NotificationViewModel.kt | 4 ++-- .../viewmodel/settings/StorageViewModel.kt | 4 ++-- .../viewmodel/timeline/TimelineViewModel.kt | 4 ++-- .../viewmodel/trend/TrendViewModel.kt | 2 +- .../twitter/TwitterSignInViewModel.kt | 4 ++-- .../search/TwitterSearchMediaViewModel.kt | 4 ++-- .../twitter/user/TwitterUserViewModel.kt | 2 +- .../viewmodel/user/FollowersViewModel.kt | 2 +- .../viewmodel/user/FollowingViewModel.kt | 2 +- .../user/UserFavouriteTimelineViewModel.kt | 4 ++-- .../viewmodel/user/UserListViewModel.kt | 2 +- .../user/UserMediaTimelineViewModel.kt | 4 ++-- .../viewmodel/user/UserTimelineViewModel.kt | 4 ++-- .../twiderex/viewmodel/user/UserViewModel.kt | 4 ++-- .../precompose/viewmodel/ViewModelProvider.kt | 8 +++---- .../precompose/viewmodel/compose/ViewModel.kt | 23 +++++++++++++++++++ 46 files changed, 112 insertions(+), 100 deletions(-) create mode 100644 common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index eb407eca7..1b805e0b7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -59,7 +59,6 @@ import androidx.core.net.ConnectivityManagerCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.insets.ProvideWindowInsets import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.action.StatusActions @@ -85,6 +84,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.MutableStateFlow import moe.tlaster.precompose.navigation.NavController import org.koin.android.ext.android.inject +import org.koin.androidx.viewmodel.ext.android.getViewModel import javax.inject.Inject @AndroidEntryPoint @@ -181,7 +181,7 @@ class TwidereXActivity : ComponentActivity() { private fun App() { val windowInsetsControllerCompat = remember { WindowInsetsControllerCompat(window, window.decorView) } - val accountViewModel = viewModel() + val accountViewModel = com.twidere.twiderex.di.ext.getViewModel() val account by accountViewModel.account.observeAsState(null) val isActiveNetworkMetered by isActiveNetworkMetered.observeAsState(initial = false) CompositionLocalProvider( diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt index 85154c306..f9463ecb1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt @@ -23,9 +23,8 @@ package com.twidere.twiderex.di.assisted import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.staticCompositionLocalOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.compose.viewModel +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.compose.viewModel @Composable inline fun assistedViewModel( @@ -40,11 +39,8 @@ inline fun assistedViewModel( } else { null }, - factory = object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - @Suppress("UNCHECKED_CAST") - return factory?.let { creator?.invoke(it) } as T - } + creator = { + factory?.let { creator?.invoke(it) } as VM } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt index 48495158c..26f878069 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt @@ -22,16 +22,15 @@ package com.twidere.twiderex.extensions import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.compose.viewModel import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.compose.viewModel @Composable inline fun viewModel( vararg dependsOn: Any, - noinline creator: (() -> VM)? = null, + noinline creator: () -> VM, ): VM { return viewModel( key = if (dependsOn.any()) { @@ -39,11 +38,8 @@ inline fun viewModel( } else { null }, - factory = object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - @Suppress("UNCHECKED_CAST") - return creator?.invoke() as T - } + creator = { + creator.invoke() } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt index f948faa52..a13576cc5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt @@ -20,15 +20,12 @@ */ package com.twidere.twiderex.viewmodel -import androidx.lifecycle.ViewModel import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.repository.AccountRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject +import moe.tlaster.precompose.viewmodel.ViewModel -@HiltViewModel -class ActiveAccountViewModel @Inject constructor( +class ActiveAccountViewModel( private val repository: AccountRepository, ) : ViewModel() { fun setActiveAccount(it: AccountDetails) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index 03da25cae..bb7432f78 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.viewmodel import androidx.core.app.NotificationManagerCompat -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import androidx.work.WorkManager import com.twidere.twiderex.db.model.DbDraft import com.twidere.twiderex.repository.DraftRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 1be35a12f..d71b22cd7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.viewmodel import android.content.Context import android.net.Uri -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt index a326512c1..de8e036ad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.MediaRepository import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index fa6dd83db..e55d5c198 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 530c6879d..136b40453 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.compose -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -33,6 +31,8 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ComposeSearchUserViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 19f593f9c..ae1e7a4a6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -32,8 +32,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 8616299fd..bad2aec1a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.compose -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 2304dbefe..5a0054610 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.dm -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.model.AccountDetails diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 4da2aa555..b7058140c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.dm import android.net.Uri -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index fcd430593..090fbd8e6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.dm -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 3e09eed34..88d752ad4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index deb79f2e8..a08183e50 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5a8be9139..0be85271e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 87355f54b..cabe20335 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.twiderex.model.AccountDetails diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 74f06191c..4564e21cf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TimelineRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 0a95d418d..7b50d081a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index f087a6657..83d89ef0b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.viewmodel.mastodon import android.accounts.Account import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.mastodon.MastodonOAuthService import com.twidere.twiderex.db.mapper.toDbUser import com.twidere.twiderex.http.TwidereServiceFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 6c63fc312..8ff655e50 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.db.model.DbSearch import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.SearchRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 7ee9cf9f7..be04e5450 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.SearchRepository import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index d21019bfb..be83287b9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index 12b91adf2..647c40461 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 8a74cbbc4..1370b6468 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.settings -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt index d378b435d..40bddf777 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.AppearancePreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index c0d0b3cdf..b15b71a30 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.DisplayPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index c97a52210..d3bd96cd0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.settings -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.scenes.home.HomeMenus import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index 14aafd602..e11be594f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.R import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt index 7ec38097b..f9c9c95bd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.NotificationPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt index 243398233..2ba96fa21 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.settings -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.repository.CacheRepository import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 5758d23ab..d03e1e897 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import androidx.core.content.edit -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.toUi diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index 76238a0d8..fa3cf1c8f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.trend -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TrendRepository import dagger.assisted.Assisted diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 9b061b024..40983f04a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.twitter import android.accounts.Account -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.http.MicroBlogException import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 31913339d..8416dae0f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.twitter.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 9cea5da60..42e019bb4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.twitter.user -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.services.microblog.LookupService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.notification.InAppNotification diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 7b40a3bc2..8ff3e21a9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 1ba3219c3..52ba1a479 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 3e506d7c4..3fdeb622e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt index 54414f621..33d4d8003 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModel import androidx.paging.PagingData import com.twidere.twiderex.model.ui.UiUser import kotlinx.coroutines.flow.Flow diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 38f68d0bb..a7984395c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 0795012ad..dc4bd0367 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserTimelineViewModel @AssistedInject constructor( private val repository: TimelineRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 5ef62593b..1cf9287f0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt index 8562bd852..fb8503b80 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt @@ -20,16 +20,16 @@ */ package moe.tlaster.precompose.viewmodel -internal inline fun ViewModelStore.getViewModel( - creator: () -> T, +inline fun ViewModelStore.getViewModel( + noinline creator: () -> T, ): T { val key = T::class.qualifiedName.toString() return getViewModel(key, creator) } -internal inline fun ViewModelStore.getViewModel( +inline fun ViewModelStore.getViewModel( key: String, - creator: () -> T, + noinline creator: () -> T, ): T { val existing = get(key) if (existing != null && existing is T) { diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt new file mode 100644 index 000000000..ca7cb9dd4 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt @@ -0,0 +1,23 @@ +package moe.tlaster.precompose.viewmodel.compose + +import androidx.compose.runtime.Composable +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.getViewModel + +@Composable +inline fun viewModel( + viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) { + "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + }, + key: String? = null, + noinline creator: () -> VM, +): VM = viewModelStoreOwner.viewModelStore.let { + if (key == null) { + it.getViewModel(creator) + } else { + it.getViewModel(key, creator) + } +} + From 2d630bb77ee554a9351c75d62b70ee1eed32b3da Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 24 Aug 2021 17:06:25 +0800 Subject: [PATCH 077/615] spotless fix --- .../twiderex/viewmodel/DraftViewModel.kt | 2 +- .../twiderex/viewmodel/MediaViewModel.kt | 4 ++-- .../twiderex/viewmodel/PureMediaViewModel.kt | 2 +- .../twiderex/viewmodel/StatusViewModel.kt | 4 ++-- .../viewmodel/compose/ComposeViewModel.kt | 4 ++-- .../MastodonComposeSearchHashtagViewModel.kt | 4 ++-- .../viewmodel/dm/DMConversationViewModel.kt | 2 +- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 4 ++-- .../dm/DMNewConversationViewModel.kt | 4 ++-- .../lists/ListsSearchUserViewModel.kt | 4 ++-- .../viewmodel/lists/ListsTimelineViewModel.kt | 4 ++-- .../viewmodel/lists/ListsUserViewModel.kt | 4 ++-- .../viewmodel/lists/ListsViewModel.kt | 4 ++-- .../mastodon/MastodonHashtagViewModel.kt | 4 ++-- .../MastodonSearchHashtagViewModel.kt | 4 ++-- .../mastodon/MastodonSignInViewModel.kt | 4 ++-- .../viewmodel/search/SearchInputViewModel.kt | 4 ++-- .../viewmodel/search/SearchSaveViewModel.kt | 4 ++-- .../viewmodel/search/SearchTweetsViewModel.kt | 4 ++-- .../viewmodel/search/SearchUserViewModel.kt | 4 ++-- .../settings/AccountNotificationViewModel.kt | 4 ++-- .../viewmodel/settings/AppearanceViewModel.kt | 4 ++-- .../viewmodel/settings/DisplayViewModel.kt | 4 ++-- .../viewmodel/settings/LayoutViewModel.kt | 4 ++-- .../viewmodel/settings/MiscViewModel.kt | 4 ++-- .../settings/NotificationViewModel.kt | 4 ++-- .../viewmodel/settings/StorageViewModel.kt | 4 ++-- .../viewmodel/timeline/TimelineViewModel.kt | 4 ++-- .../viewmodel/trend/TrendViewModel.kt | 2 +- .../twitter/TwitterSignInViewModel.kt | 4 ++-- .../search/TwitterSearchMediaViewModel.kt | 4 ++-- .../twitter/user/TwitterUserViewModel.kt | 2 +- .../viewmodel/user/FollowersViewModel.kt | 2 +- .../viewmodel/user/FollowingViewModel.kt | 2 +- .../user/UserFavouriteTimelineViewModel.kt | 4 ++-- .../viewmodel/user/UserListViewModel.kt | 2 +- .../user/UserMediaTimelineViewModel.kt | 4 ++-- .../twiderex/viewmodel/user/UserViewModel.kt | 4 ++-- .../precompose/viewmodel/compose/ViewModel.kt | 21 ++++++++++++++++++- 39 files changed, 88 insertions(+), 69 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index bb7432f78..b3dd28e9f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -21,12 +21,12 @@ package com.twidere.twiderex.viewmodel import androidx.core.app.NotificationManagerCompat -import moe.tlaster.precompose.viewmodel.ViewModel import androidx.work.WorkManager import com.twidere.twiderex.db.model.DbDraft import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.worker.draft.RemoveDraftWorker import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel class DraftViewModel @AssistedInject constructor( private val repository: DraftRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index d71b22cd7..58409ad8c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.viewmodel import android.content.Context import android.net.Uri -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R @@ -40,6 +38,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MediaViewModel @AssistedInject constructor( private val repository: StatusRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt index de8e036ad..961bee7d7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt @@ -20,12 +20,12 @@ */ package com.twidere.twiderex.viewmodel -import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.MediaRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.flow +import moe.tlaster.precompose.viewmodel.ViewModel class PureMediaViewModel @AssistedInject constructor( private val mediaRepository: MediaRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index e55d5c198..1b5174887 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.StatusRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class StatusViewModel @AssistedInject constructor( private val statusRepository: StatusRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index ae1e7a4a6..915b8fda0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -32,8 +32,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R @@ -66,6 +64,8 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import java.util.UUID enum class ComposeType { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index bad2aec1a..3123e5e73 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.compose -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -33,6 +31,8 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonComposeSearchHashtagViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 5a0054610..ea9a007dc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -20,13 +20,13 @@ */ package com.twidere.twiderex.viewmodel.dm -import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.DirectMessageRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel class DMConversationViewModel @AssistedInject constructor( private val repository: DirectMessageRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index b7058140c..a038c161c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.viewmodel.dm import android.net.Uri -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService @@ -39,6 +37,8 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import java.util.UUID class DMEventViewModel @AssistedInject constructor( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 090fbd8e6..0bae3e466 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.dm -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -39,6 +37,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class DMNewConversationViewModel @AssistedInject constructor( private val dmRepository: DirectMessageRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 88d752ad4..9b895f0e3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.lists -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -33,6 +31,8 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsSearchUserViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index a08183e50..140485c14 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.lists -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsTimelineViewModel @AssistedInject constructor( repository: TimelineRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 0be85271e..5a86aad74 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails @@ -37,6 +35,8 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsUserViewModel @AssistedInject constructor( private val listsUsersRepository: ListsUsersRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index cabe20335..7a1b6a307 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.lists -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.twiderex.model.AccountDetails @@ -37,6 +35,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsViewModel @AssistedInject constructor( private val listsRepository: ListsRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 4564e21cf..7394b893c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,13 +20,13 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TimelineRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonHashtagViewModel @AssistedInject constructor( private val repository: TimelineRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 7b50d081a..9fd2dc395 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -29,6 +27,8 @@ import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonSearchHashtagViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index 83d89ef0b..2aa5f9bd2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.viewmodel.mastodon import android.accounts.Account import androidx.compose.ui.text.input.TextFieldValue -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.mastodon.MastodonOAuthService import com.twidere.twiderex.db.mapper.toDbUser import com.twidere.twiderex.http.TwidereServiceFactory @@ -40,6 +38,8 @@ import com.twidere.twiderex.utils.json import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import java.net.URI class MastodonSignInViewModel @AssistedInject constructor( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 8ff655e50..b76188c57 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.db.model.DbSearch import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.SearchRepository @@ -29,6 +27,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchInputViewModel @AssistedInject constructor( private val repository: SearchRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index be04e5450..fdf93494c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.search -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.SearchRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchSaveViewModel @AssistedInject constructor( private val repository: SearchRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index be83287b9..89b22b962 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService @@ -33,6 +31,8 @@ import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchTweetsViewModel @AssistedInject constructor( val database: CacheDatabase, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index 647c40461..ffd46e677 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -29,6 +27,8 @@ import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.source.SearchUserPagingSource +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchUserViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 1370b6468..7752cac8c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.settings -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class AccountNotificationViewModel @AssistedInject constructor( private val accountRepository: AccountRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt index 40bddf777..b9528e1df 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.AppearancePreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class AppearanceViewModel @AssistedInject constructor( private val appearancePreferences: DataStore diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index b15b71a30..8e05bd610 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.DisplayPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class DisplayViewModel @AssistedInject constructor( private val displayPreferences: DataStore diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index d3bd96cd0..af765b962 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,13 +20,13 @@ */ package com.twidere.twiderex.viewmodel.settings -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.scenes.home.HomeMenus import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import kotlin.math.min private const val MaxMenuCount = 5 diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index e11be594f..d6b75698f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.R import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences @@ -31,6 +29,8 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope @OptIn(FlowPreview::class) class MiscViewModel @AssistedInject constructor( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt index f9c9c95bd..f4dd6268b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt @@ -21,12 +21,12 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.preferences.model.NotificationPreferences import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class NotificationViewModel @AssistedInject constructor( val notification: DataStore, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt index 2ba96fa21..ab8a71c1f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt @@ -20,12 +20,12 @@ */ package com.twidere.twiderex.viewmodel.settings -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.twiderex.repository.CacheRepository import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class StorageViewModel @AssistedInject constructor( private val repository: CacheRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index d03e1e897..e5932a2d6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import androidx.core.content.edit -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.toUi @@ -32,6 +30,8 @@ import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope abstract class TimelineViewModel( private val preferences: SharedPreferences, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index fa3cf1c8f..ef0ef584d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -20,11 +20,11 @@ */ package com.twidere.twiderex.viewmodel.trend -import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.TrendRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel class TrendViewModel @AssistedInject constructor( private val repository: TrendRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 40983f04a..efc9566ab 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.viewmodel.twitter import android.accounts.Account -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.http.MicroBlogException import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService @@ -44,6 +42,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope import retrofit2.HttpException import java.io.IOException diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 8416dae0f..4d002ad7f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.twitter.search -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map @@ -34,6 +32,8 @@ import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterSearchMediaViewModel @AssistedInject constructor( val database: CacheDatabase, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 42e019bb4..09cfcbf4b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.twitter.user -import moe.tlaster.precompose.viewmodel.ViewModel import com.twidere.services.microblog.LookupService import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.notification.InAppNotification @@ -30,6 +29,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flow +import moe.tlaster.precompose.viewmodel.ViewModel class TwitterUserViewModel @AssistedInject constructor( private val repository: UserRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 8ff3e21a9..d36fd816b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -29,6 +28,7 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.source.FollowersPagingSource +import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 52ba1a479..f4f030a51 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn @@ -29,6 +28,7 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.source.FollowingPagingSource +import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( private val account: AccountDetails, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 3fdeb622e..ce572cd8b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.TimelineRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserFavouriteTimelineViewModel @AssistedInject constructor( private val repository: TimelineRepository, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt index 33d4d8003..e407d2d1c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt @@ -20,10 +20,10 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.ViewModel import androidx.paging.PagingData import com.twidere.twiderex.model.ui.UiUser import kotlinx.coroutines.flow.Flow +import moe.tlaster.precompose.viewmodel.ViewModel abstract class UserListViewModel : ViewModel() { abstract val source: Flow> diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index a7984395c..a791ac616 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn @@ -41,6 +39,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserMediaTimelineViewModel @AssistedInject constructor( database: CacheDatabase, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 1cf9287f0..d0f8b6dfa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel.user -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship @@ -34,6 +32,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserViewModel @AssistedInject constructor( private val repository: UserRepository, diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt index ca7cb9dd4..b78d24071 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package moe.tlaster.precompose.viewmodel.compose import androidx.compose.runtime.Composable @@ -20,4 +40,3 @@ inline fun viewModel( it.getViewModel(key, creator) } } - From 0f3f0d1bad97ead0668f79c5b097de8ef7eea139 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 24 Aug 2021 18:26:46 +0800 Subject: [PATCH 078/615] [WIP] migrate active account to repo flow --- .../twiderex/viewmodel/DraftViewModel.kt | 5 -- .../twiderex/viewmodel/MediaViewModel.kt | 63 ++++++++------- .../twiderex/viewmodel/PureMediaViewModel.kt | 11 +-- .../twiderex/viewmodel/StatusViewModel.kt | 33 +++++--- .../twitter/user/TwitterUserViewModel.kt | 52 ++++++------- .../user/UserFavouriteTimelineViewModel.kt | 30 ++++---- .../user/UserMediaTimelineViewModel.kt | 76 ++++++++++--------- .../viewmodel/user/UserTimelineViewModel.kt | 46 ++++++----- .../twiderex/viewmodel/user/UserViewModel.kt | 32 ++++---- .../com/twidere/twiderex/ext/FlowExt.kt | 34 +++++++++ 10 files changed, 221 insertions(+), 161 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index b3dd28e9f..542f226b1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -34,11 +34,6 @@ class DraftViewModel @AssistedInject constructor( private val notificationManagerCompat: NotificationManagerCompat, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): DraftViewModel - } - fun delete(it: DbDraft) { workManager.beginWith(RemoveDraftWorker.create(it._id)).enqueue() notificationManagerCompat.cancel(it._id.hashCode()) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 58409ad8c..68f0e5dc8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -23,33 +23,38 @@ package com.twidere.twiderex.viewmodel import android.content.Context import android.net.Uri import androidx.work.WorkManager -import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.utils.FileProviderHelper -import com.twidere.twiderex.utils.notify import com.twidere.twiderex.worker.DownloadMediaWorker import com.twidere.twiderex.worker.ShareMediaWorker -import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class MediaViewModel @AssistedInject constructor( private val repository: StatusRepository, + private val accountRepository: AccountRepository, private val inAppNotification: InAppNotification, private val workManager: WorkManager, - @Assisted private val account: AccountDetails, - @Assisted private val statusKey: MicroBlogKey, + private val statusKey: MicroBlogKey, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } - fun saveFile(currentMedia: UiMedia, target: Uri) { + fun saveFile(currentMedia: UiMedia, target: Uri) = viewModelScope.launch { + val account = account.lastOrNull() ?: return@launch currentMedia.mediaUrl?.let { DownloadMediaWorker.create( accountKey = account.accountKey, @@ -61,7 +66,8 @@ class MediaViewModel @AssistedInject constructor( } } - fun shareMedia(currentMedia: UiMedia, target: String, context: Context) { + fun shareMedia(currentMedia: UiMedia, target: String, context: Context) = viewModelScope.launch { + val account = account.lastOrNull() ?: return@launch val uri = FileProviderHelper.getUriFromMedia(target, context) inAppNotification.show(R.string.common_alerts_media_sharing_title) currentMedia.mediaUrl?.let { @@ -80,30 +86,31 @@ class MediaViewModel @AssistedInject constructor( } } - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, statusKey: MicroBlogKey): MediaViewModel - } - val loading = MutableStateFlow(false) val status by lazy { - repository.loadStatus( - statusKey = statusKey, - accountKey = account.accountKey, - ) - } - - init { - viewModelScope.launch { - try { - repository.loadTweetFromNetwork( - statusKey.id, - account.accountKey, - account.service as LookupService + account.flatMapLatest { + if (it != null) { + repository.loadStatus( + statusKey = statusKey, + accountKey = it.accountKey, ) - } catch (e: Throwable) { - e.notify(inAppNotification) + } else { + emptyFlow() } } } + + // init { + // viewModelScope.launch { + // try { + // repository.loadTweetFromNetwork( + // statusKey.id, + // account.accountKey, + // account.service as LookupService + // ) + // } catch (e: Throwable) { + // e.notify(inAppNotification) + // } + // } + // } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt index 961bee7d7..6b56d8f05 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt @@ -22,20 +22,13 @@ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.MediaRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.flow import moe.tlaster.precompose.viewmodel.ViewModel -class PureMediaViewModel @AssistedInject constructor( +class PureMediaViewModel( private val mediaRepository: MediaRepository, - @Assisted private val belongKey: MicroBlogKey, // statusKey, conversationKey + private val belongKey: MicroBlogKey, // statusKey, conversationKey ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(belongKey: MicroBlogKey): PureMediaViewModel - } - val source = flow { emit(mediaRepository.findMediaByBelongToKey(belongKey)) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index 1b5174887..3b5dc96a4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -21,30 +21,41 @@ package com.twidere.twiderex.viewmodel import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class StatusViewModel @AssistedInject constructor( private val statusRepository: StatusRepository, - @Assisted private val account: AccountDetails, - @Assisted private val statusKey: MicroBlogKey, + private val accountRepository: AccountRepository, + private val statusKey: MicroBlogKey, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, statusKey: MicroBlogKey): StatusViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } - val status by lazy { - statusRepository.loadStatus(statusKey = statusKey, accountKey = account.accountKey) + account.flatMapLatest { + if (it != null) { + statusRepository.loadStatus(statusKey = statusKey, accountKey = it.accountKey) + } else { + emptyFlow() + } + }.asStateIn(viewModelScope, null) } val source by lazy { - statusRepository.conversation(statusKey = statusKey, account = account) - .cachedIn(viewModelScope) + account.flatMapLatest { + if (it != null) { + statusRepository.conversation(statusKey = statusKey, account = it) + } else { + emptyFlow() + } + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 09cfcbf4b..3a2a810ea 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -21,46 +21,46 @@ package com.twidere.twiderex.viewmodel.twitter.user import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notify -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope -class TwitterUserViewModel @AssistedInject constructor( +class TwitterUserViewModel( private val repository: UserRepository, private val inAppNotification: InAppNotification, - @Assisted private val account: AccountDetails, - @Assisted private val screenName: String, + private val accountRepository: AccountRepository, + private val screenName: String, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - screenName: String?, - ): TwitterUserViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val error = MutableStateFlow(null) - val user = flow { - runCatching { - repository.lookupUserByName( - screenName, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - }.onSuccess { - emit(it) - }.onFailure { - it.notify(inAppNotification) - emit(null) - error.value = it + val user by lazy { + account.map { + if (it != null) { + try { + repository.lookupUserByName( + screenName, + accountKey = it.accountKey, + lookupService = it.service as LookupService, + ) + } catch (e: Throwable) { + e.notify(inAppNotification) + error.value = e + null + } + } else { + null + } } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index ce572cd8b..8e8c21d4a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -21,29 +21,31 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class UserFavouriteTimelineViewModel @AssistedInject constructor( +class UserFavouriteTimelineViewModel( private val repository: TimelineRepository, - @Assisted account: AccountDetails, - @Assisted userKey: MicroBlogKey, + private val accountRepository: AccountRepository, + private val userKey: MicroBlogKey, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - userKey: MicroBlogKey, - ): UserFavouriteTimelineViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.favouriteTimeline(userKey = userKey, account = account).cachedIn(viewModelScope) + account.flatMapLatest { + if (it != null) { + repository.favouriteTimeline(userKey = userKey, account = it) + } else { + emptyFlow() + } + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index a791ac616..ce531c074 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -27,58 +27,62 @@ import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.paging.mediator.paging.PagingMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.user.UserMediaMediator -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class UserMediaTimelineViewModel @AssistedInject constructor( - database: CacheDatabase, - @Assisted account: AccountDetails, - @Assisted userKey: MicroBlogKey, +class UserMediaTimelineViewModel( + private val database: CacheDatabase, + private val repository: AccountRepository, + private val userKey: MicroBlogKey, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - userKey: MicroBlogKey, - ): UserMediaTimelineViewModel + private val account by lazy { + repository.activeAccount.asStateIn(viewModelScope, null) } val source: Flow>> by lazy { - pagingMediator.pager( - config = PagingConfig( - pageSize = 200, - prefetchDistance = 4, - enablePlaceholders = false, - ) - ).flow.map { pagingData -> - pagingData.map { - it.toUi(pagingMediator.accountKey) - } - }.cachedIn(viewModelScope).map { - it.flatMap { - it.media.map { media -> media to it } - } + pagingMediator.flatMapLatest { + it?.let { pagingMediator -> + pagingMediator.pager( + config = PagingConfig( + pageSize = 200, + prefetchDistance = 4, + enablePlaceholders = false, + ) + ).flow.map { pagingData -> + pagingData.map { + it.toUi(pagingMediator.accountKey) + } + }.cachedIn(viewModelScope).map { + it.flatMap { + it.media.map { media -> media to it } + } + } + } ?: emptyFlow() } } - val pagingMediator: PagingMediator = - UserMediaMediator( - userKey = userKey, - database = database, - accountKey = account.accountKey, - service = account.service as TimelineService - ) + val pagingMediator by lazy { + account.map { + it?.let { + UserMediaMediator( + userKey = userKey, + database = database, + accountKey = it.accountKey, + service = it.service as TimelineService + ) + } + }.asStateIn(viewModelScope, null) + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index dc4bd0367..be875d893 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -21,35 +21,43 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flattenMerge import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class UserTimelineViewModel @AssistedInject constructor( +class UserTimelineViewModel( private val repository: TimelineRepository, - @Assisted account: AccountDetails, - @Assisted userKey: MicroBlogKey, - @Assisted exclude_replies: Boolean, + private val accountRepository: AccountRepository, + userKey: MicroBlogKey, ) : ViewModel() { + private val _excludeReplies = MutableStateFlow(false) + val excludeReplies = _excludeReplies.asStateIn(viewModelScope, false) + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - userKey: MicroBlogKey, - exclude_replies: Boolean - ): UserTimelineViewModel + fun setExcludeReplies(value: Boolean) { + _excludeReplies.value = value } val source by lazy { - repository.userTimeline( - userKey = userKey, - account = account, - exclude_replies = exclude_replies, - ).cachedIn(viewModelScope) + combine(account, _excludeReplies) { account, excludeReplies -> + if (account != null) { + repository.userTimeline( + userKey = userKey, + account = account, + exclude_replies = excludeReplies, + ) + } else { + emptyFlow() + } + }.flattenMerge().cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index d0f8b6dfa..aebb73ff7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -23,45 +23,45 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notify import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class UserViewModel @AssistedInject constructor( private val repository: UserRepository, + private val accountRepository: AccountRepository, private val inAppNotification: InAppNotification, - @Assisted private val account: AccountDetails, @Assisted private val userKey: MicroBlogKey, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - initialUserKey: MicroBlogKey?, - ): UserViewModel - } - - private val relationshipService by lazy { - account.service as RelationshipService + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val refreshing = MutableStateFlow(false) val loadingRelationship = MutableStateFlow(false) val user = repository.getUserFlow(userKey) val relationship = MutableStateFlow(null) - val isMe = userKey == account.accountKey + val isMe by lazy { + account.map { + userKey == it?.accountKey + }.asStateIn(viewModelScope, false) + } fun refresh() = viewModelScope.launch { refreshing.value = true + val account = account.lastOrNull() ?: return@launch runCatching { repository.lookupUserById( userKey.id, @@ -76,6 +76,8 @@ class UserViewModel @AssistedInject constructor( fun follow() = viewModelScope.launch { loadingRelationship.value = true + val account = account.lastOrNull() ?: return@launch + val relationshipService = account.service as? RelationshipService ?: return@launch runCatching { relationshipService.follow(userKey.id) }.onSuccess { @@ -88,6 +90,8 @@ class UserViewModel @AssistedInject constructor( fun unfollow() = viewModelScope.launch { loadingRelationship.value = true + val account = account.lastOrNull() ?: return@launch + val relationshipService = account.service as? RelationshipService ?: return@launch runCatching { relationshipService.unfollow(userKey.id) }.onSuccess { @@ -100,6 +104,8 @@ class UserViewModel @AssistedInject constructor( private fun loadRelationShip() = viewModelScope.launch { loadingRelationship.value = true + val account = account.lastOrNull() ?: return@launch + val relationshipService = account.service as? RelationshipService ?: return@launch try { relationshipService.showRelationship(userKey.id).let { relationship.value = it diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt new file mode 100644 index 000000000..f12ccc848 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt @@ -0,0 +1,34 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.ext + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn + +fun Flow.asStateIn( + scope: CoroutineScope, + initialValue: T +): StateFlow { + return stateIn(scope, SharingStarted.Lazily, initialValue) +} From 532f9fef723438565f2b9e5dc0a07ca93a4cef62 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 18:28:02 +0800 Subject: [PATCH 079/615] add test for rest dao implement in androidMain --- .../twidere/twiderex/db/DraftDaoImplTest.kt | 62 +++++++ .../twidere/twiderex/db/ListsDaoImplTest.kt | 1 - .../twidere/twiderex/db/MediaDaoImplTest.kt | 41 +++++ .../db/NotificationCursorDaoImplTest.kt | 65 +++++++ .../twidere/twiderex/db/PagingDaoImplTest.kt | 160 ++++++++++++++++++ .../twidere/twiderex/db/StatusDaoImplTest.kt | 146 ++++++++++++++++ .../twidere/twiderex/db/UserDaoImplTest.kt | 60 +++++++ .../dataprovider/db/dao/StatusDaoImpl.kt | 1 + .../twiderex/room/db/dao/RoomReactionDao.kt | 3 + .../twidere/twiderex/mock/model/MockModels.kt | 18 +- 10 files changed, 552 insertions(+), 5 deletions(-) create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt new file mode 100644 index 000000000..b8f685b8a --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.AppDatabaseImpl +import com.twidere.twiderex.db.base.AppDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockUiDraft +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class DraftDaoImplTest : AppDatabaseDaoTest() { + + @Test + fun getAll_returnResultFlow(): Unit = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + val resultFlow = appDatabase.draftDao().getAll() + assertEquals(0, resultFlow.firstOrNull()?.size) + val removeDraft = mockUiDraft() + appDatabase.withTransaction { + appDatabase.draftDao().insert(removeDraft) + appDatabase.draftDao().insert(mockUiDraft()) + } + assertEquals(2, resultFlow.firstOrNull()?.size) + + appDatabase.draftDao().remove(removeDraft) + + assertNull(appDatabase.draftDao().get(removeDraft.draftId)) + } + + @Test + fun getDraftCount_returnCorrectCountFlow(): Unit = runBlocking { + val appDatabase = AppDatabaseImpl(roomDatabase) + val resultFlow = appDatabase.draftDao().getDraftCount() + assertEquals(0, resultFlow.firstOrNull()) + appDatabase.withTransaction { + appDatabase.draftDao().insert(mockUiDraft()) + appDatabase.draftDao().insert(mockUiDraft()) + } + assertEquals(2, resultFlow.firstOrNull()) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt index 7df0794db..2df09ce69 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt @@ -104,7 +104,6 @@ internal class ListsDaoImplTest : CacheDatabaseDaoTest() { @Test fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { val cacheDatabase = CacheDatabaseImpl(roomDatabase) - cacheDatabase.listsDao().insertAll(insertData) var invalidate = false cacheDatabase.listsDao().getPagingSource( accountKey = accountKey diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt new file mode 100644 index 000000000..2aba33d7c --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockUiMedia +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.room.db.transform.toDbMedia +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class MediaDaoImplTest : CacheDatabaseDaoTest() { + + @Test + fun findMediaByBelongToKey_ReturnsResultMatchTheBelongKey() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val belongKey = MicroBlogKey.twitter("test") + roomDatabase.mediaDao().insertAll(listOf(mockUiMedia(belongToKey = belongKey)).toDbMedia()) + assertEquals(belongKey, cacheDatabase.mediaDao().findMediaByBelongToKey(belongKey).first().belongToKey) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt new file mode 100644 index 000000000..d2a68013a --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt @@ -0,0 +1,65 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class NotificationCursorDaoImplTest : CacheDatabaseDaoTest() { + private fun generateCursor( + type: NotificationCursorType, + accountKey: MicroBlogKey + ) = NotificationCursor( + _id = UUID.randomUUID().toString(), + accountKey = accountKey, + type = type, + value = type.name, + timestamp = System.currentTimeMillis() + ) + + @Test + fun addAndFindCursorWithCorrectType(): Unit = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val accountKey = MicroBlogKey.twitter("test") + val generalCursor = generateCursor(NotificationCursorType.General, accountKey) + val mentionsCursor = generateCursor(NotificationCursorType.Mentions, accountKey) + val followerCursor = generateCursor(NotificationCursorType.Follower, accountKey) + cacheDatabase.withTransaction { + cacheDatabase.notificationCursorDao().apply { + add(generalCursor) + add(mentionsCursor) + add(followerCursor) + } + } + cacheDatabase.notificationCursorDao().apply { + assertEquals(generalCursor._id, find(accountKey = accountKey, type = NotificationCursorType.General)?._id) + assertEquals(mentionsCursor._id, find(accountKey = accountKey, type = NotificationCursorType.Mentions)?._id) + assertEquals(followerCursor._id, find(accountKey = accountKey, type = NotificationCursorType.Follower)?._id) + } + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt new file mode 100644 index 000000000..92973f387 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt @@ -0,0 +1,160 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import androidx.paging.PagingSource +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class PagingDaoImplTest : CacheDatabaseDaoTest() { + private val accountKey = MicroBlogKey.twitter("account") + private val pagingKey = "pagingKey" + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, pagingKey), + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData.map { it.timeline } + ) + cacheDatabase.statusDao().insertAll(listOf = originData.map { it.status }, accountKey = accountKey) + val pagingSource = cacheDatabase.pagingTimelineDao().getPagingSource( + accountKey = accountKey, + pagingKey = pagingKey + ) + val limit = 3 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + var invalidate = false + cacheDatabase.pagingTimelineDao().getPagingSource( + accountKey = accountKey, + pagingKey = pagingKey + ).registerInvalidatedCallback { + invalidate = true + } + cacheDatabase.pagingTimelineDao().insertAll( + listOf( + mockIStatus().toPagingTimeline(accountKey, pagingKey).timeline, + ) + ) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun getLatest_ReturnsResultWithMaxValueOfSortId() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, pagingKey), + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData.map { it.timeline } + ) + cacheDatabase.statusDao().insertAll(listOf = originData.map { it.status }, accountKey = accountKey) + assertEquals( + originData.maxOf { it.timeline.sortId }, + cacheDatabase.pagingTimelineDao().getLatest( + pagingKey = pagingKey, + accountKey = accountKey + )?.timeline?.sortId + ) + } + + @Test + fun delete_DeletePagingTimelineThatMatchesStatusKey(): Unit = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus().toPagingTimeline(accountKey, pagingKey).timeline, + mockIStatus().toPagingTimeline(accountKey, pagingKey).timeline, + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData + ) + cacheDatabase.pagingTimelineDao().delete(originData.first().statusKey) + assertNull( + cacheDatabase.pagingTimelineDao().findWithStatusKey( + maxStatusKey = originData.first().statusKey, + accountKey = accountKey + ) + ) + + assertNotNull( + cacheDatabase.pagingTimelineDao().findWithStatusKey( + maxStatusKey = originData[1].statusKey, + accountKey = accountKey + ) + ) + } + + @Test + fun clearAll_ClearAllPagingTimelineThatMatchesPagingKey(): Unit = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus().toPagingTimeline(accountKey, "pagingKey1").timeline, + mockIStatus().toPagingTimeline(accountKey, "pagingKey2").timeline, + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData + ) + cacheDatabase.pagingTimelineDao().clearAll(pagingKey = originData.first().pagingKey, accountKey = accountKey) + assertNull( + cacheDatabase.pagingTimelineDao().findWithStatusKey( + maxStatusKey = originData.first().statusKey, + accountKey = accountKey + ) + ) + + assertNotNull( + cacheDatabase.pagingTimelineDao().findWithStatusKey( + maxStatusKey = originData[1].statusKey, + accountKey = accountKey + ) + ) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt new file mode 100644 index 000000000..efd2a5c2a --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt @@ -0,0 +1,146 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockIStatus +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUrlEntity +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class StatusDaoImplTest : CacheDatabaseDaoTest() { + private val accountKey = MicroBlogKey.twitter("123") + + @Test + fun insertAll_SaveBothStatusAndAttachmentsToDatabase() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val status = mockIStatus(hasMedia = true, hasReference = true).toUi(accountKey).copy( + url = listOf( + UiUrlEntity( + url = "test", + expandedUrl = "", + displayUrl = "", + title = "", + description = "", + image = "" + ), + ), + retweeted = true, + liked = true + ) + cacheDatabase.statusDao().insertAll(listOf = listOf(status), accountKey = accountKey) + cacheDatabase.statusDao().findWithStatusKey(status.statusKey, accountKey = accountKey)?.let { + assertEquals(status.statusKey, it.statusKey) + assertEquals(status.referenceStatus.values.first().statusKey, it.referenceStatus.values.first().statusKey) + assertEquals(status.media.first().url, it.media.first().url) + assertEquals(status.url.first().url, it.url.first().url) + assertEquals(status.user.userKey, it.user.userKey) + assertEquals(status.retweeted, it.retweeted) + assertEquals(status.liked, it.retweeted) + } ?: assert(false) + + roomDatabase.statusReferenceDao().find(key = status.referenceStatus.values.first().statusKey, referenceType = status.referenceStatus.keys.first()).let { + assert(it.isNotEmpty()) + } + + roomDatabase.mediaDao().findMediaByBelongToKey(status.statusKey).let { + assert(it.isNotEmpty()) + } + + roomDatabase.userDao().findWithUserKey(status.user.userKey).let { + assertEquals(status.user.userKey, it?.userKey) + } + + roomDatabase.urlEntityDao().findWithBelongToKey(status.statusKey).let { + assert(it.isNotEmpty()) + } + + roomDatabase.reactionDao().findWithStatusKey(statusKey = status.statusKey, accountKey = accountKey).let { + assertEquals(status.retweeted, it?.retweeted) + assertEquals(status.liked, it?.retweeted) + } + } + + @Test + fun delete_DeleteBothStatusAndReferencesAndReactions() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val status = mockIStatus(hasReference = true).toUi(accountKey) + val statusFlow = cacheDatabase.statusDao().findWithStatusKeyWithFlow(statusKey = status.statusKey, accountKey = accountKey) + cacheDatabase.statusDao().insertAll(listOf = listOf(status), accountKey = accountKey) + assertNotNull(statusFlow.firstOrNull()) + cacheDatabase.statusDao().delete(statusKey = status.statusKey) + assertNull(statusFlow.firstOrNull()) + assert(roomDatabase.statusReferenceDao().find(key = status.referenceStatus.values.first().statusKey, referenceType = status.referenceStatus.keys.first()).isEmpty()) + assertNull(roomDatabase.reactionDao().findWithStatusKey(statusKey = status.statusKey, accountKey = accountKey)) + } + + @Test + fun findWithStatusKeyWithFlow_ReturnsStatusFlowAndUpdateAfterDbChanged() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val status = mockIStatus().toUi(accountKey) + val statusFlow = cacheDatabase.statusDao().findWithStatusKeyWithFlow(statusKey = status.statusKey, accountKey = accountKey) + assertNull(statusFlow.firstOrNull()) + cacheDatabase.statusDao().insertAll(listOf = listOf(status), accountKey = accountKey) + assertEquals(status.statusKey, statusFlow.firstOrNull()?.statusKey) + + cacheDatabase.statusDao().updateAction( + statusKey = status.statusKey, + accountKey = accountKey, + liked = true, + retweet = null + ) + assertEquals(true, statusFlow.firstOrNull()?.liked) + } + + @Test + fun updateReaction_UpdateOnlyEffectOnSpecifiedReaction() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val status = mockIStatus().toUi(accountKey) + val statusFlow = cacheDatabase.statusDao().findWithStatusKeyWithFlow(statusKey = status.statusKey, accountKey = accountKey) + cacheDatabase.statusDao().insertAll(listOf = listOf(status), accountKey = accountKey) + assertEquals(false, statusFlow.firstOrNull()?.liked) + assertEquals(false, statusFlow.firstOrNull()?.retweeted) + cacheDatabase.statusDao().updateAction( + statusKey = status.statusKey, + accountKey = accountKey, + liked = true, + retweet = false + ) + assertEquals(true, statusFlow.firstOrNull()?.liked) + assertEquals(false, statusFlow.firstOrNull()?.retweeted) + + cacheDatabase.statusDao().updateAction( + statusKey = status.statusKey, + accountKey = accountKey, + liked = false, + retweet = true + ) + assertEquals(false, statusFlow.firstOrNull()?.liked) + assertEquals(true, statusFlow.firstOrNull()?.retweeted) + } +} diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt new file mode 100644 index 000000000..745d7d575 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt @@ -0,0 +1,60 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.base.CacheDatabaseDaoTest +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class UserDaoImplTest : CacheDatabaseDaoTest() { + + @Test + fun findWithUserKey_ReturnUserMatchUserKey() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val accountKey = MicroBlogKey.twitter("account") + val user = mockIUser(id = "user").toUi(accountKey) + cacheDatabase.userDao().insertAll(listOf(user)) + assertEquals(user.userKey, cacheDatabase.userDao().findWithUserKey(user.userKey)?.userKey) + assertNull(cacheDatabase.userDao().findWithUserKey(MicroBlogKey.twitter("not exists"))?.userKey) + } + + @Test + fun findWithUserKeyFlow_ReturnUserFlowAndMatchUserKey() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val accountKey = MicroBlogKey.twitter("account") + val user = mockIUser(id = "user").toUi(accountKey) + val userFlow = cacheDatabase.userDao().findWithUserKeyFlow(user.userKey) + assertNull(userFlow.firstOrNull()) + + cacheDatabase.userDao().insertAll(listOf(mockIUser("other user").toUi(accountKey))) + assertNull(userFlow.firstOrNull()) + + cacheDatabase.userDao().insertAll(listOf(user)) + assertEquals(user.userKey, userFlow.firstOrNull()?.userKey) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt index f41509e48..894a0371e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt @@ -52,6 +52,7 @@ internal class StatusDaoImpl( override suspend fun delete(statusKey: MicroBlogKey) { roomCacheDatabase.statusDao().delete(statusKey) roomCacheDatabase.statusReferenceDao().delete(statusKey) + roomCacheDatabase.reactionDao().delete(statusKey) } override suspend fun updateAction( diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt index 31a517bf3..9e6007740 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt @@ -34,4 +34,7 @@ internal interface RoomReactionDao { @Query("SELECT * FROM status_reactions WHERE statusKey == :statusKey AND accountKey == :accountKey") suspend fun findWithStatusKey(statusKey: MicroBlogKey, accountKey: MicroBlogKey): DbStatusReaction? + + @Query("DELETE FROM status_reactions WHERE statusKey == :statusKey") + suspend fun delete(statusKey: MicroBlogKey) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index cb71fbd80..fff05b2bd 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -42,6 +42,8 @@ import com.twidere.services.twitter.model.MessageCreate import com.twidere.services.twitter.model.MessageData import com.twidere.services.twitter.model.MessageTarget import com.twidere.services.twitter.model.PurpleMedia +import com.twidere.services.twitter.model.ReferencedTweetType +import com.twidere.services.twitter.model.ReferencedTweetV2 import com.twidere.services.twitter.model.StatusV2 import com.twidere.services.twitter.model.TwitterList import com.twidere.services.twitter.model.TwitterPaging @@ -72,7 +74,7 @@ fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty @TestOnly fun mockUiDraft( - draftId: String = "", + draftId: String = UUID.randomUUID().toString(), content: String = "", composeType: ComposeType = ComposeType.New, statusKey: MicroBlogKey = MicroBlogKey.twitter(UUID.randomUUID().toString()) @@ -144,15 +146,23 @@ fun mockIListModel( fun mockIStatus( id: String = UUID.randomUUID().toString(), hasMedia: Boolean = false, - authorId: String = UUID.randomUUID().toString() + authorId: String = UUID.randomUUID().toString(), + hasReference: Boolean = false ): IStatus { return StatusV2( id = id, authorID = authorId, createdAt = Date().apply { time = System.currentTimeMillis() }, attachments = if (hasMedia) AttachmentsV2(mediaKeys = listOf("mediaKey")).apply { - media = listOf(MediaV2(url = "mediaUrl")) - } else null + media = listOf(MediaV2(url = "mediaUrl", type = "photo")) + } else null, + + referencedTweets = if (hasReference) listOf( + ReferencedTweetV2( + type = ReferencedTweetType.retweeted, + id = UUID.randomUUID().toString() + ).apply { status = mockIStatus() as StatusV2 } + ) else emptyList() ).apply { user = UserV2( id = authorId, From 212e501543e25fce686bdec67f603ad00aa2decf Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 24 Aug 2021 19:02:54 +0800 Subject: [PATCH 080/615] fix some test error --- .../com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt | 2 +- .../com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt | 2 +- .../twiderex/paging/crud/MemoryCachePagingMediatorTest.kt | 6 +++--- .../twiderex/paging/crud/MemoryCachePagingSourceTest.kt | 8 ++++---- .../twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt | 8 ++++---- .../twiderex/paging/paging/PagingWithGapMediatorTest.kt | 2 +- .../twiderex/repository/NotificationRepositoryTest.kt | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt index 061706ad2..00f1dc489 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt @@ -22,6 +22,6 @@ package com.twidere.twiderex.db.base import com.twidere.twiderex.room.db.RoomAppDatabase -internal open class AppDatabaseDaoTest : BaseDaoTest() { +internal abstract class AppDatabaseDaoTest : BaseDaoTest() { override fun getDBClass() = RoomAppDatabase::class.java } diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt index 83c3f12b8..7089ca84d 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt @@ -22,6 +22,6 @@ package com.twidere.twiderex.db.base import com.twidere.twiderex.room.db.RoomCacheDatabase -internal open class CacheDatabaseDaoTest : BaseDaoTest() { +internal abstract class CacheDatabaseDaoTest : BaseDaoTest() { override fun getDBClass() = RoomCacheDatabase::class.java } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt index e2e66908d..1b2d41ac3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt @@ -25,8 +25,8 @@ import androidx.paging.PagingConfig import androidx.paging.PagingSource import androidx.paging.PagingState import kotlinx.coroutines.runBlocking -import org.junit.Assert import org.junit.Test +import kotlin.test.assertEquals class TestMemoryCachePagingMediator(pagingMemoryCache: PagingMemoryCache) : MemoryCachePagingMediator(pagingMemoryCache) { override suspend fun load( @@ -44,9 +44,9 @@ class MemoryCachePagingMediatorTest { @Test fun load_saveToPagingMemoryCacheAfterSuccess() = runBlocking { val mediator = TestMemoryCachePagingMediator(pagingMemoryCache) - Assert.assertEquals(0, pagingMemoryCache.size()) + assertEquals(0, pagingMemoryCache.size()) val pagingState = PagingState(emptyList(), config = PagingConfig(20), anchorPosition = 0, leadingPlaceholderCount = 0) mediator.load(LoadType.REFRESH, pagingState) - Assert.assertEquals(2, pagingMemoryCache.size()) + assertEquals(2, pagingMemoryCache.size()) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt index ae2fe26ca..07dc542de 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.paging.crud import androidx.paging.PagingSource import kotlinx.coroutines.runBlocking -import org.junit.Assert import org.junit.Test +import kotlin.test.assertEquals class MemoryCachePagingSourceTest { private val pagingMemoryCache = PagingMemoryCache() @@ -34,14 +34,14 @@ class MemoryCachePagingSourceTest { val source = MemoryCachePagingSource(pagingMemoryCache) var result = source.load(PagingSource.LoadParams.Refresh(null, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isNotEmpty()) - Assert.assertEquals(1, result.nextKey) + assertEquals(1, result.nextKey) result = source.load(PagingSource.LoadParams.Append(result.nextKey!!, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isNotEmpty()) - Assert.assertEquals(2, result.nextKey) + assertEquals(2, result.nextKey) result = source.load(PagingSource.LoadParams.Append(result.nextKey!!, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isEmpty()) - Assert.assertEquals(null, result.nextKey) + assertEquals(null, result.nextKey) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index b1106b2b8..780e000ae 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -21,13 +21,13 @@ package com.twidere.twiderex.paging.crud import org.junit.After -import org.junit.Assert import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.kotlin.times import org.mockito.kotlin.verify +import kotlin.test.assertEquals class PagingMemoryCacheTest { @@ -53,14 +53,14 @@ class PagingMemoryCacheTest { fun find() { pagingMemoryCache.insert(listOf("1", "2", "3", "4")) // check if - Assert.assertEquals("3", pagingMemoryCache.find(2, 3)[0]) + assertEquals("3", pagingMemoryCache.find(2, 3)[0]) // check if index out of bound - Assert.assertEquals(4, pagingMemoryCache.find(0, 10).size) + assertEquals(4, pagingMemoryCache.find(0, 10).size) // check if size correct - Assert.assertEquals(3, pagingMemoryCache.find(0, 3).size) + assertEquals(3, pagingMemoryCache.find(0, 3).size) } @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt index 7408ad9da..f41672e14 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt @@ -118,6 +118,6 @@ internal class MockPagingGapMediator( max_id: String?, since_id: String? ): List { - return listOf(mockIStatus(statusId)).toIPaging() + return listOf(mockIStatus(id = statusId)).toIPaging() } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt index 6e658fb89..11792d565 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt @@ -48,7 +48,7 @@ internal class NotificationRepositoryTest { service = service ) assert(list.isNotEmpty()) - assertEquals(list.maxOf { it.timestamp }, repo.findCursor(accountKey, NotificationCursorType.General)?.timestamp) + assertEquals(list.first().statusId, repo.findCursor(accountKey, NotificationCursorType.General)?.value) } @Test @@ -67,6 +67,6 @@ internal class NotificationRepositoryTest { service = service ) assert(list.isNotEmpty()) - assertEquals(list.maxOf { it.timestamp }, repo.findCursor(accountKey, NotificationCursorType.Mentions)?.timestamp) + assertEquals(list.first().statusId, repo.findCursor(accountKey, NotificationCursorType.Mentions)?.value) } } From 039a7186fdb0970b2c42e41be50f5fb72554989c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 12:45:12 +0800 Subject: [PATCH 081/615] resolved build errors for android module --- .../twiderex/component/UserComponent.kt | 6 +- .../component/navigation/Navigator.kt | 2 +- .../twiderex/component/status/MastodonPoll.kt | 55 +++++++------- .../component/status/StatusMediaComponent.kt | 7 +- .../twiderex/component/status/StatusText.kt | 2 +- .../status/TimelineStatusComponent.kt | 15 ++-- .../com/twidere/twiderex/di/AndroidModule.kt | 21 ------ .../com/twidere/twiderex/di/JobModule.kt | 8 +- .../twidere/twiderex/di/RepositoryModule.kt | 48 ++++++------ .../com/twidere/twiderex/di/TwidereModule.kt | 27 ++++++- .../twiderex/extensions/PagingExtensions.kt | 7 +- .../twiderex/jobs/common/NotificationJob.kt | 3 +- .../jobs/compose/MastodonComposeJob.kt | 14 ++-- .../jobs/compose/TwitterComposeJob.kt | 6 +- .../twiderex/jobs/dm/DirectMessageSendJob.kt | 75 +++++++++---------- .../jobs/dm/TwitterDirectMessageSendJob.kt | 21 +++--- .../twiderex/jobs/status/LikeStatusJob.kt | 6 +- .../twiderex/jobs/status/MastodonVoteJob.kt | 37 ++++----- .../twiderex/jobs/status/RetweetStatusJob.kt | 3 +- .../jobs/status/UnRetweetStatusJob.kt | 3 +- .../twiderex/jobs/status/UnlikeStatusJob.kt | 3 +- .../com/twidere/twiderex/navigation/Route.kt | 3 +- .../twidere/twiderex/scenes/DraftListScene.kt | 4 +- .../twiderex/utils/MastodonEmojiCache.kt | 1 + .../twiderex/utils/PlatformResolver.kt | 8 +- .../twiderex/viewmodel/DraftViewModel.kt | 8 +- .../viewmodel/compose/ComposeViewModel.kt | 8 +- .../mastodon/MastodonSignInViewModel.kt | 5 +- .../viewmodel/search/SearchInputViewModel.kt | 4 +- .../viewmodel/search/SearchTweetsViewModel.kt | 6 +- .../timeline/HomeTimelineViewModel.kt | 4 +- .../timeline/MentionsTimelineViewModel.kt | 4 +- .../timeline/NotificationTimelineViewModel.kt | 4 +- .../viewmodel/timeline/TimelineViewModel.kt | 2 +- .../mastodon/FederatedTimelineViewModel.kt | 4 +- .../mastodon/LocalTimelineViewModel.kt | 4 +- .../twitter/TwitterSignInViewModel.kt | 5 +- .../search/TwitterSearchMediaViewModel.kt | 6 +- .../user/UserMediaTimelineViewModel.kt | 6 +- common/build.gradle.kts | 1 + .../twiderex/dataprovider/DataProvider.kt | 33 ++++++-- .../cache/FileCacheHandlerImpl.kt} | 30 ++++++-- .../room/db/transform/AccountTransform.kt | 4 +- .../room/db/transform/UserTransform.kt | 19 ----- .../twiderex/cache/FileCacheHandler.kt | 7 ++ .../dataprovider/mapper/DataMapper.kt | 19 +++++ .../twiderex/dataprovider/mapper/Mastodon.kt | 37 ++++----- .../twiderex/dataprovider/mapper/Twitter.kt | 2 +- .../kotlin/com/twidere/twiderex/di/Setup.kt | 1 - .../com/twidere/twiderex/di/ext/ComposeExt.kt | 15 +++- .../com/twidere/twiderex/model/ui/UiMedia.kt | 10 +-- .../twiderex/repository/MediaRepository.kt | 6 +- .../mock/cache/MockFileCacheHandler.kt | 10 +++ .../twiderex/mock/db/MockCacheDatabase.kt | 3 +- .../repository/MediaRepositoryTest.kt | 5 +- 55 files changed, 352 insertions(+), 305 deletions(-) rename common/src/{commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt => androidMain/kotlin/com/twidere/twiderex/dataprovider/cache/FileCacheHandlerImpl.kt} (51%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 74f768fc4..86ea31844 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -441,7 +441,7 @@ fun UserInfo( size = UserInfoDefaults.AvatarSize ) { if (user.profileImage is String) { - navController.navigate(RootRoute.Media.Raw(user.profileImage)) + navController.navigate(RootRoute.Media.Raw(user.profileImage.toString())) } } } @@ -558,7 +558,7 @@ fun MastodonUserField(user: UiUser) { if (user.platformType != PlatformType.Mastodon || user.mastodonExtra == null) { return } - user.mastodonExtra.fields.forEachIndexed { index, field -> + user.mastodonExtra?.fields?.forEachIndexed { index, field -> Row( modifier = Modifier .fillMaxWidth() @@ -576,7 +576,7 @@ fun MastodonUserField(user: UiUser) { ) } } - if (index != user.mastodonExtra.fields.lastIndex) { + if (index != user.mastodonExtra?.fields?.lastIndex) { Spacer(modifier = Modifier.height(MastodonUserFieldDefaults.ItemSpacing)) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt index 3ddbe99f2..e17f39f25 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt @@ -80,7 +80,7 @@ class Navigator( PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> { if (status.mastodonExtra != null) { - when (status.mastodonExtra.type) { + when (status.mastodonExtra?.type) { MastodonStatusType.Status -> status.statusKey MastodonStatusType.NotificationFollow, MastodonStatusType.NotificationFollowRequest -> null else -> status.referenceStatus[ReferenceType.MastodonNotification]?.statusKey diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt index 715b00909..6d0386525 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt @@ -91,41 +91,42 @@ fun MastodonPoll(status: UiStatus) { mutableStateListOf() } - status.poll.options.forEachIndexed { index, option -> - MastodonPollOption( - option, - index, - status.poll, - voted = voteState.contains(index), - onVote = { - if (status.poll.multiple) { - if (voteState.contains(index)) { - voteState.remove(index) - } else { - voteState.add(index) - } - } else { - if (voteState.isEmpty()) { - voteState.add(index) + status.poll?.let { poll -> + poll.options.forEachIndexed { index, option -> + MastodonPollOption( + option, + index, + poll, + voted = voteState.contains(index), + onVote = { + if (poll.multiple) { + if (voteState.contains(index)) { + voteState.remove(index) + } else { + voteState.add(index) + } } else { - voteState.clear() - voteState.add(index) + if (voteState.isEmpty()) { + voteState.add(index) + } else { + voteState.clear() + voteState.add(index) + } } } + ) + if (index != poll.options.lastIndex) { + Spacer(modifier = Modifier.height(MastodonPollDefaults.OptionSpacing)) } - ) - if (index != status.poll.options.lastIndex) { - Spacer(modifier = Modifier.height(MastodonPollDefaults.OptionSpacing)) } } - Spacer(modifier = Modifier.height(MastodonPollDefaults.VoteInfoSpacing)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - val countText = status.poll.votersCount?.let { + val countText = status.poll?.votersCount?.let { if (it > 1) { stringResource( id = R.string.common_controls_status_poll_total_people, @@ -137,7 +138,7 @@ fun MastodonPoll(status: UiStatus) { it, ) } - } ?: status.poll.votesCount?.let { + } ?: status.poll?.votesCount?.let { if (it > 1) { stringResource( id = R.string.common_controls_status_poll_total_votes, @@ -161,15 +162,15 @@ fun MastodonPoll(status: UiStatus) { Text(text = countText) } Spacer(modifier = Modifier.width(MastodonPollDefaults.VoteTimeSpacing)) - if (status.poll.expired) { + if (status.poll?.expired == true) { Text(text = stringResource(id = R.string.common_controls_status_poll_expired)) } else { - Text(text = status.poll.expiresAt?.humanizedTimestamp() ?: "") + Text(text = status.poll?.expiresAt?.humanizedTimestamp() ?: "") } } } - if (status.poll.canVote) { + if (status.poll?.canVote == true) { val statusActions = LocalStatusActions.current TextButton( onClick = { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index f5213351a..3f909fa22 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -306,9 +306,10 @@ fun StatusMediaPreviewItem( } } MediaType.video, MediaType.animated_gif -> media.mediaUrl?.let { - if (sensitive && media.previewUrl != null) { + val previewUrl = media.previewUrl + if (sensitive && previewUrl != null) { NetworkBlurImage( - data = media.previewUrl, + data = previewUrl, modifier = Modifier .fillMaxSize(), placeholder = { @@ -328,7 +329,7 @@ fun StatusMediaPreviewItem( showControls = false, volume = 0F ) { - media.previewUrl?.let { + previewUrl?.let { NetworkImage( data = it, modifier = Modifier diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt index 38b120880..fd4aca24f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -62,7 +62,7 @@ fun ColumnScope.StatusText( var expanded by rememberSaveable { mutableStateOf(!expandable) } if (expandable && status.spoilerText != null) { - Text(text = status.spoilerText) + Text(text = status.spoilerText ?: "") Spacer(modifier = Modifier.height(StatusTextDefaults.Mastodon.SpoilerSpacing)) Row( modifier = Modifier diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 581f35072..7670bd9a0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -89,8 +89,8 @@ fun TimelineStatusComponent( data.platformType == PlatformType.Mastodon && data.mastodonExtra != null && ( - data.mastodonExtra.type == MastodonStatusType.NotificationFollowRequest || - data.mastodonExtra.type == MastodonStatusType.NotificationFollow + data.mastodonExtra?.type == MastodonStatusType.NotificationFollowRequest || + data.mastodonExtra?.type == MastodonStatusType.NotificationFollow ) -> { MastodonFollowStatus(data) } @@ -225,7 +225,9 @@ private fun StatusThread(threadStyle: StatusThreadStyle, data: UiStatus) { private fun StatusHeader(data: UiStatus) { when { data.platformType == PlatformType.Mastodon && data.mastodonExtra != null -> { - MastodonStatusHeader(data.mastodonExtra, data) + data.mastodonExtra?.let { + MastodonStatusHeader(it, data) + } } data.retweet != null -> { RetweetHeader(data = data) @@ -474,11 +476,12 @@ fun StatusContent( CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.disabled ) { - if (status.platformType == PlatformType.Mastodon && status.mastodonExtra != null) { + val mastodonExtra = status.mastodonExtra + if (status.platformType == PlatformType.Mastodon && mastodonExtra != null) { Icon( modifier = Modifier.size(LocalTextStyle.current.fontSize.value.dp), - painter = status.mastodonExtra.visibility.icon(), - contentDescription = status.mastodonExtra.visibility.name + painter = mastodonExtra.visibility.icon(), + contentDescription = mastodonExtra.visibility.name ) Spacer(modifier = Modifier.width(StatusContentDefaults.Mastodon.VisibilitySpacing)) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt index 5358b62f5..279524ade 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt @@ -27,18 +27,12 @@ import android.content.SharedPreferences import android.location.LocationManager import android.net.ConnectivityManager import androidx.core.app.NotificationManagerCompat -import androidx.room.Room import androidx.work.WorkManager -import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 -import com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) @@ -55,21 +49,6 @@ object AndroidModule { fun provideSharedPreference(@ApplicationContext context: Context): SharedPreferences = context.getSharedPreferences("twiderex", Context.MODE_PRIVATE) - @Singleton - @Provides - fun provideCacheDatabase(@ApplicationContext context: Context): RoomCacheDatabase = - Room.databaseBuilder(context, RoomCacheDatabase::class.java, "twiderex-db") - .fallbackToDestructiveMigration() - .build() - - @Singleton - @Provides - fun provideDraftDatabase(@ApplicationContext context: Context): AppDatabase = - Room.databaseBuilder(context, AppDatabase::class.java, "twiderex-draft-db") - .addMigrations(AppDatabase_Migration_1_2) - .addMigrations(AppDatabase_Migration_2_3) - .build() - @Provides fun provideLocationManager(@ApplicationContext context: Context): LocationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt index 45956d7a7..86883f273 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.di import android.content.Context +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.jobs.common.DownloadMediaJob import com.twidere.twiderex.jobs.common.NotificationJob import com.twidere.twiderex.jobs.common.ShareMediaJob @@ -48,7 +49,6 @@ import com.twidere.twiderex.repository.DirectMessageRepository import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.repository.NotificationRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -209,7 +209,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: RoomCacheDatabase, + cacheDatabase: CacheDatabase, ): TwitterDirectMessageSendJob = TwitterDirectMessageSendJob( context = context, accountRepository = accountRepository, @@ -224,7 +224,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: RoomCacheDatabase, + cacheDatabase: CacheDatabase, exifScrambler: ExifScrambler, statusRepository: StatusRepository, remoteNavigator: RemoteNavigator @@ -245,7 +245,7 @@ object JobModule { accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: RoomCacheDatabase, + cacheDatabase: CacheDatabase, exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator ): MastodonComposeJob = MastodonComposeJob( diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index 74c4780fb..60062497d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -20,12 +20,11 @@ */ package com.twidere.twiderex.di -import android.content.Context -import coil.imageLoader -import coil.util.CoilUtils import com.twidere.services.nitter.NitterService +import com.twidere.twiderex.cache.FileCacheHandler import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.repository.AccountUpdateRepository import com.twidere.twiderex.repository.CacheRepository import com.twidere.twiderex.repository.DirectMessageRepository import com.twidere.twiderex.repository.DraftRepository @@ -39,17 +38,20 @@ import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.repository.TimelineRepository import com.twidere.twiderex.repository.TrendRepository import com.twidere.twiderex.repository.UserRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object RepositoryModule { + @Singleton + @Provides + fun provideAccountUpdateRepository(): AccountUpdateRepository = + AccountUpdateRepository() + @Singleton @Provides fun provideDraftRepository(database: AppDatabase): DraftRepository = @@ -61,20 +63,18 @@ object RepositoryModule { @Provides fun provideCacheRepository( - database: RoomCacheDatabase, + database: CacheDatabase, appDatabase: AppDatabase, - @ApplicationContext context: Context, + fileCacheHandler: FileCacheHandler ): CacheRepository = CacheRepository( - database = database, - appDatabase = appDatabase, - cache = CoilUtils.createDefaultCache(context), - imageLoader = context.imageLoader, - cacheDirs = listOf(context.cacheDir, *context.externalCacheDirs), + fileCache = fileCacheHandler, + cacheDatabase = database, + appDatabase = appDatabase ) @Provides fun provideStatusRepository( - database: RoomCacheDatabase, + database: CacheDatabase, nitterService: NitterService?, ): StatusRepository = StatusRepository( database = database, @@ -82,22 +82,22 @@ object RepositoryModule { ) @Provides - fun provideReactionRepository(database: RoomCacheDatabase): ReactionRepository = + fun provideReactionRepository(database: CacheDatabase): ReactionRepository = ReactionRepository(database = database) @Provides - fun provideTimelineRepository(database: RoomCacheDatabase): TimelineRepository = + fun provideTimelineRepository(database: CacheDatabase): TimelineRepository = TimelineRepository(database = database) @Provides fun provideUserRepository( - database: RoomCacheDatabase, - accountRepository: AccountRepository + database: CacheDatabase, + accountRepository: AccountUpdateRepository ): UserRepository = UserRepository(database = database, accountRepository = accountRepository) @Provides fun provideListsRepository( - database: RoomCacheDatabase + database: CacheDatabase ) = ListsRepository(database = database) @Provides @@ -105,21 +105,21 @@ object RepositoryModule { @Provides fun provideNotificationRepository( - database: RoomCacheDatabase, + database: CacheDatabase, ): NotificationRepository = NotificationRepository(database = database) @Provides fun provideTrendRepository( - database: RoomCacheDatabase + database: CacheDatabase ) = TrendRepository(database = database) @Provides fun provideDirectMessageRepository( - database: RoomCacheDatabase + database: CacheDatabase ) = DirectMessageRepository(database = database) @Provides fun provideDirectMediaRepository( - database: RoomCacheDatabase - ) = MediaRepository(database = database) + database: CacheDatabase + ) = MediaRepository(database) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt index de4d70601..651014c70 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -28,6 +28,11 @@ import androidx.work.WorkManager import com.twidere.services.nitter.NitterService import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.action.DirectMessageAction +import com.twidere.twiderex.cache.FileCacheHandler +import com.twidere.twiderex.dataprovider.DataProvider +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.http.TwidereHttpConfigProvider import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver @@ -40,7 +45,6 @@ import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences -import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.utils.PlatformResolver import dagger.Module import dagger.Provides @@ -54,6 +58,22 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object TwidereModule { + @Singleton + @Provides + fun provideDataProvider(): DataProvider = DataProvider.Factory.create() + + @Singleton + @Provides + fun provideCacheDatabase(dataProvider: DataProvider): CacheDatabase = dataProvider.cacheDatabase + + @Singleton + @Provides + fun provideDraftDatabase(dataProvider: DataProvider): AppDatabase = dataProvider.appDatabase + + @Singleton + @Provides + fun provideFileCacheHandler(dataProvider: DataProvider): FileCacheHandler = dataProvider.fileCacheHandler + @Singleton @Provides fun provideComposeQueue( @@ -66,7 +86,7 @@ object TwidereModule { @Singleton @Provides - fun providePlatformResolver(database: RoomCacheDatabase): PlatformResolver = + fun providePlatformResolver(database: CacheDatabase): PlatformResolver = PlatformResolver(database = database) @Provides @@ -107,4 +127,7 @@ object TwidereModule { fun provideRemoteNavigator(@ApplicationContext context: Context): RemoteNavigator = AndroidRemoteNavigator( context = context ) + + @Provides + fun provideTwidereHttpConfigProvider(miscPreferences: DataStore): TwidereHttpConfigProvider = TwidereHttpConfigProvider(miscPreferences) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt index cb4eb8046..6cdc16119 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt @@ -22,13 +22,12 @@ package com.twidere.twiderex.extensions import androidx.paging.Pager import androidx.paging.map -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.room.db.model.DbPagingTimelineWithStatus +import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import kotlinx.coroutines.flow.map -fun Pager.toUi(accountKey: MicroBlogKey) = +fun Pager.toUi() = this.flow.map { pagingData -> pagingData.map { - it.toUi(accountKey = accountKey) + it.status } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index d4e02415e..e047e459d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -138,7 +138,7 @@ class NotificationJob( if (status.mastodonExtra == null || actualStatus == null) { return null } - return when (status.mastodonExtra.type) { + return when (status.mastodonExtra?.type) { MastodonStatusType.Status -> null MastodonStatusType.NotificationFollow -> { NotificationData( @@ -200,6 +200,7 @@ class NotificationJob( ) } MastodonStatusType.NotificationStatus -> null + else -> null } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index 80443796b..c7fbf1c80 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -25,7 +25,8 @@ import com.twidere.services.mastodon.MastodonService import com.twidere.services.mastodon.model.PostPoll import com.twidere.services.mastodon.model.PostStatus import com.twidere.services.mastodon.model.Visibility -import com.twidere.twiderex.db.mapper.toDbStatusWithReference +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator @@ -36,7 +37,6 @@ import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase import java.io.File import java.net.URI @@ -47,7 +47,7 @@ class MastodonComposeJob( exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator, private val fileResolver: FileResolver, - private val cacheDatabase: RoomCacheDatabase, + private val cacheDatabase: CacheDatabase, ) : ComposeJob( context, accountRepository, @@ -61,7 +61,7 @@ class MastodonComposeJob( accountKey: MicroBlogKey, mediaIds: ArrayList ): UiStatus { - val result = service.compose( + return service.compose( PostStatus( status = composeData.content, inReplyToID = if (composeData.composeType == ComposeType.Reply || composeData.composeType == ComposeType.Thread) composeData.statusKey?.id else null, @@ -82,9 +82,9 @@ class MastodonComposeJob( ) } ) - ).toDbStatusWithReference(accountKey) - listOf(result).saveToDb(cacheDatabase) - return result.toUi(accountKey) + ).toUi(accountKey).also { + cacheDatabase.statusDao().insertAll(listOf = listOf(it), accountKey = accountKey) + } } override suspend fun uploadImage( diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 2fe421fd3..00ee18264 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.jobs.compose import android.content.Context import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator @@ -33,7 +34,6 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase class TwitterComposeJob constructor( context: Context, @@ -43,7 +43,7 @@ class TwitterComposeJob constructor( remoteNavigator: RemoteNavigator, private val statusRepository: StatusRepository, private val fileResolver: FileResolver, - private val cacheDatabase: RoomCacheDatabase, + private val cacheDatabase: CacheDatabase, ) : ComposeJob( context, accountRepository, @@ -79,7 +79,7 @@ class TwitterComposeJob constructor( long = long, exclude_reply_user_ids = composeData.excludedReplyUserIds ).toUi(accountKey) - cacheDatabase.statusDao().insertAll(listOf(result)) + cacheDatabase.statusDao().insertAll(listOf = listOf(result), accountKey = accountKey) return result } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index b4fb8050b..5204acfd9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -22,32 +22,27 @@ package com.twidere.twiderex.jobs.dm import android.content.Context import android.graphics.BitmapFactory -import androidx.room.withTransaction import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.R -import com.twidere.twiderex.db.model.DbDMEventWithAttachments -import com.twidere.twiderex.db.model.DbDMEventWithAttachments.Companion.saveToDb +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.job.DirectMessageSendData +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.notification.notificationChannelId import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase -import com.twidere.twiderex.room.db.model.DbDMEvent -import com.twidere.twiderex.room.db.model.DbDMEvent.Companion.saveToDb -import com.twidere.twiderex.room.db.model.DbMedia import java.net.URI -import java.util.UUID abstract class DirectMessageSendJob( private val applicationContext: Context, - protected val cacheDatabase: RoomCacheDatabase, + protected val cacheDatabase: CacheDatabase, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, protected val fileResolver: FileResolver, @@ -62,7 +57,7 @@ abstract class DirectMessageSendJob( @Suppress("UNCHECKED_CAST") val service = accountDetails.service as T - var draftEvent: DbDMEventWithAttachments? = null + var draftEvent: UiDMEvent? = null try { val images = sendData.images draftEvent = getDraft(sendData, images, accountDetails) ?: throw IllegalArgumentException() @@ -83,7 +78,7 @@ abstract class DirectMessageSendJob( e.printStackTrace() draftEvent?.let { cacheDatabase.directMessageDao() - .insertAll(listOf(draftEvent.message.copy(sendStatus = DbDMEvent.SendStatus.FAILED))) + .insertAll(listOf(draftEvent.copy(sendStatus = UiDMEvent.SendStatus.FAILED))) } val builder = AppNotification .Builder( @@ -99,18 +94,18 @@ abstract class DirectMessageSendJob( } } - private suspend fun updateDb(draftEvent: DbDMEventWithAttachments?, dbEvent: DbDMEventWithAttachments) { + private suspend fun updateDb(draftEvent: UiDMEvent?, dbEvent: UiDMEvent) { cacheDatabase.withTransaction { draftEvent?.let { cacheDatabase.directMessageDao().delete( - it.message + it ) } - listOf(dbEvent).saveToDb(cacheDatabase) + cacheDatabase.directMessageDao().insertAll(listOf(dbEvent)) } } - private suspend fun getDraft(sendData: DirectMessageSendData, images: List, account: AccountDetails): DbDMEventWithAttachments? { + private suspend fun getDraft(sendData: DirectMessageSendData, images: List, account: AccountDetails): UiDMEvent? { return cacheDatabase.withTransaction { cacheDatabase.directMessageDao().findWithMessageKey( account.accountKey, @@ -118,18 +113,17 @@ abstract class DirectMessageSendJob( sendData.draftMessageKey )?.also { cacheDatabase.directMessageDao().insertAll( - listOf(it.message.copy(sendStatus = DbDMEvent.SendStatus.PENDING)) + listOf(it.copy(sendStatus = UiDMEvent.SendStatus.PENDING)) ) } } ?: saveDraft(sendData, images, account) } - private suspend fun saveDraft(sendData: DirectMessageSendData, images: List, account: AccountDetails): DbDMEventWithAttachments? { + private suspend fun saveDraft(sendData: DirectMessageSendData, images: List, account: AccountDetails): UiDMEvent? { return cacheDatabase.withTransaction { val createTimeStamp = System.currentTimeMillis() listOf( - DbDMEvent( - _id = UUID.randomUUID().toString(), + UiDMEvent( accountKey = account.accountKey, sortId = createTimeStamp, conversationKey = sendData.conversationKey, @@ -141,27 +135,28 @@ abstract class DirectMessageSendJob( messageType = "message_create", senderAccountKey = account.accountKey, recipientAccountKey = sendData.recipientUserKey, - sendStatus = DbDMEvent.SendStatus.PENDING + sendStatus = UiDMEvent.SendStatus.PENDING, + media = images.mapIndexed { index, uri -> + val imageSize = getImageSize(URI.create(uri).path) + UiMedia( + belongToKey = sendData.draftMessageKey, + url = uri, + mediaUrl = uri, + previewUrl = uri, + type = getMediaType(uri), + width = imageSize[0], + height = imageSize[1], + altText = "", + order = index, + pageUrl = null, + ) + }, + urlEntity = emptyList(), + sender = account.toUi() ) - ).saveToDb(cacheDatabase) - cacheDatabase.mediaDao().insertAll( - images.mapIndexed { index, uri -> - val imageSize = getImageSize(URI.create(uri).path) - DbMedia( - _id = UUID.randomUUID().toString(), - belongToKey = sendData.draftMessageKey, - url = uri.toString(), - mediaUrl = uri.toString(), - previewUrl = uri.toString(), - type = getMediaType(uri), - width = imageSize[0], - height = imageSize[1], - altText = "", - order = index, - pageUrl = null, - ) - } - ) + ).let { + cacheDatabase.directMessageDao().insertAll(it) + } cacheDatabase.directMessageDao().findWithMessageKey( account.accountKey, sendData.conversationKey, @@ -195,7 +190,7 @@ abstract class DirectMessageSendJob( service: T, sendData: DirectMessageSendData, mediaIds: ArrayList - ): DbDMEventWithAttachments + ): UiDMEvent protected abstract suspend fun uploadImage( originUri: String, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt index fe2feb04f..2fc578732 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt @@ -23,24 +23,23 @@ package com.twidere.twiderex.jobs.dm import android.content.Context import com.twidere.services.microblog.LookupService import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.db.mapper.autolink -import com.twidere.twiderex.db.mapper.toDbDirectMessage -import com.twidere.twiderex.db.mapper.toDbUser -import com.twidere.twiderex.db.model.DbDMEventWithAttachments +import com.twidere.twiderex.dataprovider.mapper.autolink +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.DirectMessageSendData +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase -import com.twidere.twiderex.room.db.model.DbUser class TwitterDirectMessageSendJob( context: Context, accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, - cacheDatabase: RoomCacheDatabase, + cacheDatabase: CacheDatabase, ) : DirectMessageSendJob( context, cacheDatabase, accountRepository, notificationManager, fileResolver ) { @@ -49,20 +48,20 @@ class TwitterDirectMessageSendJob( service: TwitterService, sendData: DirectMessageSendData, mediaIds: ArrayList - ): DbDMEventWithAttachments = service.sendDirectMessage( + ): UiDMEvent = service.sendDirectMessage( recipientId = sendData.recipientUserKey.id, text = sendData.text, attachmentType = "media", mediaId = mediaIds.firstOrNull() - )?.toDbDirectMessage( + )?.toUi( accountKey = sendData.accountKey, sender = lookUpUser(cacheDatabase, sendData.accountKey, service) ) ?: throw Error() - private suspend fun lookUpUser(database: RoomCacheDatabase, userKey: MicroBlogKey, service: TwitterService): DbUser { + private suspend fun lookUpUser(database: CacheDatabase, userKey: MicroBlogKey, service: TwitterService): UiUser { return database.userDao().findWithUserKey(userKey) ?: let { val user = (service as LookupService).lookupUser(userKey.id) - .toDbUser(userKey) + .toUi(userKey) database.userDao().insertAll(listOf(user)) user } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt index f5336cff9..8168aea28 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.jobs.status import com.twidere.services.microblog.StatusService -import com.twidere.twiderex.db.mapper.toDbStatusWithReference +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult import com.twidere.twiderex.model.ui.UiStatus @@ -42,8 +42,8 @@ class LikeStatusJob( status: UiStatus ): StatusResult { val newStatus = service.like(status.statusId) - .toDbStatusWithReference(accountKey = accountKey) - .toUi(accountKey = accountKey).let { + .toUi(accountKey = accountKey) + .let { it.retweet ?: it } return StatusResult( diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index 0bc5be565..df88c1392 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -21,15 +21,13 @@ package com.twidere.twiderex.jobs.status import com.twidere.services.mastodon.MastodonService -import com.twidere.services.mastodon.model.Poll -import com.twidere.twiderex.db.model.DbMastodonStatusExtra +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiPoll import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.utils.fromJson -import com.twidere.twiderex.utils.json import com.twidere.twiderex.utils.notify class MastodonVoteJob( @@ -39,7 +37,8 @@ class MastodonVoteJob( ) { suspend fun execute(votes: List, accountKey: MicroBlogKey, statusKey: MicroBlogKey) { val status = statusRepository.loadFromCache(statusKey, accountKey = accountKey) ?: throw Error("Can't find any status matches:$statusKey") - if (status.poll == null || status.platformType != PlatformType.Mastodon) { + val pollId = status.poll?.id + if (status.poll == null || status.platformType != PlatformType.Mastodon || pollId == null) { throw Error() } val service = accountRepository.findByAccountKey(accountKey)?.let { @@ -48,37 +47,27 @@ class MastodonVoteJob( it.service as? MastodonService } ?: throw Error() - val pollId = status.poll.id - var originPoll: Poll? = null + var originPoll: UiPoll? = null statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { + originPoll = it.poll it.copy( - extra = it.extra.fromJson() - .let { extra -> - originPoll = extra.poll - extra.copy( - poll = extra.poll?.copy( - voted = true, - ownVotes = votes - ) - ) - }.json() + poll = it.poll?.copy( + voted = true, + ownVotes = votes + ) ) } try { - val newPoll = service.vote(pollId, votes) + val newPoll = service.vote(pollId, votes).toUi() statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { it.copy( - extra = it.extra.fromJson().copy( - poll = newPoll - ).json() + poll = newPoll ) } } catch (e: Throwable) { statusRepository.updateStatus(statusKey = status.statusKey, accountKey = accountKey) { it.copy( - extra = it.extra.fromJson().copy( - poll = originPoll - ).json() + poll = originPoll ) } e.notify(inAppNotification) diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt index 2d2a0480e..0b0f8cdb9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.jobs.status import com.twidere.services.microblog.StatusService -import com.twidere.twiderex.db.mapper.toDbStatusWithReference +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult import com.twidere.twiderex.model.ui.UiStatus @@ -42,7 +42,6 @@ class RetweetStatusJob( status: UiStatus ): StatusResult { val newStatus = service.retweet(status.statusId) - .toDbStatusWithReference(accountKey = accountKey) .toUi(accountKey = accountKey).let { it.retweet ?: it } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt index 7585633df..a51c00a2d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.jobs.status import com.twidere.services.microblog.StatusService -import com.twidere.twiderex.db.mapper.toDbStatusWithReference +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult import com.twidere.twiderex.model.ui.UiStatus @@ -42,7 +42,6 @@ class UnRetweetStatusJob( status: UiStatus ): StatusResult { val newStatus = service.unRetweet(status.statusId) - .toDbStatusWithReference(accountKey = accountKey) .toUi(accountKey = accountKey).let { it.retweet ?: it } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt index 4e53ca55d..f4f142e7c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.jobs.status import com.twidere.services.microblog.StatusService -import com.twidere.twiderex.db.mapper.toDbStatusWithReference +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult import com.twidere.twiderex.model.ui.UiStatus @@ -42,7 +42,6 @@ class UnlikeStatusJob( status: UiStatus ): StatusResult { val newStatus = service.unlike(status.statusId) - .toDbStatusWithReference(accountKey = accountKey) .toUi(accountKey = accountKey).let { it.retweet ?: it } diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt index 972776418..39a1642d8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -157,10 +157,11 @@ fun ProvideStatusPlatform( content: @Composable (platformType: PlatformType) -> Unit, ) { val platformResolver = LocalPlatformResolver.current + val account = LocalActiveAccount.current ?: return ProvidePlatformType( key = statusKey, provider = { - platformResolver.resolveStatus(statusKey = statusKey) + platformResolver.resolveStatus(statusKey = statusKey, account.accountKey) }, content = content ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index 494f6bb72..62876791c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -90,7 +90,7 @@ fun DraftListSceneContent( LazyColumn( state = listState ) { - items(items = source, key = { it._id.hashCode() }) { + items(items = source, key = { it.draftId.hashCode() }) { ListItem( text = { Text(text = it.content) @@ -112,7 +112,7 @@ fun DraftListSceneContent( ) { DropdownMenuItem( onClick = { - navController.navigate(RootRoute.Draft.Compose(it._id)) + navController.navigate(RootRoute.Draft.Compose(it.draftId)) } ) { Text(text = stringResource(id = R.string.scene_drafts_actions_edit_draft)) diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt index 6b4f35d28..576ffac76 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.utils import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.ui.UiEmojiCategory import kotlinx.coroutines.flow.Flow diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt index 6ef89d020..a1de8bb7d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt @@ -21,16 +21,16 @@ package com.twidere.twiderex.utils import androidx.compose.runtime.staticCompositionLocalOf +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.room.db.RoomCacheDatabase import javax.inject.Inject class PlatformResolver @Inject constructor( - private val database: RoomCacheDatabase, + private val database: CacheDatabase, ) { - suspend fun resolveStatus(statusKey: MicroBlogKey): PlatformType? { - return database.statusDao().findWithStatusKey(statusKey = statusKey)?.platformType + suspend fun resolveStatus(statusKey: MicroBlogKey, accountKey: MicroBlogKey): PlatformType? { + return database.statusDao().findWithStatusKey(statusKey = statusKey, accountKey = accountKey)?.platformType } suspend fun resolveUser(userKey: MicroBlogKey): PlatformType? { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index 03da25cae..b86a758fb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.ViewModel import androidx.work.WorkManager -import com.twidere.twiderex.db.model.DbDraft +import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.worker.draft.RemoveDraftWorker import dagger.assisted.AssistedInject @@ -39,9 +39,9 @@ class DraftViewModel @AssistedInject constructor( fun create(): DraftViewModel } - fun delete(it: DbDraft) { - workManager.beginWith(RemoveDraftWorker.create(it._id)).enqueue() - notificationManagerCompat.cancel(it._id.hashCode()) + fun delete(it: UiDraft) { + workManager.beginWith(RemoveDraftWorker.create(it.draftId)).enqueue() + notificationManagerCompat.cancel(it.draftId.hashCode()) } val source by lazy { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 57a7d1848..58ac50a69 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -38,7 +38,6 @@ import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R import com.twidere.twiderex.action.ComposeAction -import com.twidere.twiderex.db.model.DbDraft import com.twidere.twiderex.extensions.getCachedLocation import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection @@ -48,6 +47,7 @@ import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData +import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.model.ui.UiEmoji import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification @@ -97,7 +97,7 @@ class DraftComposeViewModel @AssistedInject constructor( workManager: WorkManager, inAppNotification: InAppNotification, @Assisted account: AccountDetails, - @Assisted private val draft: DbDraft, + @Assisted private val draft: UiDraft, ) : ComposeViewModel( draftRepository, locationManager, @@ -111,7 +111,7 @@ class DraftComposeViewModel @AssistedInject constructor( draft.composeType, ) { - override val draftId: String = draft._id + override val draftId: String = draft.draftId init { setText(TextFieldValue(draft.content)) @@ -123,7 +123,7 @@ class DraftComposeViewModel @AssistedInject constructor( interface AssistedFactory { fun create( account: AccountDetails, - draft: DbDraft, + draft: UiDraft, ): DraftComposeViewModel } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index af3b47656..19247b596 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -25,7 +25,8 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.twidere.services.mastodon.MastodonOAuthService -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.mapper.toAmUser +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType @@ -105,7 +106,7 @@ class MastodonSignInViewModel @AssistedInject constructor( credentials_type = CredentialsType.OAuth2, credentials_json = credentials_json, extras_json = "", - user = user.toDbUser(accountKey = internalKey).toAmUser(), + user = user.toUi(accountKey = internalKey).toAmUser(), lastActive = System.currentTimeMillis() ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 6c63fc312..ad8ef8f18 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.viewmodel.search import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.twidere.twiderex.db.model.DbSearch import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.SearchRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -50,7 +50,7 @@ class SearchInputViewModel @AssistedInject constructor( val expandSearch = MutableStateFlow(false) - fun remove(item: DbSearch) = viewModelScope.launch { + fun remove(item: UiSearch) = viewModelScope.launch { repository.remove(item) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index cbac25759..dfbe9eed6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -25,16 +25,16 @@ import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map class SearchTweetsViewModel @AssistedInject constructor( - val database: RoomCacheDatabase, + val database: CacheDatabase, @Assisted private val account: AccountDetails, @Assisted keyword: String, ) : ViewModel() { @@ -49,6 +49,6 @@ class SearchTweetsViewModel @AssistedInject constructor( val source by lazy { SearchStatusMediator(keyword, database, account.accountKey, service).pager() - .flow.map { it.map { it.status.toUi(account.accountKey) } }.cachedIn(viewModelScope) + .flow.map { it.map { it.status } }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 328474f49..b89573308 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class HomeTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: RoomCacheDatabase, + database: CacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 49fb2c7a9..ae03bd1eb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -22,18 +22,18 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.NotificationRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class MentionsTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: RoomCacheDatabase, + database: CacheDatabase, notificationRepository: NotificationRepository, @Assisted private val account: AccountDetails ) : TimelineViewModel(preferences) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 77c6fccde..0361cba34 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -22,18 +22,18 @@ package com.twidere.twiderex.viewmodel.timeline import android.content.SharedPreferences import com.twidere.services.microblog.NotificationService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.NotificationRepository -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject class NotificationTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: RoomCacheDatabase, + database: CacheDatabase, notificationRepository: NotificationRepository, @Assisted private val account: AccountDetails ) : TimelineViewModel(preferences) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 6f04885a3..0a3768faa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -38,7 +38,7 @@ abstract class TimelineViewModel( private val preferences: SharedPreferences, ) : ViewModel() { val source by lazy { - pagingMediator.pager().toUi(accountKey = pagingMediator.accountKey).cachedIn(viewModelScope) + pagingMediator.pager().toUi().cachedIn(viewModelScope) } abstract val pagingMediator: PagingWithGapMediator abstract val savedStateKey: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 36e866900..8990510e0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline.mastodon import android.content.SharedPreferences import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedInject class FederatedTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: RoomCacheDatabase, + database: CacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 4834bad03..8b56dc661 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -22,16 +22,16 @@ package com.twidere.twiderex.viewmodel.timeline.mastodon import android.content.SharedPreferences import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedInject class LocalTimelineViewModel @AssistedInject constructor( preferences: SharedPreferences, - database: RoomCacheDatabase, + database: CacheDatabase, @Assisted account: AccountDetails, ) : TimelineViewModel(preferences) { @dagger.assisted.AssistedFactory diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index fe693921e..421fa7171 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -27,7 +27,8 @@ import com.twidere.services.http.MicroBlogException import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.BuildConfig -import com.twidere.twiderex.db.mapper.toDbUser +import com.twidere.twiderex.dataprovider.mapper.toAmUser +import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType @@ -137,7 +138,7 @@ class TwitterSignInViewModel @AssistedInject constructor( credentials_type = CredentialsType.OAuth, credentials_json = credentials_json, extras_json = "", - user = user.toDbUser().toAmUser(), + user = user.toUi(accountKey = internalKey).toAmUser(), lastActive = System.currentTimeMillis() ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 9755357a6..f06c47e6a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -26,16 +26,16 @@ import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map class TwitterSearchMediaViewModel @AssistedInject constructor( - val database: RoomCacheDatabase, + val database: CacheDatabase, @Assisted private val account: AccountDetails, @Assisted keyword: String, ) : ViewModel() { @@ -49,7 +49,7 @@ class TwitterSearchMediaViewModel @AssistedInject constructor( } val source by lazy { SearchMediaMediator(keyword, database, account.accountKey, service).pager() - .flow.map { it.map { it.status.toUi(account.accountKey) } }.cachedIn(viewModelScope) + .flow.map { it.map { it.status } }.cachedIn(viewModelScope) .map { it.flatMap { it.media.map { media -> media to it } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 3ccdb9721..4a874610c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -28,6 +28,7 @@ import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia @@ -35,14 +36,13 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.PagingMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.user.UserMediaMediator -import com.twidere.twiderex.room.db.RoomCacheDatabase import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map class UserMediaTimelineViewModel @AssistedInject constructor( - database: RoomCacheDatabase, + database: CacheDatabase, @Assisted account: AccountDetails, @Assisted userKey: MicroBlogKey, ) : ViewModel() { @@ -64,7 +64,7 @@ class UserMediaTimelineViewModel @AssistedInject constructor( ) ).flow.map { pagingData -> pagingData.map { - it.toUi(pagingMediator.accountKey) + it.status } }.cachedIn(viewModelScope).map { it.flatMap { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 194c7b098..4f917f2a9 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -59,6 +59,7 @@ kotlin { implementation("androidx.room:room-ktx:${Versions.room}") implementation("androidx.room:room-paging:${Versions.room}") kapt("androidx.room:room-compiler:${Versions.room}") + implementation("io.coil-kt:coil-base:${Versions.coil}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index 311f2bc87..47a207eca 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -20,23 +20,40 @@ */ package com.twidere.twiderex.dataprovider +import android.content.Context +import androidx.room.Room import com.twidere.twiderex.cache.FileCacheHandler +import com.twidere.twiderex.dataprovider.cache.FileCacheHandlerImpl +import com.twidere.twiderex.dataprovider.db.AppDatabaseImpl +import com.twidere.twiderex.dataprovider.db.CacheDatabaseImpl import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.di.ext.get +import com.twidere.twiderex.room.db.AppDatabase_Migration_1_2 +import com.twidere.twiderex.room.db.AppDatabase_Migration_2_3 +import com.twidere.twiderex.room.db.RoomAppDatabase +import com.twidere.twiderex.room.db.RoomCacheDatabase -actual class DataProvider { +actual class DataProvider private constructor(context: Context) { // data provide functions.... actual companion object Factory { actual fun create(): DataProvider { - TODO("Not yet implemented") + return DataProvider(get()) } } - actual val appDatabase: AppDatabase - get() = TODO("Not yet implemented") + private val roomCacheDatabase = Room.databaseBuilder(context, RoomCacheDatabase::class.java, "twiderex-db") + .fallbackToDestructiveMigration() + .build() - actual val cacheDatabase: CacheDatabase - get() = TODO("Not yet implemented") - actual val fileCacheHandler: FileCacheHandler - get() = TODO("Not yet implemented") + private val roomAppDatabase = Room.databaseBuilder(context, RoomAppDatabase::class.java, "twiderex-draft-db") + .addMigrations(AppDatabase_Migration_1_2) + .addMigrations(AppDatabase_Migration_2_3) + .build() + + actual val appDatabase: AppDatabase = AppDatabaseImpl(roomAppDatabase) + + actual val cacheDatabase: CacheDatabase = CacheDatabaseImpl(roomCacheDatabase) + + actual val fileCacheHandler: FileCacheHandler = FileCacheHandlerImpl(context) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/cache/FileCacheHandlerImpl.kt similarity index 51% rename from common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/cache/FileCacheHandlerImpl.kt index c793321e8..f1a185706 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/cache/FileCacheHandlerImpl.kt @@ -18,14 +18,28 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.dataprovider.cache -import com.twidere.twiderex.dataprovider.DataProvider -import com.twidere.twiderex.repository.CacheRepository -import com.twidere.twiderex.repository.MediaRepository -import org.koin.dsl.module +import android.content.Context +import coil.util.CoilUtils +import com.twidere.twiderex.cache.FileCacheHandler -internal val repositoryModules = module { - factory { MediaRepository(get().cacheDatabase.mediaDao()) } - factory { CacheRepository(get(), get(), get()) } +internal class FileCacheHandlerImpl(context: Context) : FileCacheHandler { + private val cache = CoilUtils.createDefaultCache(context) + private val cacheDirs = listOf(context.cacheDir, *context.externalCacheDirs) + override fun getCache() = cache + + override fun getCacheDirs() = cacheDirs + + override fun clearMediaCaches() { + cache.directory.deleteRecursively() + } + + override fun clearFileCaches() { + cacheDirs.forEach { + it.listFiles()?.forEach { file -> + file.deleteRecursively() + } + } + } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt index 88d46e588..4aad0a455 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt @@ -23,11 +23,11 @@ package com.twidere.twiderex.room.db.transform import android.accounts.Account import com.twidere.twiderex.model.TwidereAccount -internal fun Account.toTwidere() = TwidereAccount( +fun Account.toTwidere() = TwidereAccount( name = name, type = type ) -internal fun TwidereAccount.toAndroid() = Account( +fun TwidereAccount.toAndroid() = Account( name, type ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt index c7ffe17dc..c72a96165 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.room.db.transform import com.twidere.services.mastodon.model.Emoji -import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.model.ui.UiUser @@ -37,24 +36,6 @@ import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import java.util.UUID -internal fun DbUser.toAmUser() = - AmUser( - userId = userId, - name = name, - userKey = userKey, - screenName = screenName, - profileImage = profileImage, - profileBackgroundImage = profileBackgroundImage, - followersCount = followersCount, - friendsCount = friendsCount, - listedCount = listedCount, - desc = rawDesc, - website = website, - location = location, - verified = verified, - isProtected = isProtected, - ) - internal fun DbUser.toUi() = UiUser( id = userId, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt index 4e8ee69cb..96ce2b81e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/cache/FileCacheHandler.kt @@ -20,7 +20,14 @@ */ package com.twidere.twiderex.cache +import okhttp3.Cache +import java.io.File + interface FileCacheHandler { + fun getCache(): Cache + + fun getCacheDirs(): List + fun clearMediaCaches() fun clearFileCaches() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt index b62504b5d..554915971 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt @@ -29,6 +29,7 @@ import com.twidere.services.microblog.model.ITrend import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.TwitterList +import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiEmoji import com.twidere.twiderex.model.ui.UiEmojiCategory @@ -124,3 +125,21 @@ fun List.toUi(): List = groupBy({ it.category }, { it }) } ) } + +fun UiUser.toAmUser() = + AmUser( + userId = id, + name = name, + userKey = userKey, + screenName = screenName, + profileImage = profileImage.toString(), + profileBackgroundImage = profileBackgroundImage, + followersCount = metrics.fans, + friendsCount = metrics.follow, + listedCount = metrics.listed, + desc = rawDesc, + website = website, + location = location, + verified = verified, + isProtected = protected, + ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index 77d6dccc7..3ad810992 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -26,6 +26,7 @@ import com.twidere.services.mastodon.model.MastodonList import com.twidere.services.mastodon.model.Mention import com.twidere.services.mastodon.model.Notification import com.twidere.services.mastodon.model.NotificationTypes +import com.twidere.services.mastodon.model.Poll import com.twidere.services.mastodon.model.Status import com.twidere.services.mastodon.model.Trend import com.twidere.services.mastodon.model.Visibility @@ -208,24 +209,7 @@ internal fun Status.toUiStatus( sensitive = sensitive ?: false, platformType = PlatformType.Mastodon, spoilerText = spoilerText?.takeIf { it.isNotEmpty() }, - poll = poll?.let { - UiPoll( - id = it.id ?: "", - options = it.options?.map { option -> - Option( - text = option.title ?: "", - count = option.votesCount ?: 0 - ) - } ?: emptyList(), - expiresAt = it.expiresAt?.time, - expired = it.expired ?: false, - multiple = it.multiple ?: false, - voted = it.voted ?: false, - votesCount = it.votesCount ?: 0, - votersCount = it.votersCount ?: 0, - ownVotes = it.ownVotes - ) - }, + poll = poll?.toUi(), extra = MastodonStatusExtra( type = MastodonStatusType.Status, emoji = emojis?.toUi() ?: emptyList(), @@ -282,6 +266,23 @@ internal fun Status.toUiStatus( ) } +fun Poll.toUi() = UiPoll( + id = id ?: "", + options = options?.map { option -> + Option( + text = option.title ?: "", + count = option.votesCount ?: 0 + ) + } ?: emptyList(), + expiresAt = expiresAt?.time, + expired = expired ?: false, + multiple = multiple ?: false, + voted = voted ?: false, + votesCount = votesCount ?: 0, + votersCount = votersCount ?: 0, + ownVotes = ownVotes +) + internal fun Account.toUiUser( accountKey: MicroBlogKey ): UiUser { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index 6882bb29d..19e037a1e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -56,10 +56,10 @@ import com.twitter.twittertext.Autolink val autolink by lazy { Autolink().apply { setUsernameIncludeSymbol(true) - // TODO migrate Route hashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" cashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" usernameUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" + TODO("migrate Route") } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index 9f6a832f0..cedfe934e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -25,5 +25,4 @@ import org.koin.core.KoinApplication fun KoinApplication.setupModules() { modules(twidereModules) modules(preferencesModule) - modules(repositoryModules) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt index 2c3cde996..a2f982eb9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt @@ -28,14 +28,21 @@ import org.koin.core.qualifier.Qualifier import org.koin.mp.KoinPlatformTools @Composable -inline fun get( +inline fun getRemember( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null, ): T = remember(qualifier, parameters) { - KoinPlatformTools.defaultContext().get().get(qualifier, parameters) + get(qualifier, parameters) } @Composable -fun getKoin(): Koin = remember { - KoinPlatformTools.defaultContext().get() +fun getKoinRemember(): Koin = remember { + getKoin() } + +inline fun get( + qualifier: Qualifier? = null, + noinline parameters: ParametersDefinition? = null, +): T = KoinPlatformTools.defaultContext().get().get(qualifier, parameters) + +fun getKoin(): Koin = KoinPlatformTools.defaultContext().get() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 7946f5a2b..85853bc87 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -33,11 +33,11 @@ data class UiMedia( val mediaUrl: String?, val previewUrl: Any?, val type: MediaType, - val width: Long, - val height: Long, - val pageUrl: String?, - val altText: String, - val order: Int, + val width: Long = 0, + val height: Long = 0, + val pageUrl: String? = "", + val altText: String = "", + val order: Int = 0, ) { val fileName: String? get() = mediaUrl?.takeIfFileName() ?: url?.takeIfFileName() ?: findFileName() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt index 2a566dfe8..637a01740 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt @@ -19,11 +19,11 @@ * along with Twidere X. If not, see . */ package com.twidere.twiderex.repository -import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey -class MediaRepository(private val mediaDao: MediaDao) { +class MediaRepository(private val cacheDatabase: CacheDatabase) { suspend fun findMediaByBelongToKey( belongToKey: MicroBlogKey - ) = mediaDao.findMediaByBelongToKey(belongToKey) + ) = cacheDatabase.mediaDao().findMediaByBelongToKey(belongToKey) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt index cac90adff..5aca7340a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/cache/MockFileCacheHandler.kt @@ -21,11 +21,21 @@ package com.twidere.twiderex.mock.cache import com.twidere.twiderex.cache.FileCacheHandler +import okhttp3.Cache +import java.io.File internal class MockFileCacheHandler( private val mediaCache: MutableList, private val fileCache: MutableList, ) : FileCacheHandler { + override fun getCache(): Cache { + TODO("Not yet implemented") + } + + override fun getCacheDirs(): List { + TODO("Not yet implemented") + } + override fun clearMediaCaches() { mediaCache.clear() } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index b0154003e..6d5406777 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -47,8 +47,9 @@ internal class MockCacheDatabase @TestOnly constructor() : CacheDatabase { return statusDao } + private val mediaDao = MockMediaDao() override fun mediaDao(): MediaDao { - return MockMediaDao() + return mediaDao } private val userDao = MockUserDao() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt index 70fbf8f46..b2be66a2c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.repository +import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.db.dao.MockMediaDao import com.twidere.twiderex.mock.model.mockUiMedia import com.twidere.twiderex.model.MicroBlogKey @@ -30,9 +31,7 @@ internal class MediaRepositoryTest { @Test fun findMediasWithBelongToKey() = runBlocking { val repository = MediaRepository( - MockMediaDao().apply { - initData(listOf(mockUiMedia("test", MicroBlogKey.twitter("account")))) - } + MockCacheDatabase().apply { (mediaDao() as MockMediaDao).initData(listOf(mockUiMedia("test", MicroBlogKey.twitter("account")))) } ) val result = repository.findMediaByBelongToKey(MicroBlogKey.twitter("account")) assert(result.isNotEmpty()) From d0e554bd7d16e982045241c5b6f82e921c832d21 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 13:01:45 +0800 Subject: [PATCH 082/615] migrate root and rootDeepLinks to commonMain --- .../main/kotlin/com/twidere/twiderex/navigation/Route.kt | 1 - common/build.gradle.kts | 8 ++++++++ .../com/twidere/twiderex/dataprovider/mapper/Twitter.kt | 9 ++++----- .../kotlin/com/twidere/twiderex/navigation/Root.kt | 0 .../com/twidere/twiderex/navigation/RootDeepLinks.kt | 2 ++ 5 files changed, 14 insertions(+), 6 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/navigation/Root.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt (97%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt index 39a1642d8..0345ad640 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -102,7 +102,6 @@ import moe.tlaster.precompose.navigation.transition.fadeScaleDestroyTransition import java.net.URLDecoder const val initialRoute = RootRouteDefinition.Home -const val twidereXSchema = "twiderex" fun RouteBuilder.authorizedScene( route: String, diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 4f917f2a9..62a95f216 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,4 @@ +import Versions.ksp import org.jetbrains.compose.compose plugins { @@ -6,6 +7,7 @@ plugins { kotlin("plugin.serialization") version Versions.Kotlin.lang id("com.android.library") kotlin("kapt") + id("com.google.devtools.ksp").version(Versions.ksp) } group = Package.group @@ -40,6 +42,8 @@ kotlin { api("io.insert-koin:koin-core:${Versions.koin}") implementation("com.twitter.twittertext:twitter-text:3.1.0") implementation("org.jsoup:jsoup:1.13.1") + implementation(projects.routeProcessor) + ksp(projects.routeProcessor) } } val commonTest by getting { @@ -82,6 +86,10 @@ fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kapt(dependencyNo configurations["kapt"].dependencies.add(project.dependencies.create(dependencyNotation)) } +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.ksp(dependencyNotation: org.gradle.accessors.dm.RouteProcessorProjectDependency) { + configurations["ksp"].dependencies.add(projects.routeProcessor) +} + android { compileSdk = AndroidSdk.compile sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index 19e037a1e..bcb5bf8d1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -50,16 +50,15 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.model.ui.UserMetrics import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra -// import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition +import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition import com.twitter.twittertext.Autolink val autolink by lazy { Autolink().apply { setUsernameIncludeSymbol(true) - hashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" - cashtagUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" - usernameUrlBase = "" // "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" - TODO("migrate Route") + hashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" + cashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" + usernameUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt index a8e3e9301..40163b265 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt @@ -28,6 +28,8 @@ import com.twidere.twiderex.model.enums.ComposeType * if deeplink has the same parameters with route in Root.kt, * make it's name the same to route parameters in Root.kt too */ +const val twidereXSchema = "twiderex" + @AppRoute( schema = twidereXSchema ) From f52fd002a6a91d2ff9790c60b4e0d7943622ab33 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 13:11:27 +0800 Subject: [PATCH 083/615] fixed room dao function invoke in main thread problem --- .../twiderex/room/db/dao/RoomDirectMessageConversationDao.kt | 2 +- .../twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt | 2 +- .../com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt | 2 +- .../com/twidere/twiderex/room/db/paging/DbPagingSource.kt | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index 7656e6fdb..ea1f35562 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -57,7 +57,7 @@ internal interface RoomDirectMessageConversationDao { LIMIT :limit OFFSET :offset """ ) - fun getPagingList( + suspend fun getPagingList( accountKey: MicroBlogKey, limit: Int, offset: Int diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index 30a707714..547e46e94 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -49,7 +49,7 @@ internal interface RoomDirectMessageEventDao { @Transaction @Query("SELECT * FROM dm_event WHERE accountKey == :accountKey AND conversationKey == :conversationKey ORDER BY sortId DESC LIMIT :limit OFFSET :offset") - fun getPagingList( + suspend fun getPagingList( accountKey: MicroBlogKey, conversationKey: MicroBlogKey, limit: Int, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index f3cbb3647..c409d0f35 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -50,7 +50,7 @@ internal interface RoomListsDao { @Transaction @Query("SELECT * FROM lists WHERE accountKey == :accountKey LIMIT :limit OFFSET :offset") - fun getPagingList( + suspend fun getPagingList( accountKey: MicroBlogKey, limit: Int, offset: Int diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index 32fc4be66..60106c884 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -44,7 +44,7 @@ internal interface RoomPagingTimelineDao { @Transaction @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY sortId DESC LIMIT :limit OFFSET :offset") - fun getPagingList( + suspend fun getPagingList( pagingKey: String, accountKey: MicroBlogKey, limit: Int, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index 2eb29f303..3656d337c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -40,7 +40,7 @@ internal interface RoomTrendDao { @Transaction @Query("SELECT * FROM trends WHERE accountKey == :accountKey LIMIT :limit OFFSET :offset") - fun getPagingList( + suspend fun getPagingList( accountKey: MicroBlogKey, limit: Int, offset: Int diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt index c8831feb4..459005ca9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt @@ -56,6 +56,7 @@ internal class DbPagingSource( nextKey = nextKey ) } catch (e: Throwable) { + e.printStackTrace() LoadResult.Error(e) } } From 8b474b6e6ceafe2f76af56a2bd543afe1d113f55 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 15:34:29 +0800 Subject: [PATCH 084/615] fixed DbPagingTimeline toUi missing reference problem --- .../kotlin/com/twidere/twiderex/utils/Json.kt | 39 ------------------- ...plTest.kt => PagingTimelineDaoImplTest.kt} | 20 +++++++++- .../room/db/transform/StatusTransform.kt | 12 +++--- .../kotlin/com/twidere/twiderex/utils/Json.kt | 9 +++-- 4 files changed, 30 insertions(+), 50 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt rename common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/{PagingDaoImplTest.kt => PagingTimelineDaoImplTest.kt} (87%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt deleted file mode 100644 index 3539babb5..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/Json.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils - -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json - -private val JSON by lazy { - Json { - ignoreUnknownKeys = true - isLenient = true - coerceInputValues = true - } -} - -internal inline fun T.json(): String = - JSON.encodeToString(this) - -internal inline fun String.fromJson() = - JSON.decodeFromString(this) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt similarity index 87% rename from common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt rename to common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt index 92973f387..7c430358f 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt @@ -32,7 +32,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull -internal class PagingDaoImplTest : CacheDatabaseDaoTest() { +internal class PagingTimelineDaoImplTest : CacheDatabaseDaoTest() { private val accountKey = MicroBlogKey.twitter("account") private val pagingKey = "pagingKey" @Test @@ -84,6 +84,24 @@ internal class PagingDaoImplTest : CacheDatabaseDaoTest() { } assert(invalidate) } + @Test + fun getLatest_ReturnsResultWithStatusAndReference() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus(hasReference = true, hasMedia = true).toPagingTimeline(accountKey, pagingKey), + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData.map { it.timeline } + ) + cacheDatabase.statusDao().insertAll(listOf = originData.map { it.status }, accountKey = accountKey) + assertEquals( + originData.first().status.referenceStatus.values.first().statusKey, + cacheDatabase.pagingTimelineDao().getLatest( + pagingKey = pagingKey, + accountKey = accountKey + )?.status?.referenceStatus?.values?.first()?.statusKey, + ) + } @Test fun getLatest_ReturnsResultWithMaxValueOfSortId() = runBlocking { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt index 6c976b587..fcbd52339 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt @@ -252,12 +252,12 @@ private fun DbPoll.toUi() = UiPoll( ownVotes = ownVotes ) -internal fun DbPagingTimelineWithStatus.toPagingTimeline(accountKey: MicroBlogKey) = with(status) { - PagingTimeLineWithStatus( - timeline = timeline.toUi(), - status = status.toUi(accountKey = accountKey) - ) -} +internal fun DbPagingTimelineWithStatus.toPagingTimeline( + accountKey: MicroBlogKey +) = PagingTimeLineWithStatus( + timeline = timeline.toUi(), + status = status.toUi(accountKey = accountKey) +) internal fun DbPagingTimeline.toUi() = PagingTimeLine( accountKey = accountKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt index 3539babb5..8227b0a96 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt @@ -24,16 +24,17 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -private val JSON by lazy { +// TODO Make it private after all android code migrate to common +val JSON by lazy { Json { ignoreUnknownKeys = true isLenient = true coerceInputValues = true } } - -internal inline fun T.json(): String = +// TODO Make it internal after all android code migrate to common +inline fun T.json(): String = JSON.encodeToString(this) -internal inline fun String.fromJson() = +inline fun String.fromJson() = JSON.decodeFromString(this) From 91517564bc123761f798eb02ea3faf633f5beb95 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 16:09:15 +0800 Subject: [PATCH 085/615] add ci for common test --- .github/workflows/common.yml | 96 ++++++++++++++++++ common/build.gradle.kts | 2 - .../paging/crud/PagingMemoryCacheTest.kt | 97 ++++++++++--------- .../paging/lists/ListsMediatorTest.kt | 3 - .../paging/PagingWithGapMediatorTest.kt | 2 + 5 files changed, 147 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/common.yml diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml new file mode 100644 index 000000000..24c3e5161 --- /dev/null +++ b/.github/workflows/common.yml @@ -0,0 +1,96 @@ +name: Common CI + +on: + push: + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + + +jobs: + unit-test: + runs-on: ubuntu-latest + needs: build + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Set up Android SDK License + run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses + + - name: Build with Gradle + run: ./gradlew :common:test + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: unit-test-result + path: "**/build/test-results/**/*.xml" + + connected-android-test: + runs-on: macOS-11 + needs: build + timeout-minutes: 60 + + strategy: + # Allow tests to continue on other devices if they fail on one device. + fail-fast: false + matrix: + include: + - api-level: 29 + target: default + - api-level: 28 + target: default + - api-level: 26 + target: default + - api-level: 24 + target: default + - api-level: 22 + target: default + + steps: + - uses: actions/checkout@v2 + + - name: set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Set up Android SDK License + run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses + + - name: Build tests + run: ./gradlew :common:assembleAndroidTest + + - name: Run tests + uses: reactivecircus/android-emulator-runner@v2 + timeout-minutes: 40 + with: + api-level: ${{ matrix.api-level }} + profile: 4in WVGA (Nexus S) + target: ${{ matrix.target }} + script: | + adb logcat > logcat.txt & + ./gradlew :common:connectedCheck + + - name: Upload logs + if: always() + uses: actions/upload-artifact@v2 + with: + name: logs-${{ matrix.api-level }}-${{ matrix.target }} + path: logcat.txt + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v2 + with: + name: test-results-${{ matrix.api-level }}-${{ matrix.target }} + path: "**/build/outputs/**/connected/**/*.xml" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 62a95f216..63388f9c1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -51,8 +51,6 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") - implementation("org.mockito:mockito-core:3.11.2") - implementation("org.mockito.kotlin:mockito-kotlin:3.2.0") } } val androidMain by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index 780e000ae..d1d2badfc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -20,35 +20,13 @@ */ package com.twidere.twiderex.paging.crud -import org.junit.After -import org.junit.Before import org.junit.Test -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.times -import org.mockito.kotlin.verify import kotlin.test.assertEquals class PagingMemoryCacheTest { - private lateinit var mocks: AutoCloseable - private val pagingMemoryCache = PagingMemoryCache() - @Mock - private lateinit var mockObserver: OnInvalidateObserver - - @Before - fun setUp() { - mocks = MockitoAnnotations.openMocks(this) - pagingMemoryCache.addWeakObserver(mockObserver) - } - - @After - fun tearDown() { - mocks.close() - } - @Test fun find() { pagingMemoryCache.insert(listOf("1", "2", "3", "4")) @@ -63,55 +41,78 @@ class PagingMemoryCacheTest { assertEquals(3, pagingMemoryCache.find(0, 3).size) } + private fun PagingMemoryCache.verifyInvalidate( times: Int, block: () -> Unit) { + var invalidateCount = 0 + val observer = object : OnInvalidateObserver { + override fun onInvalidate() { + invalidateCount++ + } + } + addWeakObserver(observer) + block.invoke() + val start = System.currentTimeMillis() + while (invalidateCount < times && System.currentTimeMillis() - start < 3000) { + continue + } + unRegister(observer) + assertEquals(times, invalidateCount) + } + @Test fun insert_NotifyObserverAfterInsertSuccess() { - pagingMemoryCache.insert(listOf("1")) - verify(mockObserver, times(1)).onInvalidate() + pagingMemoryCache.verifyInvalidate(1) { + pagingMemoryCache.insert(listOf("1")) + } } @Test fun insert_SilenceAfterInsertFailed() { - pagingMemoryCache.insert(listOf()) - verify(mockObserver, times(0)).onInvalidate() + pagingMemoryCache.verifyInvalidate(0) { + pagingMemoryCache.insert(listOf()) + } } @Test fun update_NotifyObserverAfterDeleteSuccess() { - pagingMemoryCache.insert(listOf("1")) - pagingMemoryCache.update( - "2", - object : Comparable { - override fun compareTo(other: String): Int { - return if (other == "1") 0 else 1 + pagingMemoryCache.verifyInvalidate(2) { + pagingMemoryCache.insert(listOf("1")) + pagingMemoryCache.update( + "2", + object : Comparable { + override fun compareTo(other: String): Int { + return if (other == "1") 0 else 1 + } } - } - ) - verify(mockObserver, times(2)).onInvalidate() + ) + } } @Test fun update_SilenceAfterUpdateFailed() { - pagingMemoryCache.update( - "listOf()", - object : Comparable { - override fun compareTo(other: String): Int { - return 1 + pagingMemoryCache.verifyInvalidate(0) { + pagingMemoryCache.update( + "listOf()", + object : Comparable { + override fun compareTo(other: String): Int { + return 1 + } } - } - ) - verify(mockObserver, times(0)).onInvalidate() + ) + } } @Test fun delete_NotifyObserverAfterDeleteSuccess() { - pagingMemoryCache.insert(listOf("1")) - pagingMemoryCache.delete("1") - verify(mockObserver, times(2)).onInvalidate() + pagingMemoryCache.verifyInvalidate(2) { + pagingMemoryCache.insert(listOf("1")) + pagingMemoryCache.delete("1") + } } @Test fun update_SilenceAfterDeleteFailed() { - pagingMemoryCache.delete("") - verify(mockObserver, times(0)).onInvalidate() + pagingMemoryCache.verifyInvalidate(0) { + pagingMemoryCache.delete("") + } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index 8d71316bb..73c7a5aea 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -33,14 +33,11 @@ import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.paging.mediator.list.ListsMediator import kotlinx.coroutines.runBlocking import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner /** * instead of testing pagination, we should focus on our code logic */ -@RunWith(MockitoJUnitRunner::class) class ListsMediatorTest { private var mockDataBase = MockCacheDatabase() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt index f41672e14..3e7ce7c34 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt @@ -36,6 +36,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.model.paging.saveToDb import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator +import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals @@ -118,6 +119,7 @@ internal class MockPagingGapMediator( max_id: String?, since_id: String? ): List { + delay(5) return listOf(mockIStatus(id = statusId)).toIPaging() } } From b5283adb498635badfe6b79c93c66a894bd09249 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 16:14:42 +0800 Subject: [PATCH 086/615] add build check for common --- .github/workflows/common.yml | 20 +++++++++++++++++++ .../paging/crud/PagingMemoryCacheTest.kt | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 24c3e5161..744331329 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -10,6 +10,26 @@ on: jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Build with Gradle + run: ./gradlew :common:spotlessCheck :desktop:build + + - name: Upload build reports + uses: actions/upload-artifact@v2 + with: + name: build-reports + path: '**/build/reports' + unit-test: runs-on: ubuntu-latest needs: build diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index d1d2badfc..1dfab18af 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -41,7 +41,7 @@ class PagingMemoryCacheTest { assertEquals(3, pagingMemoryCache.find(0, 3).size) } - private fun PagingMemoryCache.verifyInvalidate( times: Int, block: () -> Unit) { + private fun PagingMemoryCache.verifyInvalidate(times: Int, block: () -> Unit) { var invalidateCount = 0 val observer = object : OnInvalidateObserver { override fun onInvalidate() { From 4ec2126673377a01eed58400a90f8b211d00ee67 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 25 Aug 2021 16:25:04 +0800 Subject: [PATCH 087/615] remove TwidereModule --- .../kotlin/com/twidere/twiderex/di/Setup.kt | 1 - .../com/twidere/twiderex/di/TwidereModule.kt | 31 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index cedfe934e..1de30c247 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -23,6 +23,5 @@ package com.twidere.twiderex.di import org.koin.core.KoinApplication fun KoinApplication.setupModules() { - modules(twidereModules) modules(preferencesModule) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt deleted file mode 100644 index 71134c694..000000000 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import com.twidere.twiderex.dataprovider.DataProvider -import org.koin.dsl.module - -internal val twidereModules = module { - single { DataProvider.create() } - single { get().fileCacheHandler } - single { get().cacheDatabase } - single { get().appDatabase } -} From b806e4a4f50ca81539192b09a28aa13e8858a939 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 25 Aug 2021 18:05:28 +0800 Subject: [PATCH 088/615] migrate vm account to repo flow --- .../twiderex/component/TimelineComponent.kt | 22 ++- .../compose/ComposeSearchHashtagScene.kt | 28 ++-- .../scenes/compose/ComposeSearchUserScene.kt | 26 ++-- .../scenes/lists/ListsAddMembersScene.kt | 8 +- .../twiderex/scenes/settings/LayoutScene.kt | 25 ++-- .../compose/ComposeSearchUserViewModel.kt | 48 +++--- .../MastodonComposeSearchHashtagViewModel.kt | 48 +++--- .../viewmodel/dm/DMConversationViewModel.kt | 33 +++-- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 134 +++++++++-------- .../dm/DMNewConversationViewModel.kt | 69 ++++----- .../lists/ListsSearchUserViewModel.kt | 50 ++++--- .../viewmodel/lists/ListsTimelineViewModel.kt | 34 ++--- .../viewmodel/lists/ListsUserViewModel.kt | 86 ++++++----- .../viewmodel/lists/ListsViewModel.kt | 139 +++++++++++------- .../mastodon/MastodonHashtagViewModel.kt | 34 ++--- .../MastodonSearchHashtagViewModel.kt | 37 +++-- .../mastodon/MastodonSignInViewModel.kt | 8 +- .../viewmodel/search/SearchInputViewModel.kt | 34 +++-- .../viewmodel/search/SearchSaveViewModel.kt | 32 ++-- .../viewmodel/search/SearchTweetsViewModel.kt | 35 +++-- .../viewmodel/search/SearchUserViewModel.kt | 40 +++-- .../settings/AccountNotificationViewModel.kt | 33 ++--- .../viewmodel/settings/AppearanceViewModel.kt | 8 +- .../viewmodel/settings/DisplayViewModel.kt | 9 +- .../viewmodel/settings/LayoutViewModel.kt | 20 ++- .../viewmodel/settings/MiscViewModel.kt | 10 +- .../settings/NotificationViewModel.kt | 7 +- .../viewmodel/settings/StorageViewModel.kt | 9 +- .../timeline/HomeTimelineViewModel.kt | 47 +++--- .../timeline/MentionsTimelineViewModel.kt | 51 ++++--- .../timeline/NotificationTimelineViewModel.kt | 53 ++++--- .../viewmodel/timeline/TimelineViewModel.kt | 67 ++++++--- .../mastodon/FederatedTimelineViewModel.kt | 43 +++--- .../mastodon/LocalTimelineViewModel.kt | 43 +++--- .../viewmodel/trend/TrendViewModel.kt | 27 ++-- .../twitter/TwitterSignInViewModel.kt | 25 +--- 36 files changed, 794 insertions(+), 628 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 7e49de071..4e5230c03 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.unit.dp import androidx.paging.LoadState @@ -56,19 +55,26 @@ fun TimelineComponent( }, refreshIndicatorPadding = contentPadding ) { - val lastScrollState = remember { - viewModel.restoreScrollState() - } - val listState = rememberLazyListState( - initialFirstVisibleItemIndex = lastScrollState.firstVisibleItemIndex, - initialFirstVisibleItemScrollOffset = lastScrollState.firstVisibleItemScrollOffset, + val scrollState by viewModel.timelineScrollState.observeAsState( + initial = TimelineScrollState.Zero ) + val listState = rememberLazyListState() + LaunchedEffect(Unit) { + snapshotFlow { scrollState } + .distinctUntilChanged() + .collect { + listState.scrollToItem( + it.firstVisibleItemIndex, + it.firstVisibleItemScrollOffset + ) + } + } if (items.itemCount > 0) { LaunchedEffect(lazyListController) { lazyListController?.listState = listState } } - LaunchedEffect(listState) { + LaunchedEffect(Unit) { snapshotFlow { listState.isScrollInProgress } .distinctUntilChanged() .filter { !it } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index 2e5e71764..b9481c6f8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -34,7 +34,6 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -65,8 +64,7 @@ fun ComposeSearchHashtagScene() { } val text by viewModel.text.observeAsState(initial = "") - val sourceState by viewModel.sourceFlow.collectAsState(initial = null) - val source = sourceState?.collectAsLazyPagingItems() + val source = viewModel.source.collectAsLazyPagingItems() TwidereScene { InAppNotificationScaffold( topBar = { @@ -115,19 +113,17 @@ fun ComposeSearchHashtagScene() { ) } ) { - source?.let { source -> - LazyColumn { - loadState(source.loadState.refresh) - items(source) { - it?.name?.let { name -> - ListItem( - modifier = Modifier - .clickable { - navController.goBackWith("#$name") - } - ) { - Text(text = name) - } + LazyColumn { + loadState(source.loadState.refresh) + items(source) { + it?.name?.let { name -> + ListItem( + modifier = Modifier + .clickable { + navController.goBackWith("#$name") + } + ) { + Text(text = name) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index 8df5d5ca8..c515b1c05 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -32,7 +32,6 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.res.stringResource @@ -61,8 +60,7 @@ fun ComposeSearchUserScene() { ComposeSearchUserViewModel(account = account) } val text by viewModel.text.observeAsState(initial = "") - val sourceState by viewModel.sourceFlow.collectAsState(initial = null) - val source = sourceState?.collectAsLazyPagingItems() + val source = viewModel.source.collectAsLazyPagingItems() TwidereScene { InAppNotificationScaffold( topBar = { @@ -111,18 +109,16 @@ fun ComposeSearchUserScene() { ) } ) { - source?.let { source -> - LazyUiUserList( - items = source, - onItemClicked = { - val displayName = it.getDisplayScreenName(account.accountKey.host) - navController.goBackWith(displayName) - }, - header = { - loadState(source.loadState.refresh) - } - ) - } + LazyUiUserList( + items = source, + onItemClicked = { + val displayName = it.getDisplayScreenName(account.accountKey.host) + navController.goBackWith(displayName) + }, + header = { + loadState(source.loadState.refresh) + } + ) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index 71dd62931..ae0207eb4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -36,7 +36,6 @@ import androidx.compose.material.TextButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -46,7 +45,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState -import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.R @@ -70,7 +68,6 @@ import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsAddMemberViewModel import com.twidere.twiderex.viewmodel.lists.ListsSearchUserViewModel -import kotlinx.coroutines.flow.flowOf @Composable fun ListsAddMembersScene( @@ -97,8 +94,7 @@ fun ListsAddMembersScene( } val keyword by searchViewModel.text.observeAsState(initial = "") - val searchSourceState by searchViewModel.sourceFlow.collectAsState(initial = null) - val searchSource = searchSourceState?.collectAsLazyPagingItems() + val searchSource = searchViewModel.source.collectAsLazyPagingItems() TwidereScene { InAppNotificationScaffold( topBar = { @@ -150,7 +146,7 @@ fun ListsAddMembersScene( Box(modifier = Modifier.fillMaxSize()) { // search result SearchResultsContent( - source = searchSource ?: flowOf(PagingData.from(viewModel.pendingMap.values.toMutableList())).collectAsLazyPagingItems(), + source = searchSource, onAction = { viewModel.addToOrRemove(it) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index 27ce43577..8b88d37ac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -56,6 +56,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.scenes.home.HomeMenus import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene @@ -72,7 +73,7 @@ fun LayoutScene() { account = account, ) } - val user = viewModel.user + val user by viewModel.user.observeAsState(initial = null) val menuOrder by account.preferences.homeMenuOrder.collectAsState( initial = HomeMenus.values().map { it to it.showDefault } ) @@ -114,17 +115,19 @@ fun LayoutScene() { modifier = Modifier .verticalScroll(rememberScrollState()), ) { - Surface( - color = MaterialTheme.colors.primary, - ) { - ListItem( - text = { - Row { - UserName(user = user) - UserScreenName(user = user) + user?.let { user -> + Surface( + color = MaterialTheme.colors.primary, + ) { + ListItem( + text = { + Row { + UserName(user = user) + UserScreenName(user = user) + } } - } - ) + ) + } } ListItem( text = { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 136b40453..1945f45be 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -25,35 +25,45 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource +import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class ComposeSearchUserViewModel( - private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + val text = MutableStateFlow("") @OptIn(FlowPreview::class) - val sourceFlow = text.debounce(666L).map { - it.takeIf { it.isNotEmpty() }?.let { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - it, - account.service as SearchService - ) - }.flow.cachedIn(viewModelScope) - } - } + val source = text.debounce(666L).flatMapLatest { + it.takeIf { it.isNotEmpty() }?.let { str -> + account.flatMapLatest { + it?.let { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + str, + account.service as SearchService + ) + }.flow + } ?: emptyFlow() + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 3123e5e73..56948527c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -25,34 +25,42 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource -import kotlinx.coroutines.FlowPreview +import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonComposeSearchHashtagViewModel( - private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + val text = MutableStateFlow("") - @OptIn(FlowPreview::class) - val sourceFlow = text.debounce(666L).map { - it.takeIf { it.isNotEmpty() }?.let { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - MastodonSearchHashtagPagingSource( - it, - account.service as MastodonService - ) - }.flow.cachedIn(viewModelScope) - } - } + val source = text.debounce(666L).flatMapLatest { + it.takeIf { it.isNotEmpty() }?.let { str -> + account.flatMapLatest { + it?.let { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + MastodonSearchHashtagPagingSource( + str, + account.service as MastodonService + ) + }.flow + } ?: emptyFlow() + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index ea9a007dc..ca077e53c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -20,29 +20,34 @@ */ package com.twidere.twiderex.viewmodel.dm +import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope -class DMConversationViewModel @AssistedInject constructor( +class DMConversationViewModel( private val repository: DirectMessageRepository, - @Assisted private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): DMConversationViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.dmConversationListSource( - accountKey = account.accountKey, - service = account.service as DirectMessageService, - lookupService = account.service as LookupService - ) + account.flatMapLatest { + it?.let { account -> + repository.dmConversationListSource( + accountKey = account.accountKey, + service = account.service as DirectMessageService, + lookupService = account.service as LookupService + ) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index a038c161c..3543fe60d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -25,48 +25,56 @@ import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope import java.util.UUID -class DMEventViewModel @AssistedInject constructor( +class DMEventViewModel( private val repository: DirectMessageRepository, private val messageAction: DirectMessageAction, - @Assisted private val account: AccountDetails, - @Assisted private val conversationKey: MicroBlogKey, + private val accountRepository: AccountRepository, + private val conversationKey: MicroBlogKey, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, conversationKey: MicroBlogKey): DMEventViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val conversation by lazy { - repository.dmConversation( - accountKey = account.accountKey, - conversationKey = conversationKey - ) + account.flatMapLatest { + it?.let { account -> + repository.dmConversation( + accountKey = account.accountKey, + conversationKey = conversationKey + ) + } ?: emptyFlow() + } } val source by lazy { - repository.dmEventListSource( - accountKey = account.accountKey, - conversationKey = conversationKey, - service = account.service as DirectMessageService, - lookupService = account.service as LookupService - ).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + repository.dmEventListSource( + accountKey = account.accountKey, + conversationKey = conversationKey, + service = account.service as DirectMessageService, + lookupService = account.service as LookupService + ) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } // input @@ -78,54 +86,60 @@ class DMEventViewModel @AssistedInject constructor( fun sendMessage() { if (input.value.isEmpty() && inputImage.value == null) return viewModelScope.launch { - conversation.firstOrNull()?.let { - messageAction.send( - account.type, - data = DirectMessageSendData( - text = input.value, - images = inputImage.value?.toString()?.let { uri -> listOf(uri) } - ?: emptyList(), - recipientUserKey = it.recipientKey, - draftMessageKey = when (account.type) { - PlatformType.Twitter -> MicroBlogKey.twitter( - UUID.randomUUID().toString() - ) - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> MicroBlogKey.Empty - }, - conversationKey = it.conversationKey, - accountKey = account.accountKey + account.lastOrNull()?.let { account -> + conversation.firstOrNull()?.let { + messageAction.send( + account.type, + data = DirectMessageSendData( + text = input.value, + images = inputImage.value?.toString()?.let { uri -> listOf(uri) } + ?: emptyList(), + recipientUserKey = it.recipientKey, + draftMessageKey = when (account.type) { + PlatformType.Twitter -> MicroBlogKey.twitter( + UUID.randomUUID().toString() + ) + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> MicroBlogKey.Empty + }, + conversationKey = it.conversationKey, + accountKey = account.accountKey + ) ) - ) - input.value = "" - inputImage.value = null + input.value = "" + inputImage.value = null + } } } } - fun sendDraftMessage(event: UiDMEvent) { - messageAction.send( - account.type, - data = DirectMessageSendData( - text = event.originText, - images = event.media.mapNotNull { it.url }, - recipientUserKey = event.recipientAccountKey, - conversationKey = event.conversationKey, - accountKey = account.accountKey, - draftMessageKey = event.messageKey + fun sendDraftMessage(event: UiDMEvent) = viewModelScope.launch { + account.lastOrNull()?.let { account -> + messageAction.send( + account.type, + data = DirectMessageSendData( + text = event.originText, + images = event.media.mapNotNull { it.url }, + recipientUserKey = event.recipientAccountKey, + conversationKey = event.conversationKey, + accountKey = account.accountKey, + draftMessageKey = event.messageKey + ) ) - ) + } } - fun deleteMessage(event: UiDMEvent) { - messageAction.delete( - data = DirectMessageDeleteData( - messageId = event.messageId, - messageKey = event.messageKey, - conversationKey = event.conversationKey, - accountKey = account.accountKey + fun deleteMessage(event: UiDMEvent) = viewModelScope.launch { + account.lastOrNull()?.let { account -> + messageAction.delete( + data = DirectMessageDeleteData( + messageId = event.messageId, + messageKey = event.messageKey, + conversationKey = event.conversationKey, + accountKey = account.accountKey + ) ) - ) + } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 0bae3e466..9c5a377f3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -25,59 +25,62 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class DMNewConversationViewModel @AssistedInject constructor( +class DMNewConversationViewModel( private val dmRepository: DirectMessageRepository, - @Assisted private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): DMNewConversationViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val input = MutableStateFlow("") - @OptIn(FlowPreview::class) - val sourceFlow = input.debounce(666L).map { - it.takeIf { it.isNotEmpty() }?.let { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - it, - account.service as SearchService, - ) - }.flow.cachedIn(viewModelScope) - } - } + val sourceFlow = input.debounce(666L).flatMapLatest { + it.takeIf { it.isNotEmpty() }?.let { str -> + account.flatMapLatest { + it?.let { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + str, + account.service as SearchService, + ) + }.flow + } ?: emptyFlow() + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) fun createNewConversation(receiver: UiUser, onResult: (key: MicroBlogKey?) -> Unit) { viewModelScope.launch { kotlin.runCatching { - dmRepository.createNewConversation( - receiver = receiver, - accountKey = account.accountKey, - platformType = account.type - ) + account.lastOrNull()?.let { account -> + dmRepository.createNewConversation( + receiver = receiver, + accountKey = account.accountKey, + platformType = account.type + ) + } }.onSuccess { onResult(it) }.onFailure { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 9b895f0e3..4d112823a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -25,37 +25,45 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource -import kotlinx.coroutines.FlowPreview +import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class ListsSearchUserViewModel( - private val account: AccountDetails, + private val accountRepository: AccountRepository, following: Boolean = false, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + val text = MutableStateFlow("") - @OptIn(FlowPreview::class) - val sourceFlow = text.debounce(666L).map { + val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - it, - account.service as SearchService, - following = following - ) - }.flow.cachedIn(viewModelScope) - } - } + account.flatMapLatest { account -> + account?.let { _ -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + it, + account.service as SearchService, + following = following + ) + }.flow + } ?: emptyFlow() + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index 140485c14..40c898e56 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -21,32 +21,32 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class ListsTimelineViewModel @AssistedInject constructor( +class ListsTimelineViewModel( repository: TimelineRepository, - @Assisted account: AccountDetails, - @Assisted listKey: MicroBlogKey, + private val accountRepository: AccountRepository, + listKey: MicroBlogKey, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - listKey: MicroBlogKey, - ): ListsTimelineViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.listTimeline( - listKey = listKey, - account = account, - ).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { + repository.listTimeline( + listKey = listKey, + account = it, + ) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5a86aad74..031453ae3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -23,38 +23,48 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf import androidx.paging.PagingData import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.utils.notify import com.twidere.twiderex.viewmodel.user.UserListViewModel -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class ListsUserViewModel @AssistedInject constructor( +class ListsUserViewModel( private val listsUsersRepository: ListsUsersRepository, - @Assisted private val account: AccountDetails, - @Assisted private val listId: String, - @Assisted private val viewMembers: Boolean = true, + private val accountRepository: AccountRepository, + private val listId: String, + private val viewMembers: Boolean = true, ) : UserListViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, listId: String, viewMembers: Boolean = true): ListsUserViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } private val members by lazy { - listsUsersRepository.fetchMembers(account = account, listId = listId).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + listsUsersRepository.fetchMembers(account = account, listId = listId) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } private val subscribers by lazy { - listsUsersRepository.fetchSubscribers(account = account, listId = listId).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + listsUsersRepository.fetchSubscribers(account = account, listId = listId) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } override val source: Flow> @@ -65,11 +75,13 @@ class ListsUserViewModel @AssistedInject constructor( fun removeMember(user: UiUser) { try { viewModelScope.launch { - listsUsersRepository.removeMember( - account = account, - listId = listId, - user = user - ) + account.lastOrNull()?.let { account -> + listsUsersRepository.removeMember( + account = account, + listId = listId, + user = user + ) + } } } catch (e: Exception) { e.printStackTrace() @@ -77,16 +89,14 @@ class ListsUserViewModel @AssistedInject constructor( } } -class ListsAddMemberViewModel @AssistedInject constructor( +class ListsAddMemberViewModel( private val listsUsersRepository: ListsUsersRepository, private val inAppNotification: InAppNotification, - @Assisted private val account: AccountDetails, - @Assisted private val listId: String, + private val accountRepository: AccountRepository, + private val listId: String, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, listId: String): ListsAddMemberViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val loading = MutableStateFlow(false) @@ -96,21 +106,25 @@ class ListsAddMemberViewModel @AssistedInject constructor( if (pendingMap[user.userKey] == null) { loading.value = true loadingRequest { - listsUsersRepository.addMember( - listId = listId, - user = user, - account = account - ) - pendingMap[user.userKey] = user + account.firstOrNull()?.let { account -> + listsUsersRepository.addMember( + listId = listId, + user = user, + account = account + ) + pendingMap[user.userKey] = user + } } } else { loadingRequest { - listsUsersRepository.removeMember( - account = account, - listId = listId, - user = user - ) - pendingMap.remove(user.userKey) + account.firstOrNull()?.let { account -> + listsUsersRepository.removeMember( + account = account, + listId = listId, + user = user + ) + pendingMap.remove(user.userKey) + } } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 7a1b6a307..2ac8be576 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -22,45 +22,58 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import androidx.paging.filter -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.utils.notify -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class ListsViewModel @AssistedInject constructor( +class ListsViewModel( private val listsRepository: ListsRepository, - @Assisted private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): ListsViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - listsRepository.fetchLists(account = account).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + listsRepository.fetchLists(account = account) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } val ownerSource by lazy { - source.map { pagingData -> - pagingData.filter { it.isOwner(account.user.userId) } - } + account.flatMapLatest { + it?.let { account -> + source.map { + it.filter { it.isOwner(account.user.userId) } + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } val subscribedSource by lazy { - source.map { pagingData -> - pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } - } + account.flatMapLatest { + it?.let { account -> + source.map { pagingData -> + pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } + } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } @@ -70,7 +83,10 @@ abstract class ListsOperatorViewModel( val modifySuccess = MutableStateFlow(false) val loading = MutableStateFlow(false) - protected fun loadingRequest(onResult: (success: Boolean, list: UiList?) -> Unit, request: suspend () -> UiList?) { + protected fun loadingRequest( + onResult: (success: Boolean, list: UiList?) -> Unit, + request: suspend () -> UiList? + ) { loading.value = true viewModelScope.launch { runCatching { @@ -89,15 +105,14 @@ abstract class ListsOperatorViewModel( } } -class ListsCreateViewModel @AssistedInject constructor( +class ListsCreateViewModel( inAppNotification: InAppNotification, private val listsRepository: ListsRepository, - @Assisted private val account: AccountDetails, - @Assisted private val onResult: (success: Boolean, list: UiList?) -> Unit + private val accountRepository: AccountRepository, + private val onResult: (success: Boolean, list: UiList?) -> Unit ) : ListsOperatorViewModel(inAppNotification) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, onResult: (success: Boolean, list: UiList?) -> Unit): ListsCreateViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } fun createList( @@ -106,26 +121,26 @@ class ListsCreateViewModel @AssistedInject constructor( private: Boolean = false ) { loadingRequest(onResult) { - listsRepository.createLists( - account = account, - title = title, - description = description, - mode = if (private)ListsMode.PRIVATE.value else ListsMode.PUBLIC.value - ) + account.lastOrNull()?.let { account -> + listsRepository.createLists( + account = account, + title = title, + description = description, + mode = if (private) ListsMode.PRIVATE.value else ListsMode.PUBLIC.value + ) + } } } } -class ListsModifyViewModel @AssistedInject constructor( +class ListsModifyViewModel( private val listsRepository: ListsRepository, inAppNotification: InAppNotification, - @Assisted private val account: AccountDetails, - @Assisted private val listKey: MicroBlogKey, + private val accountRepository: AccountRepository, + private val listKey: MicroBlogKey, ) : ListsOperatorViewModel(inAppNotification) { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, listKey: MicroBlogKey): ListsModifyViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val editName = MutableStateFlow("") @@ -133,7 +148,11 @@ class ListsModifyViewModel @AssistedInject constructor( var editPrivate = MutableStateFlow(false) val source by lazy { - listsRepository.findListWithListKey(account = account, listKey = listKey) + account.flatMapLatest { + it?.let { account -> + listsRepository.findListWithListKey(account = account, listKey = listKey) + } ?: emptyFlow() + } } init { @@ -154,13 +173,15 @@ class ListsModifyViewModel @AssistedInject constructor( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - listsRepository.updateLists( - account = account, - listId = listId, - title = title, - description = description, - mode = if (private)ListsMode.PRIVATE.value else ListsMode.PUBLIC.value - ) + account.lastOrNull()?.let { account -> + listsRepository.updateLists( + account = account, + listId = listId, + title = title, + description = description, + mode = if (private) ListsMode.PRIVATE.value else ListsMode.PUBLIC.value + ) + } } } @@ -170,11 +191,13 @@ class ListsModifyViewModel @AssistedInject constructor( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - listsRepository.deleteLists( - account = account, - listKey = listKey, - listId = listId, - ) + account.lastOrNull()?.let { account -> + listsRepository.deleteLists( + account = account, + listKey = listKey, + listId = listId, + ) + } } } @@ -183,10 +206,12 @@ class ListsModifyViewModel @AssistedInject constructor( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - listsRepository.subscribeLists( - account = account, - listKey = listKey - ) + account.lastOrNull()?.let { account -> + listsRepository.subscribeLists( + account = account, + listKey = listKey + ) + } } } @@ -195,10 +220,12 @@ class ListsModifyViewModel @AssistedInject constructor( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - listsRepository.unsubscribeLists( - account = account, - listKey = listKey - ) + account.lastOrNull()?.let { account -> + listsRepository.unsubscribeLists( + account = account, + listKey = listKey + ) + } } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 7394b893c..0c5772032 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -21,31 +21,31 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.cachedIn -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class MastodonHashtagViewModel @AssistedInject constructor( +class MastodonHashtagViewModel( private val repository: TimelineRepository, - @Assisted keyword: String, - @Assisted account: AccountDetails, + private val accountRepository: AccountRepository, + keyword: String, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - keyword: String, - account: AccountDetails, - ): MastodonHashtagViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.mastodonHashtagTimeline( - keyword = keyword, - account = account, - ).cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { + repository.mastodonHashtagTimeline( + keyword = keyword, + account = it, + ) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 9fd2dc395..f3c74f0a0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -25,26 +25,37 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonSearchHashtagViewModel( - private val account: AccountDetails, + private val accountRepository: AccountRepository, keyword: String, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + val source by lazy { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - MastodonSearchHashtagPagingSource( - keyword, - account.service as MastodonService - ) - }.flow.cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + MastodonSearchHashtagPagingSource( + keyword, + it.service as MastodonService + ) + }.flow + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index 2aa5f9bd2..ca24eb5d5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -35,23 +35,17 @@ import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.ACCOUNT_TYPE import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope import java.net.URI -class MastodonSignInViewModel @AssistedInject constructor( +class MastodonSignInViewModel( private val repository: AccountRepository, private val inAppNotification: InAppNotification, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): MastodonSignInViewModel - } - val loading = MutableStateFlow(false) val host = MutableStateFlow(TextFieldValue()) fun setHost(value: TextFieldValue) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index b76188c57..8dab14519 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -21,31 +21,39 @@ package com.twidere.twiderex.viewmodel.search import com.twidere.twiderex.db.model.DbSearch -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class SearchInputViewModel @AssistedInject constructor( +class SearchInputViewModel( private val repository: SearchRepository, - @Assisted private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): SearchInputViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.searchHistory(account.accountKey) + account.flatMapLatest { + it?.let { + repository.searchHistory(it.accountKey) + } ?: flowOf(emptyList()) + } } val savedSource by lazy { - repository.savedSearch(account.accountKey) + account.flatMapLatest { + it?.let { + repository.savedSearch(it.accountKey) + } ?: flowOf(emptyList()) + } } val expandSearch = MutableStateFlow(false) @@ -55,6 +63,8 @@ class SearchInputViewModel @AssistedInject constructor( } fun addOrUpgrade(content: String) = viewModelScope.launch { - repository.addOrUpgrade(content, account.accountKey) + account.lastOrNull()?.let { + repository.addOrUpgrade(content, it.accountKey) + } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index fdf93494c..771bcb59d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,24 +20,22 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class SearchSaveViewModel @AssistedInject constructor( +class SearchSaveViewModel( private val repository: SearchRepository, - @Assisted private val account: AccountDetails, - @Assisted private val content: String, + private val accountRepository: AccountRepository, + private val content: String, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, content: String): SearchSaveViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val loading = MutableStateFlow(false) @@ -46,7 +44,9 @@ class SearchSaveViewModel @AssistedInject constructor( init { viewModelScope.launch { - isSaved.value = repository.get(content, account.accountKey)?.saved ?: false + account.lastOrNull()?.let { + isSaved.value = repository.get(content, it.accountKey)?.saved ?: false + } } } @@ -54,8 +54,14 @@ class SearchSaveViewModel @AssistedInject constructor( viewModelScope.launch { loading.value = true try { - repository.addOrUpgrade(content = content, accountKey = account.accountKey, saved = true) - isSaved.value = true + account.lastOrNull()?.let { + repository.addOrUpgrade( + content = content, + accountKey = it.accountKey, + saved = true + ) + isSaved.value = true + } } catch (e: Exception) { isSaved.value = false } finally { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 89b22b962..26c6db5a5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -24,32 +24,37 @@ import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.transform.toUi import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class SearchTweetsViewModel @AssistedInject constructor( +class SearchTweetsViewModel( val database: CacheDatabase, - @Assisted private val account: AccountDetails, - @Assisted keyword: String, + private val accountRepository: AccountRepository, + keyword: String, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, keyword: String): SearchTweetsViewModel - } - - private val service by lazy { - account.service as SearchService + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - SearchStatusMediator(keyword, database, account.accountKey, service).pager() - .flow.map { it.map { it.status.toUi(account.accountKey) } }.cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + SearchStatusMediator( + keyword, + database, + account.accountKey, + account.service as SearchService + ).pager() + .flow.map { it.map { it.status.toUi(account.accountKey) } } + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index ffd46e677..de1b29822 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -25,30 +25,40 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class SearchUserViewModel( - private val account: AccountDetails, + private val accountRepository: AccountRepository, keyword: String, following: Boolean = false, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } val source by lazy { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - keyword, - account.service as SearchService, - following = following - ) - }.flow.cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + keyword, + account.service as SearchService, + following = following + ) + }.flow + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 7752cac8c..6cd3e5ef4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,42 +20,37 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.repository.AccountRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class AccountNotificationViewModel @AssistedInject constructor( +class AccountNotificationViewModel( private val accountRepository: AccountRepository, - @Assisted accountKey: MicroBlogKey, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - accountKey: MicroBlogKey, - ): AccountNotificationViewModel - } - val account by lazy { - accountRepository.accounts.map { - it.firstOrNull { it.accountKey == accountKey }?.toUi() - } + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val preferences by lazy { - accountRepository.getAccountPreferences(accountKey) + account.map { + it?.let { + accountRepository.getAccountPreferences(it.accountKey) + } + }.asStateIn(viewModelScope, null) } val isNotificationEnabled by lazy { - preferences.isNotificationEnabled + preferences.flatMapLatest { it?.isNotificationEnabled ?: flowOf(false) } + .asStateIn(viewModelScope, false) } fun setIsNotificationEnabled(value: Boolean) = viewModelScope.launch { - preferences.setIsNotificationEnabled(value) + preferences.lastOrNull()?.setIsNotificationEnabled(value) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt index b9528e1df..fea1261f9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt @@ -22,20 +22,14 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import com.twidere.twiderex.preferences.model.AppearancePreferences -import dagger.assisted.AssistedInject import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class AppearanceViewModel @AssistedInject constructor( +class AppearanceViewModel( private val appearancePreferences: DataStore ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): AppearanceViewModel - } - fun setPrimaryColorIndex(index: Int) = viewModelScope.launch { appearancePreferences.updateData { it.copy(primaryColorIndex = index) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index 8e05bd610..d256c571a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -22,20 +22,13 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import com.twidere.twiderex.preferences.model.DisplayPreferences -import dagger.assisted.AssistedInject import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class DisplayViewModel @AssistedInject constructor( +class DisplayViewModel( private val displayPreferences: DataStore ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): DisplayViewModel - } - fun setUseSystemFontSize(value: Boolean) = viewModelScope.launch { displayPreferences.updateData { it.copy(useSystemFontSize = value) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index af765b962..be31b0ec5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,10 +20,12 @@ */ package com.twidere.twiderex.viewmodel.settings +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.scenes.home.HomeMenus -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -31,9 +33,13 @@ import kotlin.math.min private const val MaxMenuCount = 5 -class LayoutViewModel @AssistedInject constructor( - @Assisted private val account: AccountDetails, +class LayoutViewModel( + private val accountRepository: AccountRepository, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + fun updateHomeMenu(oldIndex: Int, newIndex: Int, menus: List) = viewModelScope.launch { menus.toMutableList().let { list -> list.add(newIndex, list.removeAt(oldIndex)) @@ -43,7 +49,7 @@ class LayoutViewModel @AssistedInject constructor( .map { it to true } + list.subList(index, list.size) .filterIsInstance().map { it to false } }.let { - account.preferences.setHomeMenuOrder(it) + account.lastOrNull()?.preferences?.setHomeMenuOrder(it) } } } @@ -80,6 +86,8 @@ class LayoutViewModel @AssistedInject constructor( } val user by lazy { - account.toUi() + account.map { + it?.toUi() + } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index d6b75698f..9aca95a12 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -24,24 +24,16 @@ import androidx.datastore.core.DataStore import com.twidere.twiderex.R import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences -import dagger.assisted.AssistedInject -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -@OptIn(FlowPreview::class) -class MiscViewModel @AssistedInject constructor( +class MiscViewModel( private val miscPreferences: DataStore, private val inAppNotification: InAppNotification, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): MiscViewModel - } - val nitter by lazy { MutableStateFlow("") } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt index f4dd6268b..6a9a65092 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt @@ -22,19 +22,14 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore import com.twidere.twiderex.preferences.model.NotificationPreferences -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class NotificationViewModel @AssistedInject constructor( +class NotificationViewModel( val notification: DataStore, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): NotificationViewModel - } val enabled = notification.data.map { it.enableNotification } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt index ab8a71c1f..64e973250 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt @@ -21,21 +21,14 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.repository.CacheRepository -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class StorageViewModel @AssistedInject constructor( +class StorageViewModel( private val repository: CacheRepository, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(): StorageViewModel - } - val loading = MutableStateFlow(false) fun clearDatabaseCache() = viewModelScope.launch { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index b89573308..9b8cfdf3f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -20,30 +20,39 @@ */ package com.twidere.twiderex.viewmodel.timeline -import android.content.SharedPreferences +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope -class HomeTimelineViewModel @AssistedInject constructor( - preferences: SharedPreferences, +class HomeTimelineViewModel( + dataStore: DataStore, database: CacheDatabase, - @Assisted account: AccountDetails, -) : TimelineViewModel(preferences) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): HomeTimelineViewModel + private val accountRepository: AccountRepository, +) : TimelineViewModel(dataStore) { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } - override val pagingMediator: PagingWithGapMediator = - HomeTimelineMediator( - account.service as TimelineService, - account.accountKey, - database, - ) - override val savedStateKey: String = "${account.accountKey}_home" + override val pagingMediator = account.map { + if (it != null) { + HomeTimelineMediator( + it.service as TimelineService, + it.accountKey, + database, + ) + } else { + null + } + } + override val savedStateKey = account.map { + it?.let { + "${it.accountKey}_home" + } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index bfbe4699d..f27ce29fa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -20,33 +20,44 @@ */ package com.twidere.twiderex.viewmodel.timeline -import android.content.SharedPreferences +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope -class MentionsTimelineViewModel @AssistedInject constructor( - preferences: SharedPreferences, +class MentionsTimelineViewModel( + dataStore: DataStore, database: CacheDatabase, notificationRepository: NotificationRepository, - @Assisted private val account: AccountDetails -) : TimelineViewModel(preferences) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): MentionsTimelineViewModel + private val accountRepository: AccountRepository, +) : TimelineViewModel(dataStore) { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } - override val pagingMediator: PagingWithGapMediator = - MentionTimelineMediator( - service = account.service as TimelineService, - accountKey = account.accountKey, - database = database, - notificationRepository = notificationRepository - ) - override val savedStateKey: String = "${account.accountKey}_mentions" + override val pagingMediator = account.map { + if (it != null) { + MentionTimelineMediator( + service = it.service as TimelineService, + accountKey = it.accountKey, + database = database, + notificationRepository = notificationRepository + ) + } else { + null + } + } + override val savedStateKey = account.map { + if (it != null) { + "${it.accountKey}_mentions" + } else { + null + } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 89ab75921..70912a1b1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -20,33 +20,46 @@ */ package com.twidere.twiderex.viewmodel.timeline -import android.content.SharedPreferences +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope -class NotificationTimelineViewModel @AssistedInject constructor( - preferences: SharedPreferences, +class NotificationTimelineViewModel( + dataStore: DataStore, database: CacheDatabase, notificationRepository: NotificationRepository, - @Assisted private val account: AccountDetails -) : TimelineViewModel(preferences) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): NotificationTimelineViewModel + private val accountRepository: AccountRepository, +) : TimelineViewModel(dataStore) { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } - override val pagingMediator: PagingWithGapMediator = - NotificationTimelineMediator( - service = account.service as NotificationService, - accountKey = account.accountKey, - database = database, - notificationRepository = notificationRepository, - ) - override val savedStateKey: String = "${account.accountKey}_notification" + override val pagingMediator by lazy { + account.map { + if (it != null) { + NotificationTimelineMediator( + service = it.service as NotificationService, + accountKey = it.accountKey, + database = database, + notificationRepository = notificationRepository, + ) + } else { + null + } + } + } + override val savedStateKey = account.map { + if (it == null) { + null + } else { + "${it.accountKey}_notification" + } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index e5932a2d6..503805729 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -20,48 +20,73 @@ */ package com.twidere.twiderex.viewmodel.timeline -import android.content.SharedPreferences -import androidx.core.content.edit +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.extensions.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope abstract class TimelineViewModel( - private val preferences: SharedPreferences, + private val dataStore: DataStore ) : ViewModel() { val source by lazy { - pagingMediator.pager().toUi(accountKey = pagingMediator.accountKey).cachedIn(viewModelScope) + pagingMediator.flatMapLatest { + it?.pager()?.toUi(accountKey = it.accountKey) ?: emptyFlow() + }.cachedIn(viewModelScope) } - abstract val pagingMediator: PagingWithGapMediator - abstract val savedStateKey: String + abstract val pagingMediator: Flow + abstract val savedStateKey: Flow val loadingBetween: Flow> - get() = pagingMediator.loadingBetween + get() = pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + + val timelineScrollState by lazy { + savedStateKey.flatMapLatest { + val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") + val firstVisibleItemScrollOffsetKey = + intPreferencesKey("${it}_firstVisibleItemScrollOffset") + dataStore.data.map { + val firstVisibleItemIndex = it[firstVisibleItemIndexKey] ?: 0 + val firstVisibleItemScrollOffset = it[firstVisibleItemScrollOffsetKey] ?: 0 + TimelineScrollState( + firstVisibleItemIndex = firstVisibleItemIndex, + firstVisibleItemScrollOffset = firstVisibleItemScrollOffset, + ) + } + }.asStateIn(viewModelScope, TimelineScrollState.Zero) + } fun loadBetween( maxStatusKey: MicroBlogKey, sinceStatueKey: MicroBlogKey, ) = viewModelScope.launch { - pagingMediator.loadBetween(defaultLoadCount, maxStatusKey = maxStatusKey, sinceStatusKey = sinceStatueKey) - } - - fun restoreScrollState(): TimelineScrollState { - return TimelineScrollState( - firstVisibleItemIndex = preferences.getInt("${savedStateKey}_firstVisibleItemIndex", 0), - firstVisibleItemScrollOffset = preferences.getInt("${savedStateKey}_firstVisibleItemScrollOffset", 0), + pagingMediator.lastOrNull()?.loadBetween( + defaultLoadCount, + maxStatusKey = maxStatusKey, + sinceStatusKey = sinceStatueKey ) } - fun saveScrollState(offset: TimelineScrollState) { - preferences.edit { - putInt("${savedStateKey}_firstVisibleItemIndex", offset.firstVisibleItemIndex) - putInt("${savedStateKey}_firstVisibleItemScrollOffset", offset.firstVisibleItemScrollOffset) + fun saveScrollState(offset: TimelineScrollState) = viewModelScope.launch { + dataStore.edit { + val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") + val firstVisibleItemScrollOffsetKey = + intPreferencesKey("${it}_firstVisibleItemScrollOffset") + it[firstVisibleItemIndexKey] = offset.firstVisibleItemIndex + it[firstVisibleItemScrollOffsetKey] = offset.firstVisibleItemScrollOffset } } } @@ -69,4 +94,8 @@ abstract class TimelineViewModel( data class TimelineScrollState( val firstVisibleItemIndex: Int = 0, val firstVisibleItemScrollOffset: Int = 0, -) +) { + companion object { + val Zero = TimelineScrollState(0, 0) + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 8990510e0..23f708aa5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -20,32 +20,41 @@ */ package com.twidere.twiderex.viewmodel.timeline.mastodon -import android.content.SharedPreferences +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope -class FederatedTimelineViewModel @AssistedInject constructor( - preferences: SharedPreferences, +class FederatedTimelineViewModel( + dataStore: DataStore, database: CacheDatabase, - @Assisted account: AccountDetails, -) : TimelineViewModel(preferences) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): FederatedTimelineViewModel + private val accountRepository: AccountRepository, +) : TimelineViewModel(dataStore) { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { - FederatedTimelineMediator( - account.service as MastodonService, - account.accountKey, - database, - ) + account.map { + it?.let { + FederatedTimelineMediator( + it.service as MastodonService, + it.accountKey, + database, + ) + } + } } - override val savedStateKey = "${account.accountKey}_federated" + override val savedStateKey = account.map { + it?.let { + "${it.accountKey}_federated" + } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 8b56dc661..4f67bbcb6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -20,32 +20,41 @@ */ package com.twidere.twiderex.viewmodel.timeline.mastodon -import android.content.SharedPreferences +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope -class LocalTimelineViewModel @AssistedInject constructor( - preferences: SharedPreferences, +class LocalTimelineViewModel( + dataStore: DataStore, database: CacheDatabase, - @Assisted account: AccountDetails, -) : TimelineViewModel(preferences) { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): LocalTimelineViewModel + private val accountRepository: AccountRepository, +) : TimelineViewModel(dataStore) { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { - LocalTimelineMediator( - account.service as MastodonService, - account.accountKey, - database, - ) + account.map { + it?.let { + LocalTimelineMediator( + it.service as MastodonService, + it.accountKey, + database, + ) + } + } } - override val savedStateKey = "${account.accountKey}_local" + override val savedStateKey = account.map { + it?.let { + "${it.accountKey}_local" + } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index ef0ef584d..bf84fe945 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -20,23 +20,30 @@ */ package com.twidere.twiderex.viewmodel.trend -import com.twidere.twiderex.model.AccountDetails +import androidx.paging.cachedIn +import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope -class TrendViewModel @AssistedInject constructor( +class TrendViewModel( private val repository: TrendRepository, - @Assisted private val account: AccountDetails, + private val accountRepository: AccountRepository, ) : ViewModel() { - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails): TrendViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val source by lazy { - repository.trendsSource(account) + account.flatMapLatest { + if (it != null) { + repository.trendsSource(it) + } else { + emptyFlow() + } + }.cachedIn(viewModelScope) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index efc9566ab..630457de0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -38,8 +38,6 @@ import com.twidere.twiderex.repository.ACCOUNT_TYPE import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json import com.twidere.twiderex.utils.notify -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -47,27 +45,16 @@ import moe.tlaster.precompose.viewmodel.viewModelScope import retrofit2.HttpException import java.io.IOException -class TwitterSignInViewModel @AssistedInject constructor( +class TwitterSignInViewModel( private val repository: AccountRepository, private val inAppNotification: InAppNotification, - @Assisted("consumerKey") private val consumerKey: String, - @Assisted("consumerSecret") private val consumerSecret: String, - @Assisted("oauthVerifierProvider") private val oauthVerifierProvider: suspend (url: String) -> String?, - @Assisted("pinCodeProvider") private val pinCodeProvider: suspend (url: String) -> String?, - @Assisted private val onResult: (success: Boolean) -> Unit, + private val consumerKey: String, + private val consumerSecret: String, + private val oauthVerifierProvider: suspend (url: String) -> String?, + private val pinCodeProvider: suspend (url: String) -> String?, + private val onResult: (success: Boolean) -> Unit, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - @Assisted("consumerKey") consumerKey: String, - @Assisted("consumerSecret") consumerSecret: String, - @Assisted("oauthVerifierProvider") oauthVerifierProvider: suspend (url: String) -> String?, - @Assisted("pinCodeProvider") pinCodeProvider: suspend (url: String) -> String?, - @Assisted onResult: (success: Boolean) -> Unit, - ): TwitterSignInViewModel - } - val success = MutableStateFlow(false) val loading = MutableStateFlow(false) From 37133719300e34961da365d5417c1ecdfe8e6d40 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 26 Aug 2021 16:37:35 +0800 Subject: [PATCH 089/615] migrate compose vm to fetch account from repo --- .../twiderex/scenes/compose/ComposeScene.kt | 100 +++--- .../viewmodel/compose/ComposeViewModel.kt | 287 ++++++++---------- 2 files changed, 189 insertions(+), 198 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d4ac9bf70..ab9a475fe 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -281,7 +281,9 @@ private fun ComposeBody( contentDescription = stringResource( id = if (enableThreadMode) R.string.accessibility_scene_compose_thread else R.string.accessibility_scene_compose_send ), - tint = if (canSend) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + tint = if (canSend) MaterialTheme.colors.primary else LocalContentColor.current.copy( + alpha = LocalContentAlpha.current + ) ) } } @@ -476,48 +478,42 @@ fun EmojiPanel( viewModel: ComposeViewModel, showEmoji: Boolean, ) { - viewModel.emojis?.let { emojis -> - val items by emojis.observeAsState(initial = null) - val ime = LocalWindowInsets.current.ime - val navigation = LocalWindowInsets.current.navigationBars - var height by remember { mutableStateOf(0) } - LaunchedEffect(ime) { - snapshotFlow { ime.bottom } - .distinctUntilChanged() - .filter { it > 0 } - .collect { height = max(height, it) } - } - val targetHeight = with(LocalDensity.current) { - height.toDp() - } - val bottom = with(LocalDensity.current) { - ime.bottom.coerceAtLeast(navigation.bottom).toDp() - } - var visibility by remember { mutableStateOf(false) } - LaunchedEffect(showEmoji, bottom) { - if (bottom == targetHeight || showEmoji) { - visibility = showEmoji - } - } - Box( - modifier = Modifier - .height( - height = if (visibility) { - (targetHeight - bottom).coerceAtLeast(0.dp) - } else { - 0.dp - } - ) - .fillMaxWidth(), - contentAlignment = Alignment.Center, - ) { - items?.let { items -> - EmojiList(items, viewModel) - } ?: run { - CircularProgressIndicator() - } + val items by viewModel.emojis.observeAsState(initial = emptyList()) + val ime = LocalWindowInsets.current.ime + val navigation = LocalWindowInsets.current.navigationBars + var height by remember { mutableStateOf(0) } + LaunchedEffect(ime) { + snapshotFlow { ime.bottom } + .distinctUntilChanged() + .filter { it > 0 } + .collect { height = max(height, it) } + } + val targetHeight = with(LocalDensity.current) { + height.toDp() + } + val bottom = with(LocalDensity.current) { + ime.bottom.coerceAtLeast(navigation.bottom).toDp() + } + var visibility by remember { mutableStateOf(false) } + LaunchedEffect(showEmoji, bottom) { + if (bottom == targetHeight || showEmoji) { + visibility = showEmoji } } + Box( + modifier = Modifier + .height( + height = if (visibility) { + (targetHeight - bottom).coerceAtLeast(0.dp) + } else { + 0.dp + } + ) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + EmojiList(items, viewModel) + } } @ExperimentalFoundationApi @@ -1219,8 +1215,9 @@ private fun ComposeActions( IconButton( onClick = { scope.launch { - val result = navController.navigateForResult(RootRoute.Compose.Search.User) - ?.toString() + val result = + navController.navigateForResult(RootRoute.Compose.Search.User) + ?.toString() if (!result.isNullOrEmpty()) { viewModel.insertText("$result ") } @@ -1288,7 +1285,9 @@ private fun ComposeActions( Icon( painter = painterResource(id = R.drawable.ic_thread_mode), contentDescription = stringResource(id = R.string.accessibility_scene_compose_thread), - tint = if (enableThreadMode) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + tint = if (enableThreadMode) MaterialTheme.colors.primary else LocalContentColor.current.copy( + alpha = LocalContentAlpha.current + ) ) } Spacer(modifier = Modifier.weight(1f)) @@ -1376,3 +1375,16 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { private object ComposeImageDefaults { val ImageSize = 72.dp } + +@Composable +fun VoteExpired.stringName(): String { + return when (this) { + VoteExpired.Min_5 -> stringResource(id = R.string.scene_compose_vote_expiration_5_Min) + VoteExpired.Min_30 -> stringResource(id = R.string.scene_compose_vote_expiration_30_Min) + VoteExpired.Hour_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Hour) + VoteExpired.Hour_6 -> stringResource(id = R.string.scene_compose_vote_expiration_6_Hour) + VoteExpired.Day_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Day) + VoteExpired.Day_3 -> stringResource(id = R.string.scene_compose_vote_expiration_3_Day) + VoteExpired.Day_7 -> stringResource(id = R.string.scene_compose_vote_expiration_7_Day) + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 915b8fda0..744569957 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -28,19 +28,16 @@ import android.location.LocationManager import android.net.Uri import android.os.Bundle import androidx.annotation.RequiresPermission -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.work.WorkManager import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.R import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.db.model.DbDraft +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.extensions.getCachedLocation import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.PlatformType @@ -48,6 +45,7 @@ import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiEmoji import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.repository.UserRepository @@ -55,15 +53,15 @@ import com.twidere.twiderex.utils.MastodonEmojiCache import com.twidere.twiderex.utils.notify import com.twidere.twiderex.worker.draft.SaveDraftWorker import com.twitter.twittertext.Extractor -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope import java.util.UUID @@ -75,9 +73,9 @@ enum class ComposeType { Thread, } -class DraftItemViewModel @AssistedInject constructor( +class DraftItemViewModel( private val repository: DraftRepository, - @Assisted private val draftId: String, + private val draftId: String, ) : ViewModel() { val draft = flow { @@ -85,16 +83,9 @@ class DraftItemViewModel @AssistedInject constructor( emit(it) } } - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - draftId: String, - ): DraftItemViewModel - } } -class DraftComposeViewModel @AssistedInject constructor( +class DraftComposeViewModel( draftRepository: DraftRepository, locationManager: LocationManager, composeAction: ComposeAction, @@ -102,8 +93,8 @@ class DraftComposeViewModel @AssistedInject constructor( userRepository: UserRepository, workManager: WorkManager, inAppNotification: InAppNotification, - @Assisted account: AccountDetails, - @Assisted private val draft: DbDraft, + accountRepository: AccountRepository, + draft: DbDraft, ) : ComposeViewModel( draftRepository, locationManager, @@ -112,7 +103,7 @@ class DraftComposeViewModel @AssistedInject constructor( userRepository, workManager, inAppNotification, - account, + accountRepository, draft.statusKey, draft.composeType, ) { @@ -124,14 +115,6 @@ class DraftComposeViewModel @AssistedInject constructor( putImages(draft.media.map { Uri.parse(it) }) excludedReplyUserIds.value = draft.excludedReplyUserIds ?: emptyList() } - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - draft: DbDraft, - ): DraftComposeViewModel - } } class VoteOption { @@ -148,20 +131,7 @@ enum class VoteExpired(val value: Long) { Hour_6(21600), Day_1(86400), Day_3(259200), - Day_7(604800); - - @Composable - fun stringName(): String { - return when (this) { - Min_5 -> stringResource(id = R.string.scene_compose_vote_expiration_5_Min) - Min_30 -> stringResource(id = R.string.scene_compose_vote_expiration_30_Min) - Hour_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Hour) - Hour_6 -> stringResource(id = R.string.scene_compose_vote_expiration_6_Hour) - Day_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Day) - Day_3 -> stringResource(id = R.string.scene_compose_vote_expiration_3_Day) - Day_7 -> stringResource(id = R.string.scene_compose_vote_expiration_7_Day) - } - } + Day_7(604800), } class VoteState { @@ -190,7 +160,7 @@ class VoteState { } } -open class ComposeViewModel @AssistedInject constructor( +open class ComposeViewModel( protected val draftRepository: DraftRepository, private val locationManager: LocationManager, protected val composeAction: ComposeAction, @@ -198,27 +168,26 @@ open class ComposeViewModel @AssistedInject constructor( private val userRepository: UserRepository, private val workManager: WorkManager, private val inAppNotification: InAppNotification, - @Assisted protected val account: AccountDetails, - @Assisted protected val statusKey: MicroBlogKey?, - @Assisted val composeType: ComposeType, + private val accountRepository: AccountRepository, + protected val statusKey: MicroBlogKey?, + val composeType: ComposeType, ) : ViewModel(), LocationListener { - open val draftId: String = UUID.randomUUID().toString() - - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - statusKey: MicroBlogKey?, - composeType: ComposeType - ): ComposeViewModel + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } + open val draftId: String = UUID.randomUUID().toString() + val emojis by lazy { - if (account.type == PlatformType.Mastodon) { - MastodonEmojiCache.get(account) - } else { - null - } + account.flatMapLatest { + it?.let { account -> + if (account.type == PlatformType.Mastodon) { + MastodonEmojiCache.get(account) + } else { + emptyFlow() + } + } ?: emptyFlow() + }.asStateIn(viewModelScope, emptyList()) } val draftCount by lazy { @@ -227,53 +196,49 @@ open class ComposeViewModel @AssistedInject constructor( val location = MutableStateFlow(null) val excludedReplyUserIds = MutableStateFlow>(emptyList()) - - val replyToUserName = flow { - if (account.type == PlatformType.Twitter && composeType == ComposeType.Reply && statusKey != null) { - emitAll( - status.map { - it?.let { status -> - Extractor().extractMentionedScreennames( - status.htmlText - ).filter { it != account.user.screenName && it != status.user.screenName } - } ?: run { - emptyList() - } - }, - ) - } else { - emit(emptyList()) - } + val replyToUserName by lazy { + combine(account, status) { account, status -> + if (account != null && status != null) { + if (account.type == PlatformType.Twitter && composeType == ComposeType.Reply && statusKey != null) { + Extractor().extractMentionedScreennames( + status.htmlText + ).filter { it != account.user.screenName && it != status.user.screenName } + } else { + emptyList() + } + } else { + emptyList() + } + }.asStateIn(viewModelScope, emptyList()) } val loadingReplyUser = MutableStateFlow(false) - val replyToUser = replyToUserName.map { - if (it.isNotEmpty()) { - loadingReplyUser.value = true - try { - userRepository.lookupUsersByName( - it, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - } catch (e: Throwable) { - e.notify(inAppNotification) + val replyToUser by lazy { + combine(account, replyToUserName) { account, list -> + if (account != null) { + if (list.isNotEmpty()) { + loadingReplyUser.value = true + try { + userRepository.lookupUsersByName( + list, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + } catch (e: Throwable) { + e.notify(inAppNotification) + emptyList() + } finally { + loadingReplyUser.value = false + } + } else { + emptyList() + } + } else { emptyList() - } finally { - loadingReplyUser.value = false } - } else { - emptyList() - } - // return a stateFlow to emit latest state - // WhileSubscribed will unsubscribe upstream flow when there is no subscribers - // official suggest use 5s as stop timeout - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000), - emptyList() - ) + }.asStateIn(viewModelScope, emptyList()) + } val voteState = MutableStateFlow(null) val isInVoteMode = MutableStateFlow(false) @@ -283,47 +248,53 @@ open class ComposeViewModel @AssistedInject constructor( val contentWarningTextFieldValue = MutableStateFlow(TextFieldValue()) val textFieldValue = MutableStateFlow(TextFieldValue()) val images = MutableStateFlow>(emptyList()) - val canSend = textFieldValue.combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - val canSaveDraft = - textFieldValue.combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } + val canSend = textFieldValue + .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } + .asStateIn(viewModelScope, false) + val canSaveDraft = textFieldValue + .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } + .asStateIn(viewModelScope, false) val locationEnabled = MutableStateFlow(false) val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) - val status = flow { - statusKey?.let { statusKey -> - emitAll( - repository.loadStatus(statusKey, accountKey = account.accountKey) - .map { status -> - if (status != null && - textFieldValue.value.text.isEmpty() && - status.platformType == PlatformType.Mastodon && - status.mastodonExtra?.mentions != null && - composeType == ComposeType.Reply - ) { - val mentions = - status.mastodonExtra.mentions.mapNotNull { it.acct } - .filter { it != account.user.screenName }.map { "@$it" }.let { - if (status.user.userKey != account.user.userKey) { - listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it - } else { - it - } - }.distinctBy { it }.takeIf { it.any() } - ?.joinToString(" ", postfix = " ") { it } - if (mentions != null) { - setText( - TextFieldValue( - mentions, - selection = TextRange(mentions.length) + val status by lazy { + account.flatMapLatest { + if (statusKey != null) { + it?.let { account -> + repository.loadStatus(statusKey, accountKey = account.accountKey) + .map { status -> + if (status != null && + textFieldValue.value.text.isEmpty() && + status.platformType == PlatformType.Mastodon && + status.mastodonExtra?.mentions != null && + composeType == ComposeType.Reply + ) { + val mentions = + status.mastodonExtra.mentions.mapNotNull { it.acct } + .filter { it != account.user.screenName }.map { "@$it" } + .let { + if (status.user.userKey != account.user.userKey) { + listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it + } else { + it + } + }.distinctBy { it }.takeIf { it.any() } + ?.joinToString(" ", postfix = " ") { it } + if (mentions != null) { + setText( + TextFieldValue( + mentions, + selection = TextRange(mentions.length) + ) ) - ) + } } + status } - status - } - ) - } ?: run { - emit(null) - } + } ?: flowOf(null) + } else { + flowOf(null) + } + }.asStateIn(viewModelScope, null) } fun setText(value: TextFieldValue) { @@ -359,13 +330,16 @@ open class ComposeViewModel @AssistedInject constructor( isInVoteMode.value = value } - fun compose() { - textFieldValue.value.text.let { - composeAction.commit( - account.accountKey, - account.type, - buildComposeData(it) - ) + fun compose() = viewModelScope.launch { + val account = account.lastOrNull() + if (account != null) { + textFieldValue.value.text.let { + composeAction.commit( + account.accountKey, + account.type, + buildComposeData(it) + ) + } } } @@ -399,7 +373,8 @@ open class ComposeViewModel @AssistedInject constructor( isThreadMode = enableThreadMode.value, ) - fun putImages(value: List) { + fun putImages(value: List) = viewModelScope.launch { + val imageLimit = imageLimit.lastOrNull() ?: 4 images.value.let { value + it }.take(imageLimit).let { @@ -407,13 +382,17 @@ open class ComposeViewModel @AssistedInject constructor( } } - private val imageLimit: Int - get() = when (account.type) { - PlatformType.Twitter -> 4 - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> 4 - } + private val imageLimit by lazy { + account.map { + when (it?.type) { + PlatformType.Twitter -> 4 + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> 4 + else -> 4 + } + }.asStateIn(viewModelScope, 4) + } @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION]) fun trackingLocation() { From 02a0a9e22e1df921512fe4fa937a86f4dd04ff49 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 26 Aug 2021 17:46:11 +0800 Subject: [PATCH 090/615] change DbPagingSource to LimitOffsetTransformPagingSource and unit test --- .../DirectMessageConversationDaoImplTest.kt | 24 +- .../db/DirectMessageEventDaoImplTest.kt | 22 +- .../twidere/twiderex/db/ListsDaoImplTest.kt | 15 +- .../twiderex/db/PagingTimelineDaoImplTest.kt | 26 +- .../twidere/twiderex/db/TrendDaoImplTest.kt | 22 +- .../LimitOffsetTransformPagingSourceTest.kt | 101 ++++++++ .../dao/RoomDirectMessageConversationDao.kt | 12 + .../room/db/dao/RoomDirectMessageEventDao.kt | 6 + .../twiderex/room/db/dao/RoomListsDao.kt | 6 + .../room/db/dao/RoomPagingTimelineDao.kt | 15 ++ .../twiderex/room/db/dao/RoomTrendDao.kt | 6 + .../twiderex/room/db/paging/DbPagingSource.kt | 63 ----- .../LimitOffsetTransformPagingSource.kt | 224 ++++++++++++++++++ .../room/db/paging/TablePagingSource.kt | 63 +++-- 14 files changed, 503 insertions(+), 102 deletions(-) create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt index 09dc3fcd3..d13fd3bf1 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt @@ -64,6 +64,23 @@ internal class DirectMessageConversationDaoImplTest : CacheDatabaseDaoTest() { ) } + @Test + fun getPagingListCount_ReturnsCountMatchesQuery() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other2") + .toUi(accountKey, mockIUser(id = "other2").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other3") + .toUi(MicroBlogKey.twitter("Not included"), mockIUser(id = "other3").toUi(MicroBlogKey.twitter("Not included"))), + ) + cacheDatabase.directMessageDao().insertAll(list) + cacheDatabase.directMessageConversationDao().insertAll(list.map { it.toConversation() }) + assertEquals(2, roomDatabase.directMessageConversationDao().getPagingList(accountKey, limit = 20, offset = 0).size) + assertEquals(2, roomDatabase.directMessageConversationDao().getPagingListCount(accountKey)) + } + @Test fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { val cacheDatabase = CacheDatabaseImpl(roomDatabase) @@ -99,8 +116,11 @@ internal class DirectMessageConversationDaoImplTest : CacheDatabaseDaoTest() { var invalidate = false cacheDatabase.directMessageConversationDao().getPagingSource( accountKey = accountKey, - ).registerInvalidatedCallback { - invalidate = true + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) } cacheDatabase.directMessageConversationDao().insertAll(listOf(conversation)) val start = System.currentTimeMillis() diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt index 6a87dcda5..964624c22 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt @@ -107,6 +107,21 @@ internal class DirectMessageEventDaoImplTest : CacheDatabaseDaoTest() { ) ) } + @Test + fun getPagingListCount_ReturnsCountMatchesQuery() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other3") + .toUi(accountKey, mockIUser(id = "other3").toUi(MicroBlogKey.twitter("Not included"))), + ) + cacheDatabase.directMessageDao().insertAll(list) + assertEquals(2, roomDatabase.directMessageDao().getPagingList(accountKey, limit = 20, offset = 0, conversationKey = list.first().conversationKey).size) + assertEquals(2, roomDatabase.directMessageDao().getPagingListCount(accountKey, conversationKey = list.first().conversationKey)) + } @Test fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { @@ -144,8 +159,11 @@ internal class DirectMessageEventDaoImplTest : CacheDatabaseDaoTest() { cacheDatabase.directMessageDao().getPagingSource( accountKey = accountKey, conversationKey = message.conversationKey - ).registerInvalidatedCallback { - invalidate = true + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) } cacheDatabase.directMessageDao().insertAll(listOf(message)) val start = System.currentTimeMillis() diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt index 2df09ce69..f804f5b8c 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt @@ -107,8 +107,11 @@ internal class ListsDaoImplTest : CacheDatabaseDaoTest() { var invalidate = false cacheDatabase.listsDao().getPagingSource( accountKey = accountKey - ).registerInvalidatedCallback { - invalidate = true + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) } cacheDatabase.listsDao().insertAll(listOf(mockIListModel().toUi(accountKey))) val start = System.currentTimeMillis() @@ -117,4 +120,12 @@ internal class ListsDaoImplTest : CacheDatabaseDaoTest() { } assert(invalidate) } + + @Test + fun getPagingListCount_ReturnsCountMatchesQuery() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.listsDao().insertAll(insertData + mockIListModel().toUi(MicroBlogKey.twitter("Not included"))) + assertEquals(insertData.size, roomDatabase.listsDao().getPagingListCount(accountKey)) + assertEquals(insertData.size, roomDatabase.listsDao().getPagingList(accountKey, limit = insertData.size + 10, offset = 0).size) + } } diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt index 7c430358f..8d963f986 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt @@ -35,6 +35,23 @@ import kotlin.test.assertNull internal class PagingTimelineDaoImplTest : CacheDatabaseDaoTest() { private val accountKey = MicroBlogKey.twitter("account") private val pagingKey = "pagingKey" + + @Test + fun getPagingListCount_ReturnsCountMatchesQuery() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + val originData = listOf( + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, pagingKey), + mockIStatus().toPagingTimeline(accountKey, "not included"), + ) + cacheDatabase.pagingTimelineDao().insertAll( + originData.map { it.timeline } + ) + cacheDatabase.statusDao().insertAll(listOf = originData.map { it.status }, accountKey = accountKey) + assertEquals(2, roomDatabase.pagingTimelineDao().getPagingListCount(accountKey = accountKey, pagingKey = pagingKey)) + assertEquals(2, roomDatabase.pagingTimelineDao().getPagingList(accountKey = accountKey, pagingKey = pagingKey, limit = 10, offset = 0).size) + } + @Test fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { @@ -52,7 +69,7 @@ internal class PagingTimelineDaoImplTest : CacheDatabaseDaoTest() { accountKey = accountKey, pagingKey = pagingKey ) - val limit = 3 + val limit = 2 val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) assert(result is PagingSource.LoadResult.Page) assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) @@ -70,8 +87,11 @@ internal class PagingTimelineDaoImplTest : CacheDatabaseDaoTest() { cacheDatabase.pagingTimelineDao().getPagingSource( accountKey = accountKey, pagingKey = pagingKey - ).registerInvalidatedCallback { - invalidate = true + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) } cacheDatabase.pagingTimelineDao().insertAll( listOf( diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt index e79138f31..70ed18ac6 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals -import kotlin.test.assertNull typealias TwitterTrend = com.twidere.services.twitter.model.Trend typealias MastodonTrend = com.twidere.services.mastodon.model.Trend @@ -88,6 +87,14 @@ internal class TrendDaoImplTest : CacheDatabaseDaoTest() { } } + @Test + fun getPagingListCount_ReturnsCountMatchesQuery() = runBlocking { + val cacheDatabase = CacheDatabaseImpl(roomDatabase) + cacheDatabase.trendDao().insertAll(trends) + assertEquals(twitterTrendCount, roomDatabase.trendDao().getPagingListCount(twitterAccountKey)) + assertEquals(twitterTrendCount, roomDatabase.trendDao().getPagingList(twitterAccountKey, limit = twitterTrendCount + 10, offset = 0).size) + } + @Test fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { val cacheDatabase = CacheDatabaseImpl(roomDatabase) @@ -102,12 +109,8 @@ internal class TrendDaoImplTest : CacheDatabaseDaoTest() { val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, twitterTrendCount / 2, false)) assert(loadMoreResult is PagingSource.LoadResult.Page) - assertEquals(twitterTrendCount, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) assertEquals(twitterTrendCount / 2, loadMoreResult.data.size) - - val noMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(loadMoreResult.nextKey ?: 0, twitterTrendCount / 2, false)) - assert(noMoreResult is PagingSource.LoadResult.Page) - assertNull((noMoreResult as PagingSource.LoadResult.Page).nextKey) } @Test @@ -116,8 +119,11 @@ internal class TrendDaoImplTest : CacheDatabaseDaoTest() { val cacheDatabase = CacheDatabaseImpl(roomDatabase) cacheDatabase.trendDao().getPagingSource( accountKey = twitterAccountKey - ).registerInvalidatedCallback { - invalidate = true + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) } cacheDatabase.trendDao().insertAll(listOf(mockITrend().toUi(twitterAccountKey))) val start = System.currentTimeMillis() diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt new file mode 100644 index 000000000..caf37bc68 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt @@ -0,0 +1,101 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging + +import androidx.paging.PagingSource +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import com.twidere.twiderex.room.db.RoomCacheDatabase +import com.twidere.twiderex.room.db.paging.LimitOffsetTransformPagingSource +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class LimitOffsetTransformPagingSourceTest { + private val count = 30 + + private val pagingSource = LimitOffsetTransformPagingSource( + loadPagingList = { offset, limit -> + val list = mutableListOf() + for (i in 0 until if (offset + limit <= count)limit else (count - offset)) { + list.add(Model(name = (offset + i).toString())) + } + list + }, + queryItemCount = { + count + }, + db = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java).build() + ) + @Test + fun refreshReturnsResultPageWithCorrectParams() = runBlocking { + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(key = null, loadSize = 15, placeholdersEnabled = false)) + assert(result is PagingSource.LoadResult.Page) + if (result !is PagingSource.LoadResult.Page) return@runBlocking + assertEquals(null, result.prevKey) + assertEquals(15, result.nextKey) + assertEquals(0, result.itemsBefore) + assertEquals(15, result.itemsAfter) + assertEquals(15, result.data.size) + + val noMoreResult = pagingSource.load(params = PagingSource.LoadParams.Refresh(key = null, loadSize = count, placeholdersEnabled = false)) + assert(noMoreResult is PagingSource.LoadResult.Page) + if (noMoreResult is PagingSource.LoadResult.Page) { + assertNull(noMoreResult.prevKey) + assertNull(noMoreResult.nextKey) + assertEquals(0, noMoreResult.itemsBefore) + assertEquals(0, noMoreResult.itemsAfter) + assertEquals(count, noMoreResult.data.size) + } + } + + @Test + fun appendReturnsResultPageWithCorrectParams() = runBlocking { + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(key = null, loadSize = 15, placeholdersEnabled = false)) + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(key = (result as PagingSource.LoadResult.Page).nextKey ?: 0, loadSize = 15, placeholdersEnabled = false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + if (loadMoreResult is PagingSource.LoadResult.Page) { + assertEquals(15, loadMoreResult.prevKey) + assertNull(loadMoreResult.nextKey) + assertEquals(15, loadMoreResult.itemsBefore) + assertEquals(0, loadMoreResult.itemsAfter) + assertEquals(15, loadMoreResult.data.size) + } + } + + @Test + fun prependReturnsResultPageWithCorrectParams() = runBlocking { + val prependResult = pagingSource.load(params = PagingSource.LoadParams.Prepend(key = 10, loadSize = 5, placeholdersEnabled = false)) + assert(prependResult is PagingSource.LoadResult.Page) + if (prependResult is PagingSource.LoadResult.Page) { + assertEquals(5, prependResult.prevKey) + assertEquals(10, prependResult.nextKey) + assertEquals(5, prependResult.itemsBefore) + assertEquals(20, prependResult.itemsAfter) + assertEquals(5, prependResult.data.size) + } + } +} + +private data class Model( + val name: String +) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index ea1f35562..7bd80b572 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -63,6 +63,18 @@ internal interface RoomDirectMessageConversationDao { offset: Int ): List + @Transaction + @Query( + """ + SELECT COUNT(*) FROM( + SELECT * FROM dm_event AS table1 + JOIN (SELECT conversationKey, max(sortId) as sortId FROM dm_event WHERE accountKey == :accountKey GROUP BY conversationKey) AS table2 + ON table1.conversationKey = table2.conversationKey AND table1.sortId = table2.sortId + WHERE table1.accountKey == :accountKey ORDER BY table1.sortId DESC) + """ + ) + suspend fun getPagingListCount(accountKey: MicroBlogKey): Int + @Query("SELECT * FROM dm_conversation WHERE accountKey == :accountKey AND conversationKey == :conversationKey") fun findWithConversationKeyFlow(accountKey: MicroBlogKey, conversationKey: MicroBlogKey): Flow diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index 547e46e94..3fc012da9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -55,6 +55,12 @@ internal interface RoomDirectMessageEventDao { limit: Int, offset: Int ): List + @Transaction + @Query("SELECT COUNT(*) FROM (SELECT * FROM dm_event WHERE accountKey == :accountKey AND conversationKey == :conversationKey ORDER BY sortId DESC)") + suspend fun getPagingListCount( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + ): Int @Delete suspend fun delete(data: DbDMEvent) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index c409d0f35..13572b628 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -56,6 +56,12 @@ internal interface RoomListsDao { offset: Int ): List + @Transaction + @Query("SELECT COUNT(*) FROM (SELECT * FROM lists WHERE accountKey == :accountKey)") + suspend fun getPagingListCount( + accountKey: MicroBlogKey, + ): Int + @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun update(lists: List) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index 60106c884..c6f28bed8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.room.db.dao +import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert @@ -51,6 +52,20 @@ internal interface RoomPagingTimelineDao { offset: Int ): List + @Transaction + @Query("SELECT COUNT(*) FROM (SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY sortId DESC)") + suspend fun getPagingListCount( + pagingKey: String, + accountKey: MicroBlogKey, + ): Int + + @Transaction + @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY sortId DESC") + fun getPagingSourceYeah( + pagingKey: String, + accountKey: MicroBlogKey, + ): PagingSource + @Transaction @Query("SELECT * FROM paging_timeline WHERE pagingKey == :pagingKey AND accountKey == :accountKey ORDER BY timestamp DESC") fun getLatest(pagingKey: String, accountKey: MicroBlogKey): DbPagingTimelineWithStatus? diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index 3656d337c..c05c4e45d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -46,6 +46,12 @@ internal interface RoomTrendDao { offset: Int ): List + @Transaction + @Query("SELECT COUNT(*) FROM (SELECT * FROM trends WHERE accountKey == :accountKey)") + suspend fun getPagingListCount( + accountKey: MicroBlogKey + ): Int + @Query("DELETE FROM trends WHERE accountKey == :accountKey") suspend fun clearAll( accountKey: MicroBlogKey, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt deleted file mode 100644 index 459005ca9..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/DbPagingSource.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.room.db.paging - -import android.annotation.SuppressLint -import androidx.paging.PagingSource -import androidx.paging.PagingState -import androidx.room.InvalidationTracker -import androidx.room.RoomDatabase - -@SuppressLint("RestrictedApi") -internal class DbPagingSource( - roomDatabase: RoomDatabase, - private val loadFromDb: suspend (offset: Int, limit: Int) -> List, - vararg tables: String -) : PagingSource() { - private val databaseObserver = object : InvalidationTracker.Observer(tables) { - override fun onInvalidated(tables: MutableSet) { - invalidate() - } - } - - init { - roomDatabase.invalidationTracker.addWeakObserver(databaseObserver) - } - - override fun getRefreshKey(state: PagingState): Int? { - return 0 - } - - override suspend fun load(params: LoadParams): LoadResult { - return try { - val list = loadFromDb(params.key ?: 0, params.loadSize) - val nextKey = if (list.size < params.loadSize) null else (params.key ?: 0) + list.size - LoadResult.Page( - data = list, - prevKey = null, - nextKey = nextKey - ) - } catch (e: Throwable) { - e.printStackTrace() - LoadResult.Error(e) - } - } -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt new file mode 100644 index 000000000..c89b4b839 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt @@ -0,0 +1,224 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.room.db.paging + +import android.annotation.SuppressLint +import androidx.paging.PagingSource +import androidx.paging.PagingState +import androidx.room.InvalidationTracker +import androidx.room.RoomDatabase +import androidx.room.withTransaction +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +/** + * An implementation of [PagingSource] to perform a LIMIT OFFSET query + * + * This class is used for Paging3 to perform Query and RawQuery in Room to return a PagingSource + * for Pager's consumption. Registers observers on tables lazily and automatically invalidates + * itself when data changes. + */ + +private val INVALID = PagingSource.LoadResult.Invalid() + +internal class LimitOffsetTransformPagingSource( + /** + * returns items from database matches offset and limit + */ + private val loadPagingList: suspend (offset: Int, limit: Int) -> List, + /** + * returns count of requested items to calculate itemsAfter and itemsBefore for use in creating + * LoadResult.Page<> + */ + private val queryItemCount: suspend () -> Int, + private val db: RoomDatabase, + vararg tables: String +) : PagingSource() { + + private val itemCount: AtomicInteger = AtomicInteger(-1) + + private val observer = object : InvalidationTracker.Observer(tables) { + override fun onInvalidated(tables: MutableSet) { + invalidate() + } + } + private val registeredObserver: AtomicBoolean = AtomicBoolean(false) + + @SuppressLint("RestrictedApi") + override suspend fun load(params: LoadParams): LoadResult { + return withContext(Dispatchers.IO) { + registerObserverIfNecessary() + val tempCount = itemCount.get() + // if itemCount is < 0, then it is initial load + if (tempCount < 0) { + initialLoad(params) + } else { + // otherwise, it is a subsequent load + val loadResult = loadFromDb(params, tempCount) + // manually check if database has been updated. If so, the observers's + // invalidation callback will invalidate this paging source + db.invalidationTracker.refreshVersionsSync() + @Suppress("UNCHECKED_CAST") + if (invalid) INVALID as LoadResult.Invalid else loadResult + } + } + } + + /** + * For the very first time that this PagingSource's [load] is called. Executes the count + * query (initializes [itemCount]) and db query within a transaction to ensure initial load's + * data integrity. + * + * For example, if the database gets updated after the count query but before the db query + * completes, the paging source may not invalidate in time, but this method will return + * data based on the original database that the count was performed on to ensure a valid + * initial load. + */ + private suspend fun initialLoad(params: LoadParams): LoadResult { + return db.withTransaction { + val tempCount = queryItemCount() + itemCount.set(tempCount) + loadFromDb(params, tempCount) + } + } + + private suspend fun loadFromDb( + params: LoadParams, + itemCount: Int, + ): LoadResult { + val key = params.key ?: 0 + val limit: Int = getLimit(params, key) + val offset: Int = getOffset(params, key, itemCount) + return queryDatabase(offset, limit, itemCount) + } + + /** + * Calculates query limit based on LoadType. + * + * Prepend: If requested loadSize is larger than available number of items to prepend, it will + * query with OFFSET = 0, LIMIT = prevKey + */ + private fun getLimit(params: LoadParams, key: Int): Int { + return when (params) { + is LoadParams.Prepend -> + if (key < params.loadSize) key else params.loadSize + else -> params.loadSize + } + } + + /** + * calculates query offset amount based on loadtype + * + * Prepend: OFFSET is calculated by counting backwards the number of items that needs to be + * loaded before [key]. For example, if key = 30 and loadSize = 5, then offset = 25 and items + * in db position 26-30 are loaded. + * If requested loadSize is larger than the number of available items to + * prepend, OFFSET clips to 0 to prevent negative OFFSET. + * + * Refresh: + * If initialKey is supplied through Pager, Paging 3 will now start loading from + * initialKey with initialKey being the first item. + * If key is supplied by [getRefreshKey],OFFSET will attempt to load around the anchorPosition + * with anchorPosition being the middle item. See comments on [getRefreshKey] for more details. + * If key (regardless if from initialKey or [getRefreshKey]) is larger than available items, + * the last page will be loaded by counting backwards the loadSize before last item in + * database. For example, this can happen if invalidation came from a large number of items + * dropped. i.e. in items 0 - 100, items 41-80 are dropped. Depending on last + * viewed item, hypothetically [getRefreshKey] may return key = 60. If loadSize = 10, then items + * 31-40 will be loaded. + */ + private fun getOffset(params: LoadParams, key: Int, itemCount: Int): Int { + return when (params) { + is LoadParams.Prepend -> + if (key < params.loadSize) 0 else (key - params.loadSize) + is LoadParams.Append -> key + is LoadParams.Refresh -> + if (key >= itemCount) { + maxOf(0, itemCount - params.loadSize) + } else { + key + } + } + } + + /** + * calls RoomDatabase.query() to return a cursor and then calls convertRows() to extract and + * return list of data + * + * throws [IllegalArgumentException] from [CursorUtil] if column does not exist + * + * @param offset offset parameter for LIMIT/OFFSET query. Bounded within user-supplied offset + * if it is supplied + * + * @param limit limit parameter for LIMIT/OFFSET query. Bounded within user-supplied limit + * if it is supplied + */ + private suspend fun queryDatabase( + offset: Int, + limit: Int, + itemCount: Int, + ): LoadResult { + val data = loadPagingList(offset, limit) + val nextPosToLoad = offset + data.size + val nextKey = + if (data.isEmpty() || data.size < limit || nextPosToLoad >= itemCount) { + null + } else { + nextPosToLoad + } + val prevKey = if (offset <= 0 || data.isEmpty()) null else offset + return LoadResult.Page( + data = data, + prevKey = prevKey, + nextKey = nextKey, + itemsBefore = offset, + itemsAfter = maxOf(0, itemCount - nextPosToLoad) + ) + } + + @SuppressLint("RestrictedApi") + private fun registerObserverIfNecessary() { + if (registeredObserver.compareAndSet(false, true)) { + db.invalidationTracker.addWeakObserver(observer) + } + } + + /** + * It is unknown whether anchorPosition represents the item at the top of the screen or item at + * the bottom of the screen. To ensure the number of items loaded is enough to fill up the + * screen, half of loadSize is loaded before the anchorPosition and the other half is + * loaded after the anchorPosition -- anchorPosition becomes the middle item. + * + * To prevent a negative key, key = 0 when the number of items available before anchorPosition + * is less than the requested amount of initialLoadSize / 2. + */ + override fun getRefreshKey(state: PagingState): Int? { + val initialLoadSize = state.config.initialLoadSize + return when { + state.anchorPosition == null -> null + else -> maxOf(0, state.anchorPosition!! - (initialLoadSize / 2)) + } + } + + override val jumpingSupported: Boolean + get() = true +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt index 18e296a8d..6960aa6d0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt @@ -33,26 +33,31 @@ import com.twidere.twiderex.room.db.transform.toUi internal fun RoomDirectMessageConversationDao.getPagingSource( cacheDatabase: RoomCacheDatabase, accountKey: MicroBlogKey -) = DbPagingSource( - roomDatabase = cacheDatabase, - loadFromDb = { offset, limit -> +) = LimitOffsetTransformPagingSource( + db = cacheDatabase, + loadPagingList = { offset, limit -> getPagingList( accountKey = accountKey, limit = limit, offset = offset ).map { it.toUi() } }, - "dm_conversation", - "dm_event" + queryItemCount = { + getPagingListCount(accountKey = accountKey) + }, + tables = arrayOf( + "dm_conversation", + "dm_event" + ) ) internal fun RoomDirectMessageEventDao.getPagingSource( cacheDatabase: RoomCacheDatabase, accountKey: MicroBlogKey, conversationKey: MicroBlogKey -) = DbPagingSource( - roomDatabase = cacheDatabase, - loadFromDb = { offset, limit -> +) = LimitOffsetTransformPagingSource( + db = cacheDatabase, + loadPagingList = { offset, limit -> getPagingList( accountKey = accountKey, conversationKey = conversationKey, @@ -60,31 +65,37 @@ internal fun RoomDirectMessageEventDao.getPagingSource( offset = offset ).map { it.toUi() } }, - "dm_event" + queryItemCount = { + getPagingListCount(accountKey = accountKey, conversationKey = conversationKey) + }, + tables = arrayOf("dm_event") ) internal fun RoomListsDao.getPagingSource( cacheDatabase: RoomCacheDatabase, accountKey: MicroBlogKey, -) = DbPagingSource( - roomDatabase = cacheDatabase, - loadFromDb = { offset, limit -> +) = LimitOffsetTransformPagingSource( + db = cacheDatabase, + loadPagingList = { offset, limit -> getPagingList( accountKey = accountKey, limit = limit, offset = offset ).map { it.toUi() } }, - "lists" + queryItemCount = { + getPagingListCount(accountKey = accountKey) + }, + tables = arrayOf("lists") ) internal fun RoomPagingTimelineDao.getPagingSource( cacheDatabase: RoomCacheDatabase, pagingKey: String, accountKey: MicroBlogKey, -) = DbPagingSource( - roomDatabase = cacheDatabase, - loadFromDb = { offset, limit -> +) = LimitOffsetTransformPagingSource( + db = cacheDatabase, + loadPagingList = { offset, limit -> getPagingList( pagingKey = pagingKey, accountKey = accountKey, @@ -92,21 +103,29 @@ internal fun RoomPagingTimelineDao.getPagingSource( limit = limit ).map { it.toPagingTimeline(accountKey = accountKey) } }, - "paging_timeline" + queryItemCount = { + getPagingListCount(accountKey = accountKey, pagingKey = pagingKey) + }, + tables = arrayOf("paging_timeline") ) internal fun RoomTrendDao.getPagingSource( cacheDatabase: RoomCacheDatabase, accountKey: MicroBlogKey, -) = DbPagingSource( - roomDatabase = cacheDatabase, - loadFromDb = { offset, limit -> +) = LimitOffsetTransformPagingSource( + db = cacheDatabase, + loadPagingList = { offset, limit -> getPagingList( accountKey = accountKey, limit = limit, offset = offset ).map { it.toUi(accountKey = accountKey) } }, - "trends", - "trend_histories" + queryItemCount = { + getPagingListCount(accountKey = accountKey) + }, + tables = arrayOf( + "trends", + "trend_histories" + ) ) From aa94b4f00f1b2296833bc7d77b38fd4b27ab7935 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 27 Aug 2021 13:21:44 +0800 Subject: [PATCH 091/615] add selectionContainer to StatusText --- .../twiderex/component/status/StatusText.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt index 38b120880..597edc6a2 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Icon import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme @@ -85,14 +86,16 @@ fun ColumnScope.StatusText( } AnimatedVisibility(visible = expanded) { Column { - HtmlText( - modifier = Modifier.fillMaxWidth(), - htmlText = status.htmlText, - maxLines = maxLines, - linkResolver = { href -> - status.resolveLink(href) - }, - ) + SelectionContainer { + HtmlText( + modifier = Modifier.fillMaxWidth(), + htmlText = status.htmlText, + maxLines = maxLines, + linkResolver = { href -> + status.resolveLink(href) + }, + ) + } if (showMastodonPoll && status.platformType == PlatformType.Mastodon && status.poll != null) { Spacer(modifier = Modifier.height(StatusTextDefaults.Mastodon.PollSpacing)) MastodonPoll(status) From 28dbbd4ff67e53e08ce2602dfa93c79da1c84fda Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 13:30:03 +0800 Subject: [PATCH 092/615] fix merge --- .../twiderex/viewmodel/compose/ComposeViewModel.kt | 12 +++++++----- .../viewmodel/mastodon/MastodonHashtagViewModel.kt | 6 +++--- .../twiderex/viewmodel/settings/LayoutViewModel.kt | 7 ------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 5e104a4ec..44680df7d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction -import com.twidere.twiderex.db.model.DbDraft import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.extensions.getCachedLocation import com.twidere.twiderex.extensions.getTextAfterSelection @@ -264,15 +263,18 @@ open class ComposeViewModel( composeType == ComposeType.Reply ) { val mentions = - status.mastodonExtra.mentions.mapNotNull { it.acct } - .filter { it != account.user.screenName }.map { "@$it" } - .let { + status.mastodonExtra?.mentions?.mapNotNull { it.acct } + ?.filter { it != account.user.screenName } + ?.map { "@$it" } + ?.let { if (status.user.userKey != account.user.userKey) { listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it } else { it } - }.distinctBy { it }.takeIf { it.any() } + } + ?.distinctBy { it } + ?.takeIf { it.any() } ?.joinToString(" ", postfix = " ") { it } if (mentions != null) { setText( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 12ca088a3..3c6cc4fd6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -21,9 +21,9 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.cachedIn +import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.repository.AccountRepository -import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -44,8 +44,8 @@ class MastodonHashtagViewModel( it?.let { repository.mastodonHashtagTimeline( keyword = keyword, - accountKey = account.accountKey, - service = account.service as MastodonService + accountKey = it.accountKey, + service = it.service as MastodonService ) } ?: emptyFlow() }.cachedIn(viewModelScope) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index be31b0ec5..e7ac98447 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -78,13 +78,6 @@ class LayoutViewModel( ) } - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create( - account: AccountDetails, - ): LayoutViewModel - } - val user by lazy { account.map { it?.toUi() From eb9fa243205953d285d7fe4fa44945a48dbe5c93 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 15:12:01 +0800 Subject: [PATCH 093/615] migrate vm && account repo to common --- .../com/twidere/twiderex/TwidereXActivity.kt | 2 +- .../twidere/twiderex/di/RepositoryModule.kt | 18 +++- .../com/twidere/twiderex/scenes/HomeScene.kt | 3 +- .../scenes/lists/ListsTimelineScene.kt | 1 - .../twiderex/scenes/settings/LayoutScene.kt | 3 +- .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 3 +- common/build.gradle.kts | 2 + common/src/androidMain/AndroidManifest.xml | 10 ++- .../model/AccountPreferencesFactory.kt | 51 +++++++++++ .../twiderex/repository/AccountRepository.kt | 43 +++++---- .../repository/AccountUpdateRepository.kt | 29 ------ .../twidere/twiderex/model/AccountDetails.kt | 0 .../twiderex/model/AccountPreferences.kt | 31 +------ .../com/twidere/twiderex/model}/HomeMenus.kt | 5 +- .../twiderex/repository/AccountRepository.kt | 59 ++++++++++++ .../repository/AccountUpdateRepository.kt | 35 -------- .../twiderex/repository/UserRepository.kt | 2 +- .../viewmodel/ActiveAccountViewModel.kt | 0 .../twiderex/viewmodel/DraftViewModel.kt | 3 +- .../twiderex/viewmodel/MediaViewModel.kt | 3 +- .../twiderex/viewmodel/PureMediaViewModel.kt | 0 .../twiderex/viewmodel/StatusViewModel.kt | 3 +- .../compose/ComposeSearchUserViewModel.kt | 0 .../viewmodel/compose/ComposeViewModel.kt | 0 .../MastodonComposeSearchHashtagViewModel.kt | 0 .../viewmodel/dm/DMConversationViewModel.kt | 0 .../twiderex/viewmodel/dm/DMEventViewModel.kt | 0 .../dm/DMNewConversationViewModel.kt | 0 .../lists/ListsSearchUserViewModel.kt | 0 .../viewmodel/lists/ListsTimelineViewModel.kt | 2 +- .../viewmodel/lists/ListsUserViewModel.kt | 3 +- .../viewmodel/lists/ListsViewModel.kt | 2 +- .../mastodon/MastodonHashtagViewModel.kt | 0 .../MastodonSearchHashtagViewModel.kt | 0 .../mastodon/MastodonSignInViewModel.kt | 0 .../viewmodel/search/SearchInputViewModel.kt | 2 +- .../viewmodel/search/SearchSaveViewModel.kt | 0 .../viewmodel/search/SearchTweetsViewModel.kt | 0 .../viewmodel/search/SearchUserViewModel.kt | 0 .../settings/AccountNotificationViewModel.kt | 0 .../viewmodel/settings/AppearanceViewModel.kt | 0 .../viewmodel/settings/DisplayViewModel.kt | 0 .../viewmodel/settings/LayoutViewModel.kt | 1 - .../viewmodel/settings/MiscViewModel.kt | 0 .../settings/NotificationViewModel.kt | 0 .../viewmodel/settings/StorageViewModel.kt | 0 .../timeline/HomeTimelineViewModel.kt | 0 .../timeline/MentionsTimelineViewModel.kt | 2 - .../timeline/NotificationTimelineViewModel.kt | 1 - .../viewmodel/timeline/TimelineViewModel.kt | 0 .../mastodon/FederatedTimelineViewModel.kt | 0 .../mastodon/LocalTimelineViewModel.kt | 0 .../viewmodel/trend/TrendViewModel.kt | 0 .../twitter/TwitterSignInViewModel.kt | 0 .../search/TwitterSearchMediaViewModel.kt | 0 .../twitter/user/TwitterUserViewModel.kt | 0 .../viewmodel/user/FollowersViewModel.kt | 0 .../viewmodel/user/FollowingViewModel.kt | 0 .../user/UserFavouriteTimelineViewModel.kt | 2 +- .../viewmodel/user/UserListViewModel.kt | 0 .../user/UserMediaTimelineViewModel.kt | 0 .../viewmodel/user/UserTimelineViewModel.kt | 0 .../twiderex/viewmodel/user/UserViewModel.kt | 0 .../AccountPreferencesFactory.kt} | 10 +-- .../twiderex/repository/AccountRepository.kt | 90 +++++++++++++++++++ 65 files changed, 270 insertions(+), 151 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/repository/AccountRepository.kt (86%) delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/AccountDetails.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/AccountPreferences.kt (74%) rename {android/src/main/kotlin/com/twidere/twiderex/scenes/home => common/src/commonMain/kotlin/com/twidere/twiderex/model}/HomeMenus.kt (90%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt delete mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt (99%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt (98%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt (100%) rename common/src/desktopMain/kotlin/com/twidere/twiderex/{repository/AccountUpdateRepository.kt => model/AccountPreferencesFactory.kt} (79%) create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 1b805e0b7..1f975c144 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -181,7 +181,7 @@ class TwidereXActivity : ComponentActivity() { private fun App() { val windowInsetsControllerCompat = remember { WindowInsetsControllerCompat(window, window.decorView) } - val accountViewModel = com.twidere.twiderex.di.ext.getViewModel() + val accountViewModel = com.twidere.twiderex.di.ext.getViewModel() val account by accountViewModel.account.observeAsState(null) val isActiveNetworkMetered by isActiveNetworkMetered.observeAsState(initial = false) CompositionLocalProvider( diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index 60062497d..b996f9a02 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -20,11 +20,14 @@ */ package com.twidere.twiderex.di +import android.accounts.AccountManager +import android.content.Context import com.twidere.services.nitter.NitterService import com.twidere.twiderex.cache.FileCacheHandler import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.repository.AccountUpdateRepository +import com.twidere.twiderex.model.AccountPreferencesFactory +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.CacheRepository import com.twidere.twiderex.repository.DirectMessageRepository import com.twidere.twiderex.repository.DraftRepository @@ -41,16 +44,23 @@ import com.twidere.twiderex.repository.UserRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object RepositoryModule { + @Provides + fun provideAccountPreferencesFactory(@ApplicationContext context: Context): AccountPreferencesFactory = + AccountPreferencesFactory(context = context) + @Singleton @Provides - fun provideAccountUpdateRepository(): AccountUpdateRepository = - AccountUpdateRepository() + fun provideAccountRepository( + accountPreferencesFactory: AccountPreferencesFactory, + accountManager: AccountManager, + ): AccountRepository = AccountRepository(accountManager, accountPreferencesFactory) @Singleton @Provides @@ -92,7 +102,7 @@ object RepositoryModule { @Provides fun provideUserRepository( database: CacheDatabase, - accountRepository: AccountUpdateRepository + accountRepository: AccountRepository ): UserRepository = UserRepository(database = database, accountRepository = accountRepository) @Provides diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index bab6ec1b5..e67fbd85d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.scenes import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.updateTransition import androidx.compose.animation.expandVertically @@ -90,11 +89,11 @@ import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation +import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences -import com.twidere.twiderex.scenes.home.HomeMenus import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalNavController diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index 3c975a342..6bbd1358f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -51,7 +51,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState -import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index 8b88d37ac..974e64dac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -56,8 +56,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.di.assisted.assistedViewModel -import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.scenes.home.HomeMenus +import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.LayoutViewModel diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt index 565a1dc44..65d9186e5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.staticCompositionLocalOf import androidx.core.view.WindowInsetsControllerCompat import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import moe.tlaster.precompose.navigation.NavController val LocalWindowInsetsController = @@ -37,7 +36,7 @@ val LocalWindow = staticCompositionLocalOf { error("No Window") } val LocalNavController = staticCompositionLocalOf { error("No NavController") } val LocalActiveAccount = compositionLocalOf { null } val LocalActiveAccountViewModel = - compositionLocalOf { error("No ActiveAccountViewModel") } + compositionLocalOf { error("No ActiveAccountViewModel") } val LocalApplication = staticCompositionLocalOf { error("No Application") } val LocalActivity = staticCompositionLocalOf { error("NoActivity") } val LocalVideoPlayback = compositionLocalOf { DisplayPreferences.AutoPlayback.Auto } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 9459571ab..3addb38d0 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -64,6 +64,8 @@ kotlin { implementation("androidx.room:room-paging:${Versions.room}") kapt("androidx.room:room-compiler:${Versions.room}") implementation("io.coil-kt:coil-base:${Versions.coil}") + api("androidx.datastore:datastore:${Versions.datastore}") + api("androidx.datastore:datastore-preferences:${Versions.datastore}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/AndroidManifest.xml b/common/src/androidMain/AndroidManifest.xml index 66290fb44..e6c3f9ac5 100644 --- a/common/src/androidMain/AndroidManifest.xml +++ b/common/src/androidMain/AndroidManifest.xml @@ -1,2 +1,10 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt new file mode 100644 index 000000000..6d495bd8a --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model + +import android.content.Context +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.preferencesDataStoreFile +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +actual class AccountPreferencesFactory( + private val context: Context, +) { + actual fun create(accountKey: MicroBlogKey) = createAccountPreferences(context, accountKey) + + private fun createAccountPreferences( + context: Context, + accountKey: MicroBlogKey, + ): AccountPreferences { + val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + return AccountPreferences( + dataStore = PreferenceDataStoreFactory.create( + corruptionHandler = null, + migrations = listOf(), + scope = scope + ) { + context.applicationContext.preferencesDataStoreFile(accountKey.toString()) + }, + scope = scope + ) + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt similarity index 86% rename from android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index d575a9c66..bbf2070a5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -25,20 +25,20 @@ import android.accounts.AccountManager import android.os.Build import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences +import com.twidere.twiderex.model.AccountPreferencesFactory import com.twidere.twiderex.model.AmUser import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.room.db.transform.toAndroid import com.twidere.twiderex.room.db.transform.toTwidere import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow -import javax.inject.Inject -import javax.inject.Singleton -const val ACCOUNT_TYPE = "com.twidere.twiderex.account" +private const val ACCOUNT_TYPE = "com.twidere.twiderex.account" private const val ACCOUNT_AUTH_TOKEN_TYPE = "com.twidere.twiderex.account.token" private const val ACCOUNT_USER_DATA_KEY = "key" private const val ACCOUNT_USER_DATA_TYPE = "type" @@ -51,16 +51,21 @@ private const val ACCOUNT_USER_DATA_POSITION = "position" private const val ACCOUNT_USER_DATA_TEST = "test" private const val ACCOUNT_USER_DATA_LAST_ACTIVE = "last_active" -@Singleton -class AccountRepository @Inject constructor( +actual class AccountRepository( private val manager: AccountManager, - private val accountPreferencesFactory: AccountPreferences.Factory, + private val accountPreferencesFactory: AccountPreferencesFactory, ) { + actual fun updateAccount(user: UiUser) { + findByAccountKey(user.userKey)?.let { + getAccountDetails(it).copy() + } + } + private val preferencesCache = linkedMapOf() private val _activeAccount = MutableStateFlow(if (hasAccount()) getCurrentAccount() else null) - val activeAccount + actual val activeAccount get() = _activeAccount.asSharedFlow() private val _accounts = MutableStateFlow( @@ -69,19 +74,19 @@ class AccountRepository @Inject constructor( } ) - val accounts + actual val accounts get() = _accounts.asSharedFlow() - fun getAccounts(): List { + actual fun getAccounts(): List { return manager.getAccountsByType(ACCOUNT_TYPE).toList() } - fun hasAccount(): Boolean { + actual fun hasAccount(): Boolean { return getAccounts().isNotEmpty() } // Note that UserKey that being used in AccountRepository is idStr@domain, not screenName@domain - fun findByAccountKey(accountKey: MicroBlogKey): Account? { + actual fun findByAccountKey(accountKey: MicroBlogKey): Account? { for (account in getAccounts()) { if (accountKey == getAccountKey(account)) { return account @@ -90,7 +95,7 @@ class AccountRepository @Inject constructor( return null } - fun setCurrentAccount(detail: AccountDetails) { + actual fun setCurrentAccount(detail: AccountDetails) { detail.lastActive = System.currentTimeMillis() updateAccount(detail) _activeAccount.value = detail @@ -101,7 +106,7 @@ class AccountRepository @Inject constructor( .map { getAccountDetails(it) }.maxByOrNull { it.lastActive } } - fun addAccount( + actual fun addAccount( account: Account, type: PlatformType, accountKey: MicroBlogKey, @@ -130,7 +135,7 @@ class AccountRepository @Inject constructor( } } - fun getAccountDetails( + actual fun getAccountDetails( account: Account, ): AccountDetails { return AccountDetails( @@ -152,7 +157,7 @@ class AccountRepository @Inject constructor( ) } - fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences { + actual fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences { return preferencesCache.getOrPut(accountKey) { accountPreferencesFactory.create(accountKey) } @@ -161,11 +166,11 @@ class AccountRepository @Inject constructor( private fun getAccountKey(account: Account): MicroBlogKey = MicroBlogKey.valueOf(manager.getUserData(account, ACCOUNT_USER_DATA_KEY)) - fun containsAccount(key: MicroBlogKey): Boolean { + actual fun containsAccount(key: MicroBlogKey): Boolean { return findByAccountKey(key) != null } - fun updateAccount(detail: AccountDetails) { + actual fun updateAccount(detail: AccountDetails) { val account = detail.account.toAndroid() manager.setUserData(account, ACCOUNT_USER_DATA_TYPE, detail.type.name) manager.setUserData(account, ACCOUNT_USER_DATA_KEY, detail.accountKey.toString()) @@ -188,7 +193,7 @@ class AccountRepository @Inject constructor( } } - fun delete(detail: AccountDetails) { + actual fun delete(detail: AccountDetails) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { manager.removeAccountExplicitly(detail.account.toAndroid()) _accounts.value = getAccounts().map { @@ -199,7 +204,7 @@ class AccountRepository @Inject constructor( } } - fun getFirstByType(type: PlatformType): AccountDetails? { + actual fun getFirstByType(type: PlatformType): AccountDetails? { return _accounts.value.sortedByDescending { it.lastActive }.firstOrNull { it.type == type } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt deleted file mode 100644 index 3cd7a6705..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository - -import com.twidere.twiderex.model.ui.UiUser - -actual class AccountUpdateRepository { - actual fun updateAccount(user: UiUser) { - TODO("NOT IMPLEMENT YET") - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt similarity index 74% rename from android/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt index c1e7b85a9..4a28e41f6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/model/AccountPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt @@ -20,18 +20,12 @@ */ package com.twidere.twiderex.model -import android.content.Context import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStoreFile -import com.twidere.twiderex.scenes.home.HomeMenus import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.map @@ -84,27 +78,8 @@ class AccountPreferences( it[homeMenuOrderKey] = data.joinToString(",") { it.first.name } } } +} - class Factory( - private val context: Context, - ) { - fun create(accountKey: MicroBlogKey) = createAccountPreferences(context, accountKey) - - private fun createAccountPreferences( - context: Context, - accountKey: MicroBlogKey, - ): AccountPreferences { - val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - return AccountPreferences( - dataStore = PreferenceDataStoreFactory.create( - corruptionHandler = null, - migrations = listOf(), - scope = scope - ) { - context.applicationContext.preferencesDataStoreFile(accountKey.toString()) - }, - scope = scope - ) - } - } +expect class AccountPreferencesFactory { + fun create(accountKey: MicroBlogKey): AccountPreferences } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt index 9d0a9cf5b..695b02ae9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt @@ -18,12 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.scenes.home +package com.twidere.twiderex.model import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.scenes.home.mastodon.FederatedTimelineItem -import com.twidere.twiderex.scenes.home.mastodon.LocalTimelineItem -import com.twidere.twiderex.scenes.home.mastodon.MastodonNotificationItem enum class HomeMenus( val item: HomeNavigationItem, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt new file mode 100644 index 000000000..72dc57907 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -0,0 +1,59 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import android.accounts.Account +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.AccountPreferences +import com.twidere.twiderex.model.AmUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.cred.CredentialsType +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiUser +import kotlinx.coroutines.flow.SharedFlow + +expect class AccountRepository { + val activeAccount: SharedFlow + val accounts: SharedFlow> + fun updateAccount(user: UiUser) + fun getAccounts(): List + fun hasAccount(): Boolean + fun findByAccountKey(accountKey: MicroBlogKey): Account? + fun setCurrentAccount(detail: AccountDetails) + fun addAccount( + account: Account, + type: PlatformType, + accountKey: MicroBlogKey, + credentials_type: CredentialsType, + credentials_json: String, + extras_json: String, + user: AmUser, + lastActive: Long, + ) + fun getAccountDetails( + account: Account, + ): AccountDetails + fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences + fun containsAccount(key: MicroBlogKey): Boolean + fun updateAccount(detail: AccountDetails) + fun delete(detail: AccountDetails) + fun getFirstByType(type: PlatformType): AccountDetails? +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt deleted file mode 100644 index 8029947d0..000000000 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.repository - -import com.twidere.twiderex.model.ui.UiUser - -expect class AccountUpdateRepository { - fun updateAccount(user: UiUser) - // accountRepository.findByAccountKey(user.userKey)?.let { - // accountRepository.getAccountDetails(it) - // }?.let { details -> - // user.let { - // details.user = it.toAmUser() - // accountRepository.updateAccount(details) - // } - // } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt index 03d159cc3..75b1e19bd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.Flow class UserRepository( private val database: CacheDatabase, - private val accountRepository: AccountUpdateRepository, + private val accountRepository: AccountRepository, ) { suspend fun lookupUserByName(name: String, accountKey: MicroBlogKey, lookupService: LookupService): UiUser { return lookupService.lookupUserByName(name).toUi(accountKey).also { diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index 951b88d95..3dcd361ac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -25,10 +25,9 @@ import androidx.work.WorkManager import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.worker.draft.RemoveDraftWorker -import dagger.assisted.AssistedInject import moe.tlaster.precompose.viewmodel.ViewModel -class DraftViewModel @AssistedInject constructor( +class DraftViewModel( private val repository: DraftRepository, private val workManager: WorkManager, private val notificationManagerCompat: NotificationManagerCompat, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 68f0e5dc8..9be67ad9f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -33,7 +33,6 @@ import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.utils.FileProviderHelper import com.twidere.twiderex.worker.DownloadMediaWorker import com.twidere.twiderex.worker.ShareMediaWorker -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -42,7 +41,7 @@ import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class MediaViewModel @AssistedInject constructor( +class MediaViewModel( private val repository: StatusRepository, private val accountRepository: AccountRepository, private val inAppNotification: InAppNotification, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index da4d17cb1..baacd2a6b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -25,13 +25,12 @@ import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class StatusViewModel @AssistedInject constructor( +class StatusViewModel( private val statusRepository: StatusRepository, private val accountRepository: AccountRepository, private val statusKey: MicroBlogKey, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index 8c8e112eb..d9c0f66f2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn -import com.twidere.twiderex.ext.asStateIn import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt similarity index 99% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index a0907cdfd..5ce4f6f16 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -23,9 +23,8 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf import androidx.paging.PagingData import androidx.paging.cachedIn -import com.twidere.twiderex.ext.asStateIn import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index b061c6053..a92a1e0b0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -22,8 +22,8 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import androidx.paging.filter -import com.twidere.twiderex.ext.asStateIn import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index cb251d66e..8b07be55d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.search import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flatMapLatest diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index e7ac98447..b59229cdd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.scenes.home.HomeMenus import kotlinx.coroutines.flow.lastOrNull diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 203228ebf..2a631684f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -25,9 +25,7 @@ import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.NotificationCursorType -import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index ad0ac4c35..05925c112 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -26,7 +26,6 @@ import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType -import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 85709f802..54bc60c40 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn -import com.twidere.twiderex.ext.asStateIn import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.ext.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt similarity index 79% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt index 3cd7a6705..f4c3153cf 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountUpdateRepository.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt @@ -18,12 +18,10 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.repository +package com.twidere.twiderex.model -import com.twidere.twiderex.model.ui.UiUser - -actual class AccountUpdateRepository { - actual fun updateAccount(user: UiUser) { - TODO("NOT IMPLEMENT YET") +actual class AccountPreferencesFactory { + actual fun create(accountKey: MicroBlogKey): AccountPreferences { + TODO("Not yet implemented") } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt new file mode 100644 index 000000000..c6af3f7d6 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -0,0 +1,90 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import android.accounts.Account +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.AccountPreferences +import com.twidere.twiderex.model.AmUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.cred.CredentialsType +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiUser +import kotlinx.coroutines.flow.SharedFlow + +actual class AccountRepository { + actual val activeAccount: SharedFlow + get() = TODO("Not yet implemented") + actual val accounts: SharedFlow> + get() = TODO("Not yet implemented") + + actual fun updateAccount(user: UiUser) { + } + + actual fun updateAccount(detail: AccountDetails) { + } + + actual fun getAccounts(): List { + TODO("Not yet implemented") + } + + actual fun hasAccount(): Boolean { + TODO("Not yet implemented") + } + + actual fun findByAccountKey(accountKey: MicroBlogKey): Account? { + TODO("Not yet implemented") + } + + actual fun setCurrentAccount(detail: AccountDetails) { + } + + actual fun addAccount( + account: Account, + type: PlatformType, + accountKey: MicroBlogKey, + credentials_type: CredentialsType, + credentials_json: String, + extras_json: String, + user: AmUser, + lastActive: Long + ) { + } + + actual fun getAccountDetails(account: Account): AccountDetails { + TODO("Not yet implemented") + } + + actual fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences { + TODO("Not yet implemented") + } + + actual fun containsAccount(key: MicroBlogKey): Boolean { + TODO("Not yet implemented") + } + + actual fun delete(detail: AccountDetails) { + } + + actual fun getFirstByType(type: PlatformType): AccountDetails? { + TODO("Not yet implemented") + } +} From ad1a990718b911420ccbf46bd01366248f305a38 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 16:56:33 +0800 Subject: [PATCH 094/615] update account repository --- .../twiderex/jobs/common/DownloadMediaJob.kt | 2 - .../twiderex/jobs/common/NotificationJob.kt | 2 +- .../twiderex/jobs/compose/ComposeJob.kt | 2 - .../jobs/dm/DirectMessageDeleteJob.kt | 2 - .../twiderex/jobs/dm/DirectMessageSendJob.kt | 2 - .../twiderex/jobs/status/DeleteStatusJob.kt | 2 - .../twiderex/jobs/status/MastodonVoteJob.kt | 2 - .../twidere/twiderex/jobs/status/StatusJob.kt | 2 - .../NotificationChannelInitializer.kt | 2 +- .../twiderex/repository/AccountRepository.kt | 103 +++++++++--------- .../twiderex/repository/AccountRepository.kt | 10 +- .../mastodon/MastodonSignInViewModel.kt | 6 +- .../twitter/TwitterSignInViewModel.kt | 6 +- .../twiderex/repository/AccountRepository.kt | 11 +- 14 files changed, 60 insertions(+), 94 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt index ef7cf3729..75476d605 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt @@ -39,8 +39,6 @@ class DownloadMediaJob( ) { val accountDetails = accountKey.let { accountRepository.findByAccountKey(accountKey = it) - }?.let { - accountRepository.getAccountDetails(it) } ?: throw Error("Can't find any account matches:$$accountKey") val service = accountDetails.service if (service !is DownloadMediaService) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index e047e459d..c746f4ff0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -52,7 +52,7 @@ class NotificationJob( ) { suspend fun execute(enableNotification: Boolean) = coroutineScope { if (!enableNotification) { - accountRepository.getAccounts().map { accountRepository.getAccountDetails(it) } + accountRepository.getAccounts() .filter { it.preferences.isNotificationEnabled.first() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 07f50021a..5a352a953 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -52,8 +52,6 @@ abstract class ComposeJob( .setProgress(100, 0, false) val accountDetails = accountKey.let { accountRepository.findByAccountKey(accountKey = it) - }?.let { - accountRepository.getAccountDetails(it) } ?: throw Error("Can't find any account matches:$$accountKey") val notificationId = composeData.draftId.hashCode() @Suppress("UNCHECKED_CAST") diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt index 21ff36fb7..b477f3590 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt @@ -33,8 +33,6 @@ class DirectMessageDeleteJob( suspend fun execute(deleteData: DirectMessageDeleteData, accountKey: MicroBlogKey) { val accountDetails = accountKey.let { accountRepository.findByAccountKey(accountKey = it) - }?.let { - accountRepository.getAccountDetails(it) } ?: throw Error("Can't find any account matches:$$accountKey") repository.deleteMessage( accountKey = deleteData.accountKey, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index 5204acfd9..23c0a89de 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -50,8 +50,6 @@ abstract class DirectMessageSendJob( suspend fun execute(sendData: DirectMessageSendData, accountKey: MicroBlogKey) { val accountDetails = accountKey.let { accountRepository.findByAccountKey(accountKey = it) - }?.let { - accountRepository.getAccountDetails(it) } ?: throw Error("can't find any account matches:$accountKey") val notificationId = sendData.draftMessageKey.hashCode() @Suppress("UNCHECKED_CAST") diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt index 3ffa66a87..d4b05b7e8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt @@ -40,8 +40,6 @@ class DeleteStatusJob( statusRepository.loadFromCache(it, accountKey = accountKey) } ?: throw Error("Can't find any status matches:$statusKey") val service = accountRepository.findByAccountKey(accountKey)?.let { - accountRepository.getAccountDetails(it) - }?.let { it.service as? StatusService } ?: throw Error() try { diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index df88c1392..f4585d91e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -42,8 +42,6 @@ class MastodonVoteJob( throw Error() } val service = accountRepository.findByAccountKey(accountKey)?.let { - accountRepository.getAccountDetails(it) - }?.let { it.service as? MastodonService } ?: throw Error() diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt index 85ef159b6..53e56afc2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt @@ -41,8 +41,6 @@ abstract class StatusJob( statusRepository.loadFromCache(it, accountKey = accountKey) } ?: throw Error("can't find any status matches:$statusKey") val service = accountRepository.findByAccountKey(accountKey)?.let { - accountRepository.getAccountDetails(it) - }?.let { it.service as? StatusService } ?: throw Error("account service is not StatusService") return try { diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt index 210fd09b4..b1f5c80c0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt @@ -68,7 +68,7 @@ class NotificationChannelInitializer : Initializer() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index bbf2070a5..6474d35ad 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.repository import android.accounts.Account import android.accounts.AccountManager import android.os.Build +import com.twidere.twiderex.dataprovider.mapper.toAmUser import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.model.AccountPreferencesFactory @@ -56,8 +57,10 @@ actual class AccountRepository( private val accountPreferencesFactory: AccountPreferencesFactory, ) { actual fun updateAccount(user: UiUser) { - findByAccountKey(user.userKey)?.let { - getAccountDetails(it).copy() + findByAccountKey(user.userKey)?.copy( + user = user.toAmUser() + )?.let { + updateAccount(it) } } @@ -69,32 +72,16 @@ actual class AccountRepository( get() = _activeAccount.asSharedFlow() private val _accounts = MutableStateFlow( - getAccounts().map { - getAccountDetails(it) - } + getAccounts() ) actual val accounts get() = _accounts.asSharedFlow() - actual fun getAccounts(): List { - return manager.getAccountsByType(ACCOUNT_TYPE).toList() - } - actual fun hasAccount(): Boolean { return getAccounts().isNotEmpty() } - // Note that UserKey that being used in AccountRepository is idStr@domain, not screenName@domain - actual fun findByAccountKey(accountKey: MicroBlogKey): Account? { - for (account in getAccounts()) { - if (accountKey == getAccountKey(account)) { - return account - } - } - return null - } - actual fun setCurrentAccount(detail: AccountDetails) { detail.lastActive = System.currentTimeMillis() updateAccount(detail) @@ -102,12 +89,11 @@ actual class AccountRepository( } private fun getCurrentAccount(): AccountDetails? { - return getAccounts() - .map { getAccountDetails(it) }.maxByOrNull { it.lastActive } + return getAccounts().maxByOrNull { it.lastActive } } actual fun addAccount( - account: Account, + displayKey: MicroBlogKey, type: PlatformType, accountKey: MicroBlogKey, credentials_type: CredentialsType, @@ -116,6 +102,7 @@ actual class AccountRepository( user: AmUser, lastActive: Long, ) { + val account = Account(displayKey.toString(), ACCOUNT_TYPE) manager.addAccountExplicitly(account, null, null) val detail = AccountDetails( account = account.toTwidere(), @@ -130,31 +117,7 @@ actual class AccountRepository( ) updateAccount(detail) setCurrentAccount(detail) - _accounts.value = getAccounts().map { - getAccountDetails(it) - } - } - - actual fun getAccountDetails( - account: Account, - ): AccountDetails { - return AccountDetails( - account = account.toTwidere(), - type = PlatformType.valueOf(manager.getUserData(account, ACCOUNT_USER_DATA_TYPE)), - accountKey = getAccountKey(account), - credentials_type = CredentialsType.valueOf( - manager.getUserData( - account, - ACCOUNT_USER_DATA_CREDS_TYPE - ) - ), - credentials_json = manager.peekAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE), - extras_json = manager.getUserData(account, ACCOUNT_USER_DATA_EXTRAS), - user = manager.getUserData(account, ACCOUNT_USER_DATA_USER).fromJson(), - lastActive = manager.getUserData(account, ACCOUNT_USER_DATA_LAST_ACTIVE)?.toLongOrNull() - ?: 0, - preferences = getAccountPreferences(getAccountKey(account)) - ) + _accounts.value = getAccounts() } actual fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences { @@ -188,17 +151,13 @@ actual class AccountRepository( detail.lastActive.toString() ) _activeAccount.value = getCurrentAccount() - _accounts.value = getAccounts().map { - getAccountDetails(it) - } + _accounts.value = getAccounts() } actual fun delete(detail: AccountDetails) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { manager.removeAccountExplicitly(detail.account.toAndroid()) - _accounts.value = getAccounts().map { - getAccountDetails(it) - } + _accounts.value = getAccounts() _activeAccount.value = getCurrentAccount() preferencesCache.remove(detail.accountKey)?.close() } @@ -207,4 +166,42 @@ actual class AccountRepository( actual fun getFirstByType(type: PlatformType): AccountDetails? { return _accounts.value.sortedByDescending { it.lastActive }.firstOrNull { it.type == type } } + + actual fun getAccounts(): List { + return manager.getAccountsByType(ACCOUNT_TYPE).map { + getAccountDetails(it) + } + } + + // Note that UserKey that being used in AccountRepository is idStr@domain, not screenName@domain + actual fun findByAccountKey(accountKey: MicroBlogKey): AccountDetails? { + for (account in getAccounts()) { + if (accountKey == getAccountKey(account.account.toAndroid())) { + return account + } + } + return null + } + + private fun getAccountDetails( + account: Account, + ): AccountDetails { + return AccountDetails( + account = account.toTwidere(), + type = PlatformType.valueOf(manager.getUserData(account, ACCOUNT_USER_DATA_TYPE)), + accountKey = getAccountKey(account), + credentials_type = CredentialsType.valueOf( + manager.getUserData( + account, + ACCOUNT_USER_DATA_CREDS_TYPE + ) + ), + credentials_json = manager.peekAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE), + extras_json = manager.getUserData(account, ACCOUNT_USER_DATA_EXTRAS), + user = manager.getUserData(account, ACCOUNT_USER_DATA_USER).fromJson(), + lastActive = manager.getUserData(account, ACCOUNT_USER_DATA_LAST_ACTIVE)?.toLongOrNull() + ?: 0, + preferences = getAccountPreferences(getAccountKey(account)) + ) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 72dc57907..d7e158932 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.repository -import android.accounts.Account import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.model.AmUser @@ -34,12 +33,12 @@ expect class AccountRepository { val activeAccount: SharedFlow val accounts: SharedFlow> fun updateAccount(user: UiUser) - fun getAccounts(): List + fun getAccounts(): List fun hasAccount(): Boolean - fun findByAccountKey(accountKey: MicroBlogKey): Account? + fun findByAccountKey(accountKey: MicroBlogKey): AccountDetails? fun setCurrentAccount(detail: AccountDetails) fun addAccount( - account: Account, + displayKey: MicroBlogKey, type: PlatformType, accountKey: MicroBlogKey, credentials_type: CredentialsType, @@ -48,9 +47,6 @@ expect class AccountRepository { user: AmUser, lastActive: Long, ) - fun getAccountDetails( - account: Account, - ): AccountDetails fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences fun containsAccount(key: MicroBlogKey): Boolean fun updateAccount(detail: AccountDetails) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index cc75ccfb7..362b6151f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import android.accounts.Account import androidx.compose.ui.text.input.TextFieldValue import com.twidere.services.mastodon.MastodonOAuthService import com.twidere.twiderex.dataprovider.mapper.toAmUser @@ -32,7 +31,6 @@ import com.twidere.twiderex.model.cred.OAuth2Credentials import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.repository.ACCOUNT_TYPE import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json import kotlinx.coroutines.flow.MutableStateFlow @@ -87,14 +85,12 @@ class MastodonSignInViewModel( ).json() if (repository.containsAccount(internalKey)) { repository.findByAccountKey(internalKey)?.let { - repository.getAccountDetails(it) - }?.let { it.credentials_json = credentials_json repository.updateAccount(it) } } else { repository.addAccount( - account = Account(displayKey.toString(), ACCOUNT_TYPE), + displayKey = displayKey, type = PlatformType.Mastodon, accountKey = internalKey, credentials_type = CredentialsType.OAuth2, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index fa21f8c38..3560f09ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.twitter -import android.accounts.Account import com.twidere.services.http.MicroBlogException import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService @@ -34,7 +33,6 @@ import com.twidere.twiderex.model.cred.OAuthCredentials import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.repository.ACCOUNT_TYPE import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json import com.twidere.twiderex.utils.notify @@ -112,14 +110,12 @@ class TwitterSignInViewModel( ).json() if (repository.containsAccount(internalKey)) { repository.findByAccountKey(internalKey)?.let { - repository.getAccountDetails(it) - }?.let { it.credentials_json = credentials_json repository.updateAccount(it) } } else { repository.addAccount( - account = Account(displayKey.toString(), ACCOUNT_TYPE), + displayKey = displayKey, type = PlatformType.Twitter, accountKey = internalKey, credentials_type = CredentialsType.OAuth, diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index c6af3f7d6..574de46ad 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.repository -import android.accounts.Account import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.model.AmUser @@ -42,7 +41,7 @@ actual class AccountRepository { actual fun updateAccount(detail: AccountDetails) { } - actual fun getAccounts(): List { + actual fun getAccounts(): List { TODO("Not yet implemented") } @@ -50,7 +49,7 @@ actual class AccountRepository { TODO("Not yet implemented") } - actual fun findByAccountKey(accountKey: MicroBlogKey): Account? { + actual fun findByAccountKey(accountKey: MicroBlogKey): AccountDetails? { TODO("Not yet implemented") } @@ -58,7 +57,7 @@ actual class AccountRepository { } actual fun addAccount( - account: Account, + displayKey: MicroBlogKey, type: PlatformType, accountKey: MicroBlogKey, credentials_type: CredentialsType, @@ -69,10 +68,6 @@ actual class AccountRepository { ) { } - actual fun getAccountDetails(account: Account): AccountDetails { - TODO("Not yet implemented") - } - actual fun getAccountPreferences(accountKey: MicroBlogKey): AccountPreferences { TODO("Not yet implemented") } From 8dd86b2707473805a1193fbeb99dd6f0e01e39ec Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 16:57:38 +0800 Subject: [PATCH 095/615] remove parameter from Pager.toUi --- .../paging/mediator/paging/PagingTimelineMediatorBase.kt | 6 ++---- .../com/twidere/twiderex/repository/StatusRepository.kt | 2 +- .../com/twidere/twiderex/repository/TimelineRepository.kt | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index 83a84da75..e5ca1a52f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -25,7 +25,6 @@ import androidx.paging.LoadType import androidx.paging.Pager import androidx.paging.PagingData import androidx.paging.PagingState -import androidx.paging.filter import androidx.paging.map import com.twidere.services.microblog.model.IStatus import com.twidere.twiderex.dataprovider.mapper.toPagingTimeline @@ -124,9 +123,8 @@ abstract class PagingTimelineMediatorBase( ): List } -fun Pager.toUi(accountKey: MicroBlogKey): Flow> { +fun Pager.toUi(): Flow> { return flow.map { pagingData -> - pagingData.filter { it.timeline.accountKey == accountKey } - .map { it.status } + pagingData.map { it.status } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 2f5fc295a..9c0aa6383 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -104,6 +104,6 @@ class StatusRepository( database = database, ) } - return remoteMediator.pager().toUi(accountKey = accountKey) + return remoteMediator.pager().toUi() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt index 97fd18483..d891f1f39 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt @@ -53,7 +53,7 @@ class TimelineRepository( accountKey = accountKey, service = service, ) - return mediator.pager().toUi(accountKey = accountKey) + return mediator.pager().toUi() } fun userTimeline( @@ -68,7 +68,7 @@ class TimelineRepository( accountKey = accountKey, service = service, exclude_replies = exclude_replies, - ).pager().toUi(accountKey = accountKey) + ).pager().toUi() } fun listTimeline( @@ -81,7 +81,7 @@ class TimelineRepository( database = database, listKey = listKey, service = service - ).pager().toUi(accountKey = accountKey) + ).pager().toUi() } fun mastodonHashtagTimeline( @@ -95,6 +95,6 @@ class TimelineRepository( accountKey = accountKey, database = database ) - return mediator.pager().toUi(accountKey = accountKey) + return mediator.pager().toUi() } } From 628410f8b40d75d0558f203571fa0c18a6a3d93f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 16:58:26 +0800 Subject: [PATCH 096/615] migrate compose vm --- .../twiderex/ext}/TextFieldValueExtensions.kt | 2 +- .../twidere/twiderex/utils/MastodonEmojiCache.kt | 0 .../viewmodel/compose/ComposeViewModel.kt | 16 ++++++++-------- 3 files changed, 9 insertions(+), 9 deletions(-) rename {android/src/main/kotlin/com/twidere/twiderex/extensions => common/src/commonMain/kotlin/com/twidere/twiderex/ext}/TextFieldValueExtensions.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt (100%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt index 4acad6808..b112f5a9e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.extensions +package com.twidere.twiderex.ext import androidx.compose.ui.text.input.TextFieldValue diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 44680df7d..388a4f19a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -34,9 +34,9 @@ import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.ext.getTextAfterSelection +import com.twidere.twiderex.ext.getTextBeforeSelection import com.twidere.twiderex.extensions.getCachedLocation -import com.twidere.twiderex.extensions.getTextAfterSelection -import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility @@ -263,18 +263,18 @@ open class ComposeViewModel( composeType == ComposeType.Reply ) { val mentions = - status.mastodonExtra?.mentions?.mapNotNull { it.acct } - ?.filter { it != account.user.screenName } - ?.map { "@$it" } - ?.let { + status.mastodonExtra.mentions.mapNotNull { it.acct } + .filter { it != account.user.screenName } + .map { "@$it" } + .let { if (status.user.userKey != account.user.userKey) { listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it } else { it } } - ?.distinctBy { it } - ?.takeIf { it.any() } + .distinctBy { it } + .takeIf { it.any() } ?.joinToString(" ", postfix = " ") { it } if (mentions != null) { setText( From f11b7b87ae47537c7c5c380962d7fefa283485ba Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 16:59:05 +0800 Subject: [PATCH 097/615] clean up --- .../twiderex/viewmodel/timeline/TimelineViewModel.kt | 1 - .../twitter/search/TwitterSearchMediaViewModel.kt | 12 +++--------- .../twidere/twiderex/viewmodel/user/UserViewModel.kt | 6 ++---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 4d092b676..82939c5fc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -27,7 +27,6 @@ import androidx.datastore.preferences.core.intPreferencesKey import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.extensions.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index a08161756..fb5f2cd54 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -28,21 +28,15 @@ import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class TwitterSearchMediaViewModel @AssistedInject constructor( +class TwitterSearchMediaViewModel( val database: CacheDatabase, - @Assisted private val account: AccountDetails, - @Assisted keyword: String, + private val account: AccountDetails, + keyword: String, ) : ViewModel() { - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(account: AccountDetails, keyword: String): TwitterSearchMediaViewModel - } private val service by lazy { account.service as TwitterService diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index aebb73ff7..24e526361 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -29,8 +29,6 @@ import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notify -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map @@ -38,11 +36,11 @@ import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -class UserViewModel @AssistedInject constructor( +class UserViewModel( private val repository: UserRepository, private val accountRepository: AccountRepository, private val inAppNotification: InAppNotification, - @Assisted private val userKey: MicroBlogKey, + private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { From f2896303c63fd176f1966febee9e2f6b69c3f57b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 17:00:10 +0800 Subject: [PATCH 098/615] clean up --- .../com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index b59229cdd..92d722792 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.scenes.home.HomeMenus import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch From 43a9a4ab575be77930c7a782027b20b8836b5910 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 17:00:25 +0800 Subject: [PATCH 099/615] move Event to common --- .../kotlin/com/twidere/twiderex/utils/Event.kt | 16 ---------------- 1 file changed, 16 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/utils/Event.kt (74%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/Event.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt similarity index 74% rename from android/src/main/kotlin/com/twidere/twiderex/utils/Event.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt index 6cd3898ce..ade39e12a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/Event.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.utils -import androidx.lifecycle.Observer - /** * Used as a wrapper for data that is exposed via a Flow that represents an event. */ @@ -48,17 +46,3 @@ open class Event(private val content: T) { */ fun peekContent(): T = content } - -/** - * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has - * already been handled. - * - * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled. - */ -class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> { - override fun onChanged(event: Event?) { - event?.getContentIfNotHandled()?.let { - onEventUnhandledContent(it) - } - } -} From 071fb942d3fa59ae29a49ac03ea872cf834c28dc Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 17:16:34 +0800 Subject: [PATCH 100/615] move flow extension to common --- .../twiderex/extensions/FlowExtensions.kt | 20 +++- .../precompose/lifecycle/RepeatOnLifecycle.kt | 98 ++++++++++++++++++- .../precompose/ui/ComposeCompositionLocal.kt | 2 +- 3 files changed, 112 insertions(+), 8 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt (69%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt similarity index 69% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index b3be1c2ce..73aba9449 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -24,9 +24,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.lifecycle.flowWithLifecycle +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.collect +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.repeatOnLifecycle +import moe.tlaster.precompose.ui.LocalLifecycleOwner @Composable fun Flow.observeAsState(initial: T): State { @@ -35,3 +39,15 @@ fun Flow.observeAsState(initial: T): State { flowWithLifecycle(lifecycleOwner.lifecycle) }.collectAsState(initial = initial) } + +@OptIn(ExperimentalCoroutinesApi::class) +fun Flow.flowWithLifecycle( + lifecycle: Lifecycle, +): Flow = callbackFlow { + lifecycle.repeatOnLifecycle { + this@flowWithLifecycle.collect { + send(it) + } + } + close() +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt index cd07dcf84..85b8b1a52 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt @@ -26,28 +26,90 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import kotlin.coroutines.resume +/** + * Runs the given [block] in a new coroutine when `this` [Lifecycle] is at least at [state] and + * suspends the execution until `this` [Lifecycle] is [Lifecycle.State.Destroyed]. + * + * The [block] will cancel and re-launch as the lifecycle moves in and out of the target state. + * + * ``` + * class MyActivity : AppCompatActivity() { + * override fun onCreate(savedInstanceState: Bundle?) { + * /* ... */ + * // Runs the block of code in a coroutine when the lifecycle is at least STARTED. + * // The coroutine will be cancelled when the ON_STOP event happens and will + * // restart executing if the lifecycle receives the ON_START event again. + * lifecycleScope.launch { + * lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + * uiStateFlow.collect { uiState -> + * updateUi(uiState) + * } + * } + * } + * } + * } + * ``` + * + * The best practice is to call this function when the lifecycle is initialized. For + * example, `onCreate` in an Activity, or `onViewCreated` in a Fragment. Otherwise, multiple + * repeating coroutines doing the same could be created and be executed at the same time. + * + * Repeated invocations of `block` will run serially, that is they will always wait for the + * previous invocation to fully finish before re-starting execution as the state moves in and out + * of the required state. + * + * Warning: [Lifecycle.State.Initialized] is not allowed in this API. Passing it as a + * parameter will throw an [IllegalArgumentException]. + * + * @param state [Lifecycle.State] in which `block` runs in a new coroutine. That coroutine + * will cancel if the lifecycle falls below that state, and will restart if it's in that state + * again. + */ suspend fun Lifecycle.repeatOnLifecycle( block: suspend CoroutineScope.() -> Unit ) { if (currentState === Lifecycle.State.Destroyed) { return } + + // This scope is required to preserve context before we move to Dispatchers.Main coroutineScope { withContext(Dispatchers.Main.immediate) { + // Check the current state of the lifecycle as the previous check is not guaranteed + // to be done on the main thread. if (currentState === Lifecycle.State.Destroyed) return@withContext + + // Instance of the running repeating coroutine var launchedJob: Job? = null + + // Registered observer var observer: LifecycleObserver? = null try { + // Suspend the coroutine until the lifecycle is destroyed or + // the coroutine is cancelled suspendCancellableCoroutine { cont -> - object : LifecycleObserver { + // Lifecycle observers that executes `block` when the lifecycle reaches certain state, and + // cancels when it falls below that state. + val mutex = Mutex() + observer = object : LifecycleObserver { override fun onStateChanged(state: Lifecycle.State) { when (state) { Lifecycle.State.Initialized -> Unit Lifecycle.State.Active -> { - launchedJob = this@coroutineScope.launch(block = block) + launchedJob = this@coroutineScope.launch { + // Mutex makes invocations run serially, + // coroutineScope ensures all child coroutines finish + mutex.withLock { + coroutineScope { + block() + } + } + } } Lifecycle.State.InActive -> { launchedJob?.cancel() @@ -58,10 +120,8 @@ suspend fun Lifecycle.repeatOnLifecycle( } } } - }.let { - observer = it - this@repeatOnLifecycle.addObserver(it) } + this@repeatOnLifecycle.addObserver(observer as LifecycleObserver) } } finally { launchedJob?.cancel() @@ -72,3 +132,31 @@ suspend fun Lifecycle.repeatOnLifecycle( } } } + +/** + * [LifecycleOwner]'s extension function for [Lifecycle.repeatOnLifecycle] to allow an easier + * call to the API from LifecycleOwners such as Activities and Fragments. + * + * ``` + * class MyActivity : AppCompatActivity() { + * override fun onCreate(savedInstanceState: Bundle?) { + * /* ... */ + * // Runs the block of code in a coroutine when the lifecycle is at least STARTED. + * // The coroutine will be cancelled when the ON_STOP event happens and will + * // restart executing if the lifecycle receives the ON_START event again. + * lifecycleScope.launch { + * repeatOnLifecycle(Lifecycle.State.STARTED) { + * uiStateFlow.collect { uiState -> + * updateUi(uiState) + * } + * } + * } + * } + * } + * ``` + * + * @see Lifecycle.repeatOnLifecycle + */ +suspend fun LifecycleOwner.repeatOnLifecycle( + block: suspend CoroutineScope.() -> Unit +): Unit = lifecycle.repeatOnLifecycle(block) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt index 9216697c3..a1288262c 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.compositionLocalOf import moe.tlaster.precompose.lifecycle.LifecycleOwner import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner -val LocalLifecycleOwner = compositionLocalOf { null } +val LocalLifecycleOwner = compositionLocalOf { noLocalProvidedFor("LocalLifecycleOwner") } val LocalViewModelStoreOwner = compositionLocalOf { noLocalProvidedFor("ViewModelStoreOwner") } From a32ff01c0e9dea365d454f7c3850161725dc32c1 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 17:17:37 +0800 Subject: [PATCH 101/615] update package name --- .../twidere/twiderex/extensions/FlowExtensions.kt | 12 ++++++++++++ .../{ext => extensions}/TextFieldValueExtensions.kt | 2 +- .../com/twidere/twiderex/viewmodel/MediaViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/StatusViewModel.kt | 2 +- .../viewmodel/compose/ComposeSearchUserViewModel.kt | 8 +------- .../twiderex/viewmodel/compose/ComposeViewModel.kt | 7 +++---- .../compose/MastodonComposeSearchHashtagViewModel.kt | 8 +------- .../twiderex/viewmodel/dm/DMConversationViewModel.kt | 2 +- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 5 +---- .../viewmodel/dm/DMNewConversationViewModel.kt | 2 +- .../viewmodel/lists/ListsSearchUserViewModel.kt | 2 +- .../viewmodel/lists/ListsTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/lists/ListsUserViewModel.kt | 2 +- .../twiderex/viewmodel/lists/ListsViewModel.kt | 2 +- .../viewmodel/mastodon/MastodonHashtagViewModel.kt | 3 +-- .../mastodon/MastodonSearchHashtagViewModel.kt | 3 +-- .../viewmodel/search/SearchInputViewModel.kt | 2 +- .../twiderex/viewmodel/search/SearchSaveViewModel.kt | 2 +- .../viewmodel/search/SearchTweetsViewModel.kt | 2 +- .../twiderex/viewmodel/search/SearchUserViewModel.kt | 3 +-- .../settings/AccountNotificationViewModel.kt | 4 +--- .../twiderex/viewmodel/settings/LayoutViewModel.kt | 3 +-- .../viewmodel/timeline/HomeTimelineViewModel.kt | 2 +- .../viewmodel/timeline/MentionsTimelineViewModel.kt | 2 +- .../timeline/NotificationTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/timeline/TimelineViewModel.kt | 2 +- .../timeline/mastodon/FederatedTimelineViewModel.kt | 2 +- .../timeline/mastodon/LocalTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/trend/TrendViewModel.kt | 2 +- .../viewmodel/twitter/user/TwitterUserViewModel.kt | 2 +- .../viewmodel/user/UserFavouriteTimelineViewModel.kt | 2 +- .../viewmodel/user/UserMediaTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/user/UserTimelineViewModel.kt | 4 +--- .../twidere/twiderex/viewmodel/user/UserViewModel.kt | 2 +- 34 files changed, 47 insertions(+), 59 deletions(-) rename common/src/commonMain/kotlin/com/twidere/twiderex/{ext => extensions}/TextFieldValueExtensions.kt (96%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index 73aba9449..b3baf87fc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -24,10 +24,14 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.stateIn import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.repeatOnLifecycle import moe.tlaster.precompose.ui.LocalLifecycleOwner @@ -51,3 +55,11 @@ fun Flow.flowWithLifecycle( } close() } + + +fun Flow.asStateIn( + scope: CoroutineScope, + initialValue: T +): StateFlow { + return stateIn(scope, SharingStarted.Lazily, initialValue) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt similarity index 96% rename from common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt index b112f5a9e..4acad6808 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ext/TextFieldValueExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.ext +package com.twidere.twiderex.extensions import androidx.compose.ui.text.input.TextFieldValue diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 9be67ad9f..a74e2977b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -24,7 +24,7 @@ import android.content.Context import android.net.Uri import androidx.work.WorkManager import com.twidere.twiderex.R -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.notification.InAppNotification diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index baacd2a6b..7185b6f41 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.viewmodel import androidx.paging.cachedIn -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 1945f45be..f24c884d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -20,13 +20,7 @@ */ package com.twidere.twiderex.viewmodel.compose -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.cachedIn -import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.paging.source.SearchUserPagingSource +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 388a4f19a..00ae4ed4a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -33,9 +33,9 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction -import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.ext.getTextAfterSelection -import com.twidere.twiderex.ext.getTextBeforeSelection +import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.extensions.getTextAfterSelection +import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.extensions.getCachedLocation import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType @@ -60,7 +60,6 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 56948527c..1bdc2761b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -20,13 +20,7 @@ */ package com.twidere.twiderex.viewmodel.compose -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.cachedIn -import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn -import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index ca077e53c..3b9ca6d79 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.dm import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.flow.emptyFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 3543fe60d..41f0e5895 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -21,11 +21,10 @@ package com.twidere.twiderex.viewmodel.dm import android.net.Uri -import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData @@ -35,9 +34,7 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 9c5a377f3..a52bcd687 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -25,7 +25,7 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 4d112823a..e850d3e5f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -25,7 +25,7 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.MutableStateFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index d9c0f66f2..1817db1dc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5ce4f6f16..d8324d1ec 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.mutableStateMapOf import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index a92a1e0b0..8e99f8fa5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 3c6cc4fd6..14256a0fd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,9 +20,8 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.flow.emptyFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index f3c74f0a0..8caa2f07d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -22,10 +22,9 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.emptyFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 8b07be55d..a80fa2bae 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 771bcb59d..6fce97ac3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index d564596d9..375b1bff7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -24,7 +24,7 @@ import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index de1b29822..dc80ac4d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -22,10 +22,9 @@ package com.twidere.twiderex.viewmodel.search import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.emptyFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 6cd3e5ef4..6af5419c5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,11 +20,9 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 92d722792..ff8f15aa9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,10 +20,9 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 9b8cfdf3f..cd6f83d28 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.map diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 2a631684f..96010c7e8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.AccountRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 05925c112..1ebe55400 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.AccountRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 82939c5fc..0c95f55e9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -26,7 +26,7 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 23f708aa5..13fec87e3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 4f67bbcb6..867d61edc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -24,7 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index 47e7331de..cd3479259 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.viewmodel.trend import androidx.paging.cachedIn import com.twidere.services.microblog.TrendService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository import kotlinx.coroutines.flow.emptyFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 3a2a810ea..b4f1d62f8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.viewmodel.twitter.user import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 54bc60c40..ad2389508 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 69ec26412..dfcf50d66 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -27,7 +27,7 @@ import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 9772b8b5c..520f0d3fb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -20,16 +20,14 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flattenMerge import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 24e526361..a7bc2178f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship -import com.twidere.twiderex.ext.asStateIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository From 9f5c233fea9c79af66eb9808c6be144851e93a1d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 27 Aug 2021 17:25:03 +0800 Subject: [PATCH 102/615] migrate InAppNotification --- .../foundation/InAppNotificationScaffold.kt | 9 +++-- .../notification/InAppNotification.kt | 35 ++----------------- .../InAppNotification.kt} | 34 ++++++++++++------ 3 files changed, 31 insertions(+), 47 deletions(-) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/notification/InAppNotification.kt (68%) rename common/src/commonMain/kotlin/com/twidere/twiderex/{ext/FlowExt.kt => notification/InAppNotification.kt} (51%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt index d67fe4ef4..da1e415a1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt @@ -48,7 +48,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Dp -import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.notification.EventActionContext import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.notification.NotificationWithActionEvent @@ -71,9 +70,13 @@ fun ApplyNotification( } } val context = LocalContext.current - val navigator = LocalNavigator.current + // val navigator = LocalNavigator.current val actionContext = remember { - EventActionContext(context = context, navigator = navigator) + EventActionContext( + context = context, + // TODO: add navigator + // navigator = navigator, + ) } LaunchedEffect(event) { message?.let { diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt similarity index 68% rename from android/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index e0ccc07cd..8ac4578ab 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -24,16 +24,6 @@ import android.content.Context import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.component.navigation.INavigator -import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.utils.Event -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow - -interface NotificationEvent { - @Composable - fun getMessage(): String -} interface NotificationWithActionEvent : NotificationEvent { @Composable @@ -61,7 +51,8 @@ open class StringResNotificationEvent( data class EventActionContext( val context: Context, - val navigator: INavigator, + // TODO: add navigator + // val navigator: INavigator, ) class StringResWithActionNotificationEvent( @@ -80,25 +71,3 @@ class StringResWithActionNotificationEvent( return messageId.map { stringResource(id = it) }.joinToString(separator) } } - -class InAppNotification { - private val _source = MutableStateFlow?>(null) - private val source - get() = _source.asSharedFlow() - - fun show(event: NotificationEvent) { - _source.value = ((Event(event))) - } - - fun show(message: String) { - _source.value = Event(StringNotificationEvent(message)) - } - - fun show(@StringRes messageId: Int) { - _source.value = Event(StringResNotificationEvent(messageId = messageId)) - } - - @Composable - fun observeAsState(initial: Event? = null) = - source.observeAsState(initial = initial) -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt similarity index 51% rename from common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index f12ccc848..03e3fd34b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ext/FlowExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -18,17 +18,29 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.ext +package com.twidere.twiderex.notification -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.stateIn +import androidx.compose.runtime.Composable +import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.utils.Event +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow -fun Flow.asStateIn( - scope: CoroutineScope, - initialValue: T -): StateFlow { - return stateIn(scope, SharingStarted.Lazily, initialValue) +interface NotificationEvent { + @Composable + fun getMessage(): String +} + +class InAppNotification { + private val _source = MutableStateFlow?>(null) + val source + get() = _source.asSharedFlow() + + fun show(event: NotificationEvent) { + _source.value = ((Event(event))) + } + + @Composable + fun observeAsState(initial: Event? = null) = + source.observeAsState(initial = initial) } From 4981d9e6b278a86ecc7f1c1352479edebd33ab33 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 30 Aug 2021 12:11:50 +0800 Subject: [PATCH 103/615] add go to tweet menu for mediascene --- .../kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 12 ++++++++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 13 insertions(+) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 90e05382a..cb01eaede 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -83,6 +83,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer +import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.status.LikeButton import com.twidere.twiderex.component.status.ReplyButton import com.twidere.twiderex.component.status.RetweetButton @@ -297,6 +298,7 @@ private fun StatusMediaInfo( currentMedia: UiMedia ) { val context = LocalContext.current + val navigator = LocalNavigator.current Column( modifier = Modifier .padding(StatusMediaInfoDefaults.ContentPadding), @@ -359,6 +361,16 @@ private fun StatusMediaInfo( text = stringResource(id = R.string.common_controls_actions_share_media), ) } + DropdownMenuItem( + onClick = { + callback.invoke() + navigator.status(status = status) + } + ) { + Text( + text = stringResource(id = R.string.common_controls_actions_go_to_status), + ) + } } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bff316365..7ffb4bc49 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -103,6 +103,7 @@ Cancel Preview Share media + Go to tweet Edit Open in Safari Yes From 25c0613b1d85894501ef2cd1ea696b5419f095db Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 30 Aug 2021 15:35:36 +0800 Subject: [PATCH 104/615] fix test under api 24 --- .../kotlin/moe/tlaster/precompose/navigation/RouteParser.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt index 12c57486f..9cbaa5038 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt @@ -610,7 +610,7 @@ internal class RouteParser { } val len = pattern.length var key = 0 - val paths = hashMapOf() + val paths = linkedMapOf() val pathAppender = { index: Int, segment: StringBuilder -> for (i in index until index - 1) { paths[i]?.append(segment) From e76d76966dd0c36519ed8511c96b839a83ac9748 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 30 Aug 2021 18:58:11 +0800 Subject: [PATCH 105/615] migrate job and action to common --- .../com/twidere/twiderex/di/TwidereModule.kt | 8 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 4 +- .../twiderex/utils/TwitterErrorHandling.kt | 119 ++++++++---------- common/build.gradle.kts | 3 +- common/src/androidMain/AndroidManifest.xml | 2 + .../twidere/twiderex/action/ComposeAction.kt | 4 +- .../twiderex/action/DirectMessageAction.kt | 6 +- .../twidere/twiderex/action/DraftAction.kt | 31 +++++ .../twidere/twiderex/action/MediaAction.kt | 23 ++-- .../twidere/twiderex/action/StatusActions.kt | 23 +--- .../db/transform/WorkDataTransform.kt | 0 .../twiderex/extensions/ContextExtensions.kt | 0 .../twiderex/extensions/DataExtensions.kt | 0 .../com/twidere/twiderex/kmp/ExifScrambler.kt | 9 +- .../com/twidere/twiderex/kmp/FileResolver.kt | 13 +- .../twidere/twiderex/kmp/LocationProvider.kt | 86 +++++++++++++ .../twidere/twiderex/kmp/RemoteNavigator.kt | 11 +- .../AndroidNotificationManager.kt | 20 +-- .../notification/InAppNotification.kt | 9 -- .../twiderex/worker/DownloadMediaWorker.kt | 0 .../twiderex/worker/NotificationWorker.kt | 0 .../twiderex/worker/ShareMediaWorker.kt | 0 .../twiderex/worker/compose/ComposeWorker.kt | 0 .../worker/compose/MastodonComposeWorker.kt | 0 .../worker/compose/TwitterComposeWorker.kt | 0 .../worker/database/DeleteDbStatusWorker.kt | 0 .../worker/dm/DirectMessageDeleteWorker.kt | 0 .../worker/dm/DirectMessageFetchWorker.kt | 0 .../worker/dm/DirectMessageInitializer.kt | 0 .../worker/dm/DirectMessageSendWorker.kt | 0 .../dm/TwitterDirectMessageSendWorker.kt | 0 .../worker/draft/RemoveDraftWorker.kt | 0 .../twiderex/worker/draft/SaveDraftWorker.kt | 0 .../worker/status/DeleteStatusWorker.kt | 0 .../twiderex/worker/status/LikeWorker.kt | 0 .../worker/status/MastodonVoteWorker.kt | 0 .../twiderex/worker/status/RetweetWorker.kt | 0 .../twiderex/worker/status/StatusWorker.kt | 0 .../twiderex/worker/status/UnLikeWorker.kt | 0 .../twiderex/worker/status/UnRetweetWorker.kt | 0 .../worker/status/UpdateStatusWorker.kt | 0 .../twidere/twiderex/action/ComposeAction.kt | 33 +++++ .../twiderex/action/DirectMessageAction.kt | 36 ++++++ .../twidere/twiderex/action/DraftAction.kt | 28 +++++ .../twidere/twiderex/action/MediaAction.kt | 28 +++++ .../twidere/twiderex/action/StatusActions.kt | 43 +++++++ .../twiderex/extensions/FlowExtensions.kt | 1 - .../twiderex/jobs/common/DownloadMediaJob.kt | 0 .../twiderex/jobs/common/NotificationJob.kt | 0 .../twiderex/jobs/common/ShareMediaJob.kt | 0 .../twiderex/jobs/compose/ComposeJob.kt | 0 .../jobs/compose/MastodonComposeJob.kt | 0 .../jobs/compose/TwitterComposeJob.kt | 0 .../jobs/database/DeleteDbStatusJob.kt | 0 .../jobs/dm/DirectMessageDeleteJob.kt | 0 .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 0 .../twiderex/jobs/dm/DirectMessageSendJob.kt | 0 .../jobs/dm/TwitterDirectMessageSendJob.kt | 0 .../twiderex/jobs/draft/RemoveDraftJob.kt | 0 .../twiderex/jobs/draft/SaveDraftJob.kt | 4 +- .../twiderex/jobs/status/DeleteStatusJob.kt | 4 +- .../twiderex/jobs/status/LikeStatusJob.kt | 0 .../twiderex/jobs/status/MastodonVoteJob.kt | 4 +- .../twiderex/jobs/status/RetweetStatusJob.kt | 0 .../twidere/twiderex/jobs/status/StatusJob.kt | 8 +- .../jobs/status/UnRetweetStatusJob.kt | 0 .../twiderex/jobs/status/UnlikeStatusJob.kt | 0 .../com/twidere/twiderex/kmp/ExifScrambler.kt | 2 +- .../com/twidere/twiderex/kmp/FileResolver.kt | 2 +- .../twidere/twiderex/kmp/LocationProvider.kt | 30 +++++ .../twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- .../twidere/twiderex/model/job/ComposeData.kt | 0 .../model/job/DirectMessageDeleteData.kt | 0 .../model/job/DirectMessageSendData.kt | 0 .../twiderex/model/job/StatusResult.kt | 0 .../twidere/twiderex/model/kmp/Location.kt | 26 ++++ .../notification/AppNotificationManager.kt | 9 +- .../notification/InAppNotification.kt | 9 ++ .../twiderex/utils/TwitterErrorHandling.kt | 91 ++++++++++++++ .../twiderex/viewmodel/DraftViewModel.kt | 10 +- .../twiderex/viewmodel/MediaViewModel.kt | 33 ++--- .../compose/ComposeSearchUserViewModel.kt | 6 + .../viewmodel/compose/ComposeViewModel.kt | 106 ++++++---------- .../MastodonComposeSearchHashtagViewModel.kt | 6 + .../twiderex/viewmodel/dm/DMEventViewModel.kt | 3 + .../viewmodel/lists/ListsUserViewModel.kt | 4 +- .../viewmodel/lists/ListsViewModel.kt | 4 +- .../mastodon/MastodonHashtagViewModel.kt | 1 + .../MastodonSearchHashtagViewModel.kt | 1 + .../mastodon/MastodonSignInViewModel.kt | 3 +- .../viewmodel/search/SearchUserViewModel.kt | 1 + .../settings/AccountNotificationViewModel.kt | 2 + .../viewmodel/settings/LayoutViewModel.kt | 1 + .../twitter/TwitterSignInViewModel.kt | 13 +- .../twitter/user/TwitterUserViewModel.kt | 4 +- .../viewmodel/user/UserTimelineViewModel.kt | 2 + .../twiderex/viewmodel/user/UserViewModel.kt | 8 +- .../twidere/twiderex/action/ComposeAction.kt | 34 +++++ .../twiderex/action/DirectMessageAction.kt | 38 ++++++ .../twidere/twiderex/action/DraftAction.kt | 31 +++++ .../twidere/twiderex/action/MediaAction.kt | 35 ++++++ .../twidere/twiderex/action/StatusActions.kt | 38 ++++++ .../com/twidere/twiderex/kmp/ExifScrambler.kt | 30 +++++ .../com/twidere/twiderex/kmp/FileResolver.kt | 42 +++++++ .../twidere/twiderex/kmp/LocationProvider.kt | 35 ++++++ .../twidere/twiderex/kmp/RemoteNavigator.kt | 36 ++++++ 106 files changed, 969 insertions(+), 289 deletions(-) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/action/ComposeAction.kt (97%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt (95%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt rename android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt => common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt (60%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/action/StatusActions.kt (81%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt (100%) rename android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt => common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt (92%) rename android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt => common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt (74%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt rename android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt => common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt (78%) rename {android/src/main/kotlin/com/twidere/twiderex/kmp/android => common/src/androidMain/kotlin/com/twidere/twiderex/notification}/AndroidNotificationManager.kt (84%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt (100%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt (100%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/kmp/FileResolver.kt (97%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/job/ComposeData.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/model/job/StatusResult.kt (100%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt (93%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt index 651014c70..dd79ea908 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -34,13 +34,13 @@ import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.http.TwidereHttpConfigProvider import com.twidere.twiderex.http.TwidereServiceFactory +import com.twidere.twiderex.kmp.AndroidExifScrambler +import com.twidere.twiderex.kmp.AndroidFileResolver +import com.twidere.twiderex.kmp.AndroidNotificationManager +import com.twidere.twiderex.kmp.AndroidRemoteNavigator import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator -import com.twidere.twiderex.kmp.android.AndroidExifScrambler -import com.twidere.twiderex.kmp.android.AndroidFileResolver -import com.twidere.twiderex.kmp.android.AndroidNotificationManager -import com.twidere.twiderex.kmp.android.AndroidRemoteNavigator import com.twidere.twiderex.model.AccountPreferences import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.InAppNotification diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 95655f4bc..cae4c00c4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -348,9 +348,7 @@ private fun StatusMediaInfo( callback.invoke() currentMedia.fileName?.let { viewModel.shareMedia( - currentMedia = currentMedia, - target = it, - context = context + currentMedia = currentMedia ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt index 983a91391..04dc270ab 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -20,71 +20,54 @@ */ package com.twidere.twiderex.utils -import android.content.Intent -import android.content.Intent.ACTION_VIEW -import android.net.Uri -import com.twidere.services.http.MicroBlogHttpException -import com.twidere.services.mastodon.model.exceptions.MastodonException -import com.twidere.services.twitter.TwitterErrorCodes -import com.twidere.services.twitter.model.exceptions.TwitterApiException -import com.twidere.services.twitter.model.exceptions.TwitterApiExceptionV2 -import com.twidere.services.utils.MicroBlogJsonException -import com.twidere.twiderex.R -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.notification.NotificationEvent -import com.twidere.twiderex.notification.StringNotificationEvent -import com.twidere.twiderex.notification.StringResNotificationEvent -import com.twidere.twiderex.notification.StringResWithActionNotificationEvent -import java.util.concurrent.CancellationException - -fun Throwable.generateNotificationEvent(): NotificationEvent? { - return when (this) { - is MicroBlogHttpException -> { - when (this.httpCode) { - HttpErrorCodes.TooManyRequests -> { - return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) - } - else -> null - } - } - is MicroBlogJsonException -> { - microBlogErrorMessage?.let { StringNotificationEvent(it) } - } - is TwitterApiException -> { - when (this.errors?.firstOrNull()?.code) { - TwitterErrorCodes.TemporarilyLocked -> { - StringResWithActionNotificationEvent( - R.string.common_alerts_account_temporarily_locked_title, - R.string.common_alerts_account_temporarily_locked_message, - actionId = R.string.common_controls_actions_ok - ) { - context.startActivity( - Intent( - ACTION_VIEW, - Uri.parse("https://twitter.com/login") - ) - ) - } - } - TwitterErrorCodes.RateLimitExceeded -> null - else -> microBlogErrorMessage?.let { StringNotificationEvent(it) } - } - } - is TwitterApiExceptionV2 -> { - detail?.let { StringNotificationEvent(it) } - } - is MastodonException -> { - microBlogErrorMessage?.let { StringNotificationEvent(it) } - } - !is CancellationException -> { - message?.let { StringNotificationEvent(it) } - } - else -> null - } -} - -fun Throwable.notify(notification: InAppNotification) { - generateNotificationEvent()?.let { - notification.show(it) - } ?: throw this -} +// fun Throwable.generateNotificationEvent(): NotificationEvent? { +// return when (this) { +// is MicroBlogHttpException -> { +// when (this.httpCode) { +// HttpErrorCodes.TooManyRequests -> { +// return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) +// } +// else -> null +// } +// } +// is MicroBlogJsonException -> { +// microBlogErrorMessage?.let { StringNotificationEvent(it) } +// } +// is TwitterApiException -> { +// when (this.errors?.firstOrNull()?.code) { +// TwitterErrorCodes.TemporarilyLocked -> { +// StringResWithActionNotificationEvent( +// R.string.common_alerts_account_temporarily_locked_title, +// R.string.common_alerts_account_temporarily_locked_message, +// actionId = R.string.common_controls_actions_ok +// ) { +// context.startActivity( +// Intent( +// ACTION_VIEW, +// Uri.parse("https://twitter.com/login") +// ) +// ) +// } +// } +// TwitterErrorCodes.RateLimitExceeded -> null +// else -> microBlogErrorMessage?.let { StringNotificationEvent(it) } +// } +// } +// is TwitterApiExceptionV2 -> { +// detail?.let { StringNotificationEvent(it) } +// } +// is MastodonException -> { +// microBlogErrorMessage?.let { StringNotificationEvent(it) } +// } +// !is CancellationException -> { +// message?.let { StringNotificationEvent(it) } +// } +// else -> null +// } +// } +// +// fun Throwable.notify(notification: InAppNotification) { +// generateNotificationEvent()?.let { +// notification.show(it) +// } ?: throw this +// } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 3addb38d0..191285ef3 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,4 +1,3 @@ -import Versions.ksp import org.jetbrains.compose.compose plugins { @@ -57,6 +56,7 @@ kotlin { dependencies { api("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") api("androidx.savedstate:savedstate-ktx:1.1.0") + implementation("androidx.core:core-ktx:1.7.0-alpha01") api("io.insert-koin:koin-android:${Versions.koin}") api("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") implementation("androidx.room:room-runtime:${Versions.room}") @@ -66,6 +66,7 @@ kotlin { implementation("io.coil-kt:coil-base:${Versions.coil}") api("androidx.datastore:datastore:${Versions.datastore}") api("androidx.datastore:datastore-preferences:${Versions.datastore}") + implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/AndroidManifest.xml b/common/src/androidMain/AndroidManifest.xml index e6c3f9ac5..ec1450410 100644 --- a/common/src/androidMain/AndroidManifest.xml +++ b/common/src/androidMain/AndroidManifest.xml @@ -1,6 +1,8 @@ + + diff --git a/android/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index fa671a1a7..4fb641993 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -29,10 +29,10 @@ import com.twidere.twiderex.worker.compose.TwitterComposeWorker import com.twidere.twiderex.worker.draft.RemoveDraftWorker import com.twidere.twiderex.worker.draft.SaveDraftWorker -class ComposeAction( +actual class ComposeAction( private val workManager: WorkManager, ) { - fun commit( + actual fun commit( accountKey: MicroBlogKey, platformType: PlatformType, data: ComposeData, diff --git a/android/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt index fc66e0d0e..3f1163a43 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -27,10 +27,10 @@ import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.worker.dm.DirectMessageDeleteWorker import com.twidere.twiderex.worker.dm.TwitterDirectMessageSendWorker -class DirectMessageAction( +actual class DirectMessageAction( private val workManager: WorkManager, ) { - fun send( + actual fun send( platformType: PlatformType, data: DirectMessageSendData, ) { @@ -44,7 +44,7 @@ class DirectMessageAction( } } - fun delete( + actual fun delete( data: DirectMessageDeleteData ) { val worker = DirectMessageDeleteWorker.createWorker(deleteData = data) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt new file mode 100644 index 000000000..52a1c01e1 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.job.ComposeData + +actual class DraftAction { + actual fun delete(id: String) { + } + + actual fun save(composeData: ComposeData) { + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt similarity index 60% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index 92c747f34..f44285658 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/LocationManagerExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -18,23 +18,18 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.extensions +package com.twidere.twiderex.action -import android.location.Location -import android.location.LocationManager +import com.twidere.twiderex.model.MicroBlogKey -fun LocationManager.getCachedLocation(): Location? { - var location: Location? = null - try { - location = getLastKnownLocation(LocationManager.GPS_PROVIDER) - } catch (ignore: SecurityException) { +actual class MediaAction { + actual fun download( + source: String, + target: String, + accountKey: MicroBlogKey + ) { } - if (location != null) return location - try { - location = getLastKnownLocation(LocationManager.NETWORK_PROVIDER) - } catch (ignore: SecurityException) { + actual fun share(source: String, accountKey: MicroBlogKey) { } - - return location } diff --git a/android/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt similarity index 81% rename from android/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt index bc3290400..ee9e13280 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/action/StatusActions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.action -import androidx.compose.runtime.compositionLocalOf import androidx.work.WorkManager import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.job.StatusResult @@ -34,21 +33,11 @@ import com.twidere.twiderex.worker.status.StatusWorker import com.twidere.twiderex.worker.status.UnLikeWorker import com.twidere.twiderex.worker.status.UnRetweetWorker import com.twidere.twiderex.worker.status.UpdateStatusWorker -import javax.inject.Inject -val LocalStatusActions = compositionLocalOf { error("No LocalStatusActions") } - -interface IStatusActions { - fun like(status: UiStatus, account: AccountDetails) {} - fun retweet(status: UiStatus, account: AccountDetails) {} - fun delete(status: UiStatus, account: AccountDetails) {} - fun vote(status: UiStatus, account: AccountDetails, votes: List) {} -} - -class StatusActions @Inject constructor( +actual class StatusActions( private val workManager: WorkManager, ) : IStatusActions { - override fun delete(status: UiStatus, account: AccountDetails) { + actual override fun delete(status: UiStatus, account: AccountDetails) { workManager.beginWith( DeleteStatusWorker.create( status = status, @@ -58,7 +47,7 @@ class StatusActions @Inject constructor( .enqueue() } - override fun like(status: UiStatus, account: AccountDetails) { + actual override fun like(status: UiStatus, account: AccountDetails) { workManager.beginWith( UpdateStatusWorker.create( StatusResult( @@ -82,7 +71,7 @@ class StatusActions @Inject constructor( ).then(listOf(UpdateStatusWorker.create())).enqueue() } - override fun retweet(status: UiStatus, account: AccountDetails) { + actual override fun retweet(status: UiStatus, account: AccountDetails) { workManager.beginWith( UpdateStatusWorker.create( StatusResult( @@ -106,7 +95,7 @@ class StatusActions @Inject constructor( ).then(listOf(UpdateStatusWorker.create())).enqueue() } - override fun vote(status: UiStatus, account: AccountDetails, votes: List) { + actual override fun vote(status: UiStatus, account: AccountDetails, votes: List) { workManager.beginWith( MastodonVoteWorker.create( statusKey = status.statusKey, @@ -116,5 +105,3 @@ class StatusActions @Inject constructor( ).enqueue() } } - -object FakeStatusActions : IStatusActions diff --git a/android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index fb9112bca..4b0cfea74 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.kmp.android +package com.twidere.twiderex.kmp import android.content.Context import android.graphics.Bitmap @@ -26,12 +26,11 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.core.net.toUri import androidx.exifinterface.media.ExifInterface -import com.twidere.twiderex.kmp.ExifScrambler import java.io.File import java.util.UUID -class AndroidExifScrambler(private val context: Context) : ExifScrambler { - override fun removeExifData(file: String, compress: Int): String { +actual class ExifScrambler(private val context: Context) { + actual fun removeExifData(file: String, compress: Int): String { // first get input stream val uri = Uri.parse(file) val contentResolver = context.contentResolver @@ -81,7 +80,7 @@ class AndroidExifScrambler(private val context: Context) : ExifScrambler { return uri.toString() } - override fun deleteCacheFile(file: String) { + actual fun deleteCacheFile(file: String) { Uri.parse(file).path?.let { File(it) }?.apply { diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt similarity index 74% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 0cfc7e6a1..6bcfa99dc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidFileResolver.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -18,28 +18,27 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.kmp.android +package com.twidere.twiderex.kmp import android.content.ContentResolver import android.net.Uri -import com.twidere.twiderex.kmp.FileResolver import java.io.InputStream import java.io.OutputStream -class AndroidFileResolver(private val contentResolver: ContentResolver) : FileResolver { - override fun getMimeType(file: String): String? { +actual class FileResolver(private val contentResolver: ContentResolver) { + actual fun getMimeType(file: String): String? { return contentResolver.getType(Uri.parse(file)) } - override fun getFileSize(file: String): Long? { + actual fun getFileSize(file: String): Long? { return contentResolver.openFileDescriptor(Uri.parse(file), "r")?.statSize } - override fun openInputStream(file: String): InputStream? { + actual fun openInputStream(file: String): InputStream? { return contentResolver.openInputStream(Uri.parse(file)) } - override fun openOutputStream(file: String): OutputStream? { + actual fun openOutputStream(file: String): OutputStream? { return contentResolver.openOutputStream(Uri.parse(file)) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt new file mode 100644 index 000000000..ef383b6d7 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -0,0 +1,86 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import android.Manifest +import android.location.Criteria +import android.location.LocationListener +import android.location.LocationManager +import android.os.Bundle +import androidx.annotation.RequiresPermission +import com.twidere.twiderex.model.kmp.Location +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +actual class LocationProvider( + private val locationManager: LocationManager, +) : LocationListener { + private val _location = MutableStateFlow(null) + actual val location: SharedFlow = _location.asSharedFlow() + + @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) + actual fun enable() { + val criteria = Criteria() + criteria.accuracy = Criteria.ACCURACY_FINE + val provider = locationManager.getBestProvider(criteria, true) ?: return + locationManager.requestLocationUpdates(provider, 0, 0f, this) + locationManager.getCachedLocation()?.let { + _location.value = Location( + latitude = it.latitude, + longitude = it.longitude, + ) + } + } + + actual fun disable() { + locationManager.removeUpdates(this) + } + + override fun onLocationChanged(p0: android.location.Location) { + _location.value = p0.let { + Location( + latitude = it.latitude, + longitude = it.longitude, + ) + } + } + + // compatibility fix for Api < 22 + override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { + } +} + +private fun LocationManager.getCachedLocation(): android.location.Location? { + var location: android.location.Location? = null + try { + location = getLastKnownLocation(LocationManager.GPS_PROVIDER) + } catch (ignore: SecurityException) { + } + + if (location != null) return location + try { + location = getLastKnownLocation(LocationManager.NETWORK_PROVIDER) + } catch (ignore: SecurityException) { + } + + return location +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt similarity index 78% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index 77bf60422..6435bd4f0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidRemoteNavigator.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -18,17 +18,16 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.kmp.android +package com.twidere.twiderex.kmp import android.content.Context import android.content.Intent import android.net.Uri import com.twidere.twiderex.extensions.shareMedia import com.twidere.twiderex.extensions.shareText -import com.twidere.twiderex.kmp.RemoteNavigator -class AndroidRemoteNavigator(private val context: Context) : RemoteNavigator { - override fun openDeepLink(deeplink: String, fromBackground: Boolean) { +actual class RemoteNavigator(private val context: Context) { + actual fun openDeepLink(deeplink: String, fromBackground: Boolean) { context.startActivity( Intent( Intent.ACTION_VIEW, @@ -39,7 +38,7 @@ class AndroidRemoteNavigator(private val context: Context) : RemoteNavigator { ) } - override fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean) { + actual fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean) { context.shareMedia( uri = Uri.parse(filePath), mimeType = mimeType, @@ -47,7 +46,7 @@ class AndroidRemoteNavigator(private val context: Context) : RemoteNavigator { ) } - override fun shareText(content: String, fromBackground: Boolean) { + actual fun shareText(content: String, fromBackground: Boolean) { context.shareText( content = content, fromOutsideOfActivity = fromBackground diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AndroidNotificationManager.kt similarity index 84% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/notification/AndroidNotificationManager.kt index 70a007fc1..deffb0bf9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidNotificationManager.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AndroidNotificationManager.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.kmp.android +package com.twidere.twiderex.notification import android.app.PendingIntent import android.content.Context @@ -28,20 +28,17 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.twidere.twiderex.R -import com.twidere.twiderex.notification.AppNotification -import com.twidere.twiderex.notification.AppNotificationManager import kotlinx.coroutines.MainScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit -import kotlin.time.ExperimentalTime -class AndroidNotificationManager( +actual class AppNotificationManager( private val context: Context, private val notificationManagerCompat: NotificationManagerCompat -) : AppNotificationManager { +) { val scope = MainScope() - override fun notify(notificationId: Int, appNotification: AppNotification) { + actual fun notify(notificationId: Int, appNotification: AppNotification) { val builder = NotificationCompat.Builder( context, appNotification.channelId @@ -52,7 +49,11 @@ class AndroidNotificationManager( .setContentTitle(appNotification.title) .setOngoing(appNotification.ongoing) .setSilent(appNotification.silent) - .setProgress(appNotification.progressMax, appNotification.progress, appNotification.progressIndeterminate) + .setProgress( + appNotification.progressMax, + appNotification.progress, + appNotification.progressIndeterminate + ) appNotification.content?.let { builder.setContentText(it) .setStyle(NotificationCompat.BigTextStyle().bigText(it)) @@ -77,8 +78,7 @@ class AndroidNotificationManager( notificationManagerCompat.notify(notificationId, builder.build()) } - @OptIn(ExperimentalTime::class) - override fun notifyTransient( + actual fun notifyTransient( notificationId: Int, appNotification: AppNotification, duration: Long, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index 8ac4578ab..931365c83 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -31,15 +31,6 @@ interface NotificationWithActionEvent : NotificationEvent { val action: EventActionContext.() -> Unit } -class StringNotificationEvent( - private val message: String, -) : NotificationEvent { - @Composable - override fun getMessage(): String { - return message - } -} - open class StringResNotificationEvent( @StringRes val messageId: Int, ) : NotificationEvent { diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt new file mode 100644 index 000000000..95c0de5ce --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.job.ComposeData + +expect class ComposeAction { + fun commit( + accountKey: MicroBlogKey, + platformType: PlatformType, + data: ComposeData, + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt new file mode 100644 index 000000000..a9b8997b9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.job.DirectMessageDeleteData +import com.twidere.twiderex.model.job.DirectMessageSendData + +expect class DirectMessageAction { + fun send( + platformType: PlatformType, + data: DirectMessageSendData, + ) + + fun delete( + data: DirectMessageDeleteData + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt new file mode 100644 index 000000000..a3eb3114c --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.job.ComposeData + +expect class DraftAction { + fun delete(id: String) + fun save(composeData: ComposeData) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt new file mode 100644 index 000000000..6672ee6e4 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.MicroBlogKey + +expect class MediaAction { + fun download(source: String, target: String, accountKey: MicroBlogKey) + fun share(source: String, accountKey: MicroBlogKey) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt new file mode 100644 index 000000000..c68028205 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -0,0 +1,43 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import androidx.compose.runtime.compositionLocalOf +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.ui.UiStatus + +val LocalStatusActions = compositionLocalOf { error("No LocalStatusActions") } + +interface IStatusActions { + fun like(status: UiStatus, account: AccountDetails) {} + fun retweet(status: UiStatus, account: AccountDetails) {} + fun delete(status: UiStatus, account: AccountDetails) {} + fun vote(status: UiStatus, account: AccountDetails, votes: List) {} +} + +expect class StatusActions : IStatusActions { + override fun delete(status: UiStatus, account: AccountDetails) + override fun like(status: UiStatus, account: AccountDetails) + override fun retweet(status: UiStatus, account: AccountDetails) + override fun vote(status: UiStatus, account: AccountDetails, votes: List) +} + +object FakeStatusActions : IStatusActions diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index b3baf87fc..9aba1ef10 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -56,7 +56,6 @@ fun Flow.flowWithLifecycle( close() } - fun Flow.asStateIn( scope: CoroutineScope, initialValue: T diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt index 8b6342178..903cf4165 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.jobs.draft import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.DraftRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError class SaveDraftJob( private val repository: DraftRepository, @@ -42,7 +42,7 @@ class SaveDraftJob( ) true } catch (e: Throwable) { - e.notify(inAppNotification) + inAppNotification.notifyError(e) throw e } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt index d4b05b7e8..a0563ffb0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError class DeleteStatusJob( private val accountRepository: AccountRepository, @@ -45,7 +45,7 @@ class DeleteStatusJob( try { service.delete(status.statusId) } catch (e: Throwable) { - e.notify(inAppNotification) + inAppNotification.notifyError(e) throw e } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index f4585d91e..9834440ac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.ui.UiPoll import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError class MastodonVoteJob( private val accountRepository: AccountRepository, @@ -68,7 +68,7 @@ class MastodonVoteJob( poll = originPoll ) } - e.notify(inAppNotification) + inAppNotification.notifyError(e) throw e } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt index 53e56afc2..2af297e33 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError abstract class StatusJob( private val accountRepository: AccountRepository, @@ -63,16 +63,16 @@ abstract class StatusJob( ) } else -> { - e.notify(inAppNotification) + inAppNotification.notifyError(e) fallback(accountKey, status) } } } ?: run { - e.notify(inAppNotification) + inAppNotification.notifyError(e) fallback(accountKey, status) } } catch (e: Throwable) { - e.notify(inAppNotification) + inAppNotification.notifyError(e) fallback(accountKey, status) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 6b68a250c..9d3817bb8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.kmp -interface ExifScrambler { +expect class ExifScrambler { fun removeExifData(file: String, compress: Int = 100): String fun deleteCacheFile(file: String) diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 4df89a1ba..8e9fffc10 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.kmp import java.io.InputStream import java.io.OutputStream -interface FileResolver { +expect class FileResolver { fun getMimeType(file: String): String? fun getFileSize(file: String): Long? diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt new file mode 100644 index 000000000..53521b7e9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import com.twidere.twiderex.model.kmp.Location +import kotlinx.coroutines.flow.SharedFlow + +expect class LocationProvider { + val location: SharedFlow + fun enable() + fun disable() +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index 9a6107c14..ceea6d6a5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.kmp -interface RemoteNavigator { +expect class RemoteNavigator { fun openDeepLink(deeplink: String, fromBackground: Boolean = false) fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean = false) diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/job/ComposeData.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/model/job/StatusResult.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/model/job/StatusResult.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt new file mode 100644 index 000000000..5ff3be18f --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt @@ -0,0 +1,26 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.kmp + +data class Location( + val latitude: Double, + val longitude: Double, +) diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 42cb89d63..ddd997a06 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -23,10 +23,15 @@ package com.twidere.twiderex.notification import android.graphics.Bitmap import java.util.concurrent.TimeUnit -interface AppNotificationManager { +expect class AppNotificationManager { fun notify(notificationId: Int, appNotification: AppNotification) - fun notifyTransient(notificationId: Int, appNotification: AppNotification, duration: Long = 5, durationTimeUnit: TimeUnit = TimeUnit.SECONDS) + fun notifyTransient( + notificationId: Int, + appNotification: AppNotification, + duration: Long = 5, + durationTimeUnit: TimeUnit = TimeUnit.SECONDS + ) } class AppNotification( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index 03e3fd34b..cef04eb5d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -31,6 +31,15 @@ interface NotificationEvent { fun getMessage(): String } +class StringNotificationEvent( + private val message: String, +) : NotificationEvent { + @Composable + override fun getMessage(): String { + return message + } +} + class InAppNotification { private val _source = MutableStateFlow?>(null) val source diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt new file mode 100644 index 000000000..580c04537 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -0,0 +1,91 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils + +import com.twidere.services.http.MicroBlogHttpException +import com.twidere.services.mastodon.model.exceptions.MastodonException +import com.twidere.services.twitter.TwitterErrorCodes +import com.twidere.services.twitter.model.exceptions.TwitterApiException +import com.twidere.services.twitter.model.exceptions.TwitterApiExceptionV2 +import com.twidere.services.utils.MicroBlogJsonException +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.notification.NotificationEvent +import com.twidere.twiderex.notification.StringNotificationEvent +import java.util.concurrent.CancellationException + +internal fun InAppNotification.notifyError(e: Throwable) { + val event = e.generateNotificationEvent() + if (event != null) { + show(event) + } +} + +fun Throwable.generateNotificationEvent(): NotificationEvent? { + return when (this) { + is MicroBlogHttpException -> { + when (this.httpCode) { + HttpErrorCodes.TooManyRequests -> { + TODO("Implementation") + // return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) + } + else -> null + } + } + is MicroBlogJsonException -> { + microBlogErrorMessage?.let { StringNotificationEvent(it) } + } + is TwitterApiException -> { + when (this.errors?.firstOrNull()?.code) { + TwitterErrorCodes.TemporarilyLocked -> { + TODO("Implementation") + // StringResWithActionNotificationEvent( + // R.string.common_alerts_account_temporarily_locked_title, + // R.string.common_alerts_account_temporarily_locked_message, + // actionId = R.string.common_controls_actions_ok + // ) { + // context.startActivity( + // Intent( + // Intent.ACTION_VIEW, + // Uri.parse("https://twitter.com/login") + // ) + // ) + // } + } + TwitterErrorCodes.RateLimitExceeded -> null + else -> microBlogErrorMessage?.let { StringNotificationEvent(it) } + } + } + is TwitterApiExceptionV2 -> { + detail?.let { StringNotificationEvent(it) } + } + is MastodonException -> { + microBlogErrorMessage?.let { StringNotificationEvent(it) } + } + !is CancellationException -> { + message?.let { StringNotificationEvent(it) } + } + else -> null + } +} + +private object HttpErrorCodes { + const val TooManyRequests = 429 +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index 3dcd361ac..fdd163ef1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -20,22 +20,18 @@ */ package com.twidere.twiderex.viewmodel -import androidx.core.app.NotificationManagerCompat -import androidx.work.WorkManager +import com.twidere.twiderex.action.DraftAction import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.repository.DraftRepository -import com.twidere.twiderex.worker.draft.RemoveDraftWorker import moe.tlaster.precompose.viewmodel.ViewModel class DraftViewModel( private val repository: DraftRepository, - private val workManager: WorkManager, - private val notificationManagerCompat: NotificationManagerCompat, + private val draftAction: DraftAction ) : ViewModel() { fun delete(it: UiDraft) { - workManager.beginWith(RemoveDraftWorker.create(it.draftId)).enqueue() - notificationManagerCompat.cancel(it.draftId.hashCode()) + draftAction.delete(it.draftId) } val source by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index a74e2977b..a229f8a3d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -20,19 +20,13 @@ */ package com.twidere.twiderex.viewmodel -import android.content.Context import android.net.Uri -import androidx.work.WorkManager -import com.twidere.twiderex.R +import com.twidere.twiderex.action.MediaAction import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia -import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.utils.FileProviderHelper -import com.twidere.twiderex.worker.DownloadMediaWorker -import com.twidere.twiderex.worker.ShareMediaWorker import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -44,8 +38,7 @@ import moe.tlaster.precompose.viewmodel.viewModelScope class MediaViewModel( private val repository: StatusRepository, private val accountRepository: AccountRepository, - private val inAppNotification: InAppNotification, - private val workManager: WorkManager, + private val mediaAction: MediaAction, private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { @@ -55,33 +48,21 @@ class MediaViewModel( fun saveFile(currentMedia: UiMedia, target: Uri) = viewModelScope.launch { val account = account.lastOrNull() ?: return@launch currentMedia.mediaUrl?.let { - DownloadMediaWorker.create( + mediaAction.download( accountKey = account.accountKey, source = it, - target = target + target = target.toString() ) - }?.let { - workManager.enqueue(it) } } - fun shareMedia(currentMedia: UiMedia, target: String, context: Context) = viewModelScope.launch { + fun shareMedia(currentMedia: UiMedia) = viewModelScope.launch { val account = account.lastOrNull() ?: return@launch - val uri = FileProviderHelper.getUriFromMedia(target, context) - inAppNotification.show(R.string.common_alerts_media_sharing_title) currentMedia.mediaUrl?.let { - DownloadMediaWorker.create( - accountKey = account.accountKey, + mediaAction.share( source = it, - target = uri + accountKey = account.accountKey ) - }?.let { - workManager.beginWith(it) - .then( - ShareMediaWorker.create( - target = uri - ) - ).enqueue() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index f24c884d5..3b3536d5c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -20,7 +20,13 @@ */ package com.twidere.twiderex.viewmodel.compose +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.cachedIn +import com.twidere.services.microblog.SearchService +import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 00ae4ed4a..0d04c7aed 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -20,23 +20,15 @@ */ package com.twidere.twiderex.viewmodel.compose -import android.Manifest.permission -import android.location.Criteria -import android.location.Location -import android.location.LocationListener -import android.location.LocationManager -import android.net.Uri -import android.os.Bundle -import androidx.annotation.RequiresPermission import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue -import androidx.work.WorkManager import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction +import com.twidere.twiderex.action.DraftAction import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection -import com.twidere.twiderex.extensions.getCachedLocation +import com.twidere.twiderex.kmp.LocationProvider import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility @@ -51,8 +43,7 @@ import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.MastodonEmojiCache -import com.twidere.twiderex.utils.notify -import com.twidere.twiderex.worker.draft.SaveDraftWorker +import com.twidere.twiderex.utils.notifyError import com.twitter.twittertext.Extractor import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -60,6 +51,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -80,32 +72,32 @@ class DraftItemViewModel( class DraftComposeViewModel( draftRepository: DraftRepository, - locationManager: LocationManager, composeAction: ComposeAction, repository: StatusRepository, userRepository: UserRepository, - workManager: WorkManager, + draftAction: DraftAction, inAppNotification: InAppNotification, accountRepository: AccountRepository, + locationProvider: LocationProvider, draft: UiDraft, ) : ComposeViewModel( - draftRepository, - locationManager, - composeAction, - repository, - userRepository, - workManager, - inAppNotification, - accountRepository, - draft.statusKey, - draft.composeType, + draftRepository = draftRepository, + composeAction = composeAction, + repository = repository, + userRepository = userRepository, + draftAction = draftAction, + inAppNotification = inAppNotification, + accountRepository = accountRepository, + locationProvider = locationProvider, + statusKey = draft.statusKey, + composeType = draft.composeType, ) { override val draftId: String = draft.draftId init { setText(TextFieldValue(draft.content)) - putImages(draft.media.map { Uri.parse(it) }) + putImages(draft.media) excludedReplyUserIds.value = draft.excludedReplyUserIds ?: emptyList() } } @@ -155,16 +147,16 @@ class VoteState { open class ComposeViewModel( protected val draftRepository: DraftRepository, - private val locationManager: LocationManager, - protected val composeAction: ComposeAction, + private val composeAction: ComposeAction, protected val repository: StatusRepository, private val userRepository: UserRepository, - private val workManager: WorkManager, + private val draftAction: DraftAction, private val inAppNotification: InAppNotification, private val accountRepository: AccountRepository, + private val locationProvider: LocationProvider, protected val statusKey: MicroBlogKey?, val composeType: ComposeType, -) : ViewModel(), LocationListener { +) : ViewModel() { private val account by lazy { accountRepository.activeAccount.asStateIn(viewModelScope, null) } @@ -187,7 +179,9 @@ open class ComposeViewModel( draftRepository.sourceCount } - val location = MutableStateFlow(null) + val location by lazy { + locationProvider.location.asStateIn(viewModelScope, null) + } val excludedReplyUserIds = MutableStateFlow>(emptyList()) val replyToUserName by lazy { combine(account, status) { account, status -> @@ -219,7 +213,7 @@ open class ComposeViewModel( lookupService = account.service as LookupService, ) } catch (e: Throwable) { - e.notify(inAppNotification) + inAppNotification.notifyError(e) emptyList() } finally { loadingReplyUser.value = false @@ -240,7 +234,7 @@ open class ComposeViewModel( val isContentWarningEnabled = MutableStateFlow(false) val contentWarningTextFieldValue = MutableStateFlow(TextFieldValue()) val textFieldValue = MutableStateFlow(TextFieldValue()) - val images = MutableStateFlow>(emptyList()) + val images = MutableStateFlow>(emptyList()) val canSend = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } .asStateIn(viewModelScope, false) @@ -341,20 +335,14 @@ open class ComposeViewModel( fun saveDraft() { textFieldValue.value.text.let { text -> - workManager - .beginWith( - SaveDraftWorker.create( - buildComposeData(text) - ) - ) - .enqueue() + draftAction.save(buildComposeData(text)) } } private fun buildComposeData(text: String) = ComposeData( content = text, draftId = draftId, - images = images.value.map { it.toString() }, + images = images.value, composeType = composeType, statusKey = statusKey, lat = location.value?.latitude, @@ -369,7 +357,7 @@ open class ComposeViewModel( isThreadMode = enableThreadMode.value, ) - fun putImages(value: List) = viewModelScope.launch { + fun putImages(value: List) = viewModelScope.launch { val imageLimit = imageLimit.lastOrNull() ?: 4 images.value.let { value + it @@ -390,42 +378,18 @@ open class ComposeViewModel( }.asStateIn(viewModelScope, 4) } - @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION]) fun trackingLocation() { locationEnabled.value = true - val criteria = Criteria() - criteria.accuracy = Criteria.ACCURACY_FINE - val provider = locationManager.getBestProvider(criteria, true) ?: return - locationManager.requestLocationUpdates(provider, 0, 0f, this) - locationManager.getCachedLocation()?.let { - location.value = it - } + locationProvider.enable() } fun disableLocation() { - location.value = null locationEnabled.value = false - locationManager.removeUpdates(this) - } - - override fun onLocationChanged(location: Location) { - this.location.value = location - } - - // compatibility fix for Api < 22 - override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { + locationProvider.disable() } - override fun onCleared() { - if (locationEnabled.value) { - locationManager.removeUpdates(this) - } - } - - fun removeImage(item: Uri) { - images.value.let { - it - item - }.let { + fun removeImage(item: String) { + (images.value - item).let { images.value = it } } @@ -456,4 +420,8 @@ open class ComposeViewModel( fun insertEmoji(emoji: UiEmoji) { insertText("${if (textFieldValue.value.selection.start != 0) " " else ""}:${emoji.shortcode}: ") } + + override fun onCleared() { + locationProvider.disable() + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 1bdc2761b..22fbdfa65 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -20,7 +20,13 @@ */ package com.twidere.twiderex.viewmodel.compose +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.cachedIn +import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 41f0e5895..8372375b4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.viewmodel.dm import android.net.Uri +import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction @@ -34,7 +35,9 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index d8324d1ec..14eea0259 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError import com.twidere.twiderex.viewmodel.user.UserListViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -148,7 +148,7 @@ class ListsAddMemberViewModel( runCatching { request() }.onFailure { - it.notify(inAppNotification) + inAppNotification.notifyError(it) loading.value = false }.onSuccess { loading.value = false diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 8e99f8fa5..28a78ec7a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull @@ -98,7 +98,7 @@ abstract class ListsOperatorViewModel( modifySuccess.value = true onResult(true, result) }.onFailure { - it.notify(inAppNotification) + inAppNotification.notifyError(it) modifySuccess.value = false onResult(false, null) loading.value = false diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 14256a0fd..907fcbdaf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.mastodon +import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 8caa2f07d..45181796a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index 362b6151f..26ba53735 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -33,6 +33,7 @@ import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json +import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -105,7 +106,7 @@ class MastodonSignInViewModel( } } }.onFailure { - inAppNotification.show(it.message.toString()) + inAppNotification.notifyError(it) } loading.value = false finished.invoke(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index dc80ac4d4..f6f14845e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.viewmodel.search import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 6af5419c5..ec2a7d942 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -22,7 +22,9 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index ff8f15aa9..467bcfc7a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 3560f09ef..e36ec640d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.twitter -import com.twidere.services.http.MicroBlogException import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.BuildConfig @@ -35,13 +34,11 @@ import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.json -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope -import retrofit2.HttpException -import java.io.IOException class TwitterSignInViewModel( private val repository: AccountRepository, @@ -129,12 +126,8 @@ class TwitterSignInViewModel( } } } - } catch (e: MicroBlogException) { - e.notify(inAppNotification) - } catch (e: IOException) { - e.message?.let { inAppNotification.show(it) } - } catch (e: HttpException) { - e.message?.let { inAppNotification.show(it) } + } catch (e: Throwable) { + inAppNotification.notifyError(e) } loading.value = false return false diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index b4f1d62f8..9e68292d8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel @@ -54,7 +54,7 @@ class TwitterUserViewModel( lookupService = it.service as LookupService, ) } catch (e: Throwable) { - e.notify(inAppNotification) + inAppNotification.notifyError(e) error.value = e null } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 520f0d3fb..73c2fe259 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user +import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey @@ -28,6 +29,7 @@ import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flattenMerge import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index a7bc2178f..3215b52ff 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository -import com.twidere.twiderex.utils.notify +import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map @@ -67,7 +67,7 @@ class UserViewModel( lookupService = account.service as LookupService, ) }.onFailure { - it.notify(inAppNotification) + inAppNotification.notifyError(it) } refreshing.value = false } @@ -82,7 +82,7 @@ class UserViewModel( loadRelationShip() }.onFailure { loadingRelationship.value = false - it.notify(inAppNotification) + inAppNotification.notifyError(it) } } @@ -96,7 +96,7 @@ class UserViewModel( loadRelationShip() }.onFailure { loadingRelationship.value = false - it.notify(inAppNotification) + inAppNotification.notifyError(it) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt new file mode 100644 index 000000000..2c3599a6f --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -0,0 +1,34 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.job.ComposeData + +actual class ComposeAction { + actual fun commit( + accountKey: MicroBlogKey, + platformType: PlatformType, + data: ComposeData, + ) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt new file mode 100644 index 000000000..943f7c242 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.job.DirectMessageDeleteData +import com.twidere.twiderex.model.job.DirectMessageSendData + +actual class DirectMessageAction { + actual fun send( + platformType: PlatformType, + data: DirectMessageSendData, + ) { + } + + actual fun delete( + data: DirectMessageDeleteData + ) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt new file mode 100644 index 000000000..52a1c01e1 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.job.ComposeData + +actual class DraftAction { + actual fun delete(id: String) { + } + + actual fun save(composeData: ComposeData) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt new file mode 100644 index 000000000..f44285658 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.MicroBlogKey + +actual class MediaAction { + actual fun download( + source: String, + target: String, + accountKey: MicroBlogKey + ) { + } + + actual fun share(source: String, accountKey: MicroBlogKey) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt new file mode 100644 index 000000000..79193d0e5 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.action + +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.ui.UiStatus + +actual class StatusActions : IStatusActions { + actual override fun delete(status: UiStatus, account: AccountDetails) { + } + + actual override fun like(status: UiStatus, account: AccountDetails) { + } + + actual override fun retweet(status: UiStatus, account: AccountDetails) { + } + + actual override fun vote(status: UiStatus, account: AccountDetails, votes: List) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt new file mode 100644 index 000000000..14f5d2120 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +actual class ExifScrambler { + actual fun removeExifData(file: String, compress: Int): String { + TODO("Not yet implemented") + } + + actual fun deleteCacheFile(file: String) { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt new file mode 100644 index 000000000..c1f8b3b9d --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -0,0 +1,42 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import java.io.InputStream +import java.io.OutputStream + +actual class FileResolver { + actual fun getMimeType(file: String): String? { + TODO("Not yet implemented") + } + + actual fun getFileSize(file: String): Long? { + TODO("Not yet implemented") + } + + actual fun openInputStream(file: String): InputStream? { + TODO("Not yet implemented") + } + + actual fun openOutputStream(file: String): OutputStream? { + TODO("Not yet implemented") + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt new file mode 100644 index 000000000..3bce50eed --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import com.twidere.twiderex.model.kmp.Location +import kotlinx.coroutines.flow.SharedFlow + +actual class LocationProvider { + actual val location: SharedFlow + get() = TODO("Not yet implemented") + + actual fun enable() { + } + + actual fun disable() { + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt new file mode 100644 index 000000000..2c2275644 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +actual class RemoteNavigator { + actual fun openDeepLink(deeplink: String, fromBackground: Boolean) { + } + + actual fun shareMedia( + filePath: String, + mimeType: String, + fromBackground: Boolean + ) { + } + + actual fun shareText(content: String, fromBackground: Boolean) { + } +} From d8f0e3c5b9515d5297b22939c24e7553e3bcaf42 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 30 Aug 2021 19:23:01 +0800 Subject: [PATCH 106/615] navigate to status when click media scene pannel --- .../com/twidere/twiderex/scenes/MediaScene.kt | 15 +++------------ app/src/main/res/values/strings.xml | 1 - 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index cb01eaede..77f957776 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -158,6 +158,7 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM var controlVisibility by remember { mutableStateOf(true) } val controlPanelColor = MaterialTheme.colors.surface.copy(alpha = 0.6f) val navController = LocalNavController.current + val navigator = LocalNavigator.current val pagerState = rememberPagerState( initialPage = selectedIndex, pageCount = status.media.size, @@ -208,7 +209,8 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM Box( modifier = Modifier .background(color = controlPanelColor) - .navigationBarsPadding(), + .navigationBarsPadding() + .clickable { navigator.status(status = status) }, ) { StatusMediaInfo(videoControl, status, viewModel, currentMedia) } @@ -298,7 +300,6 @@ private fun StatusMediaInfo( currentMedia: UiMedia ) { val context = LocalContext.current - val navigator = LocalNavigator.current Column( modifier = Modifier .padding(StatusMediaInfoDefaults.ContentPadding), @@ -361,16 +362,6 @@ private fun StatusMediaInfo( text = stringResource(id = R.string.common_controls_actions_share_media), ) } - DropdownMenuItem( - onClick = { - callback.invoke() - navigator.status(status = status) - } - ) { - Text( - text = stringResource(id = R.string.common_controls_actions_go_to_status), - ) - } } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7ffb4bc49..bff316365 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -103,7 +103,6 @@ Cancel Preview Share media - Go to tweet Edit Open in Safari Yes From 744704be655bce94c2ca5c968e94ab9c807cc757 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 31 Aug 2021 16:39:10 +0800 Subject: [PATCH 107/615] migrate notification --- .../twiderex/component/UserComponent.kt | 4 +- .../NotificationChannelInitializer.kt | 9 -- .../twiderex/scenes/settings/MiscScene.kt | 6 +- common/build.gradle.kts | 7 +- .../AndroidNotificationManager.kt | 93 --------------- .../notification/AppNotificationManager.kt | 108 ++++++++++++++++++ .../notification/NotificationChannelSpec.kt | 30 +++++ .../twiderex/worker/NotificationWorker.kt | 4 +- .../drawable-anydpi-v24/ic_notification.xml | 35 ++++++ .../res/drawable-hdpi/ic_notification.png | Bin 0 -> 331 bytes .../res/drawable-mdpi/ic_notification.png | Bin 0 -> 253 bytes .../res/drawable-xhdpi/ic_notification.png | Bin 0 -> 414 bytes .../res/drawable-xxhdpi/ic_notification.png | Bin 0 -> 644 bytes .../twiderex/jobs/common/NotificationJob.kt | 82 +++++-------- .../twiderex/jobs/compose/ComposeJob.kt | 2 - .../jobs/compose/MastodonComposeJob.kt | 3 - .../jobs/compose/TwitterComposeJob.kt | 3 - .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 2 - .../twiderex/jobs/dm/DirectMessageSendJob.kt | 2 - .../jobs/dm/TwitterDirectMessageSendJob.kt | 4 +- .../com/twidere/twiderex/model/ui/UiUser.kt | 2 +- .../notification/AppNotificationManager.kt | 14 +-- .../notification/NotificationChannelSpec.kt | 27 +++-- .../viewmodel/settings/MiscViewModel.kt | 10 +- .../notification/AppNotificationManager.kt | 38 ++++++ 25 files changed, 279 insertions(+), 206 deletions(-) delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/notification/AndroidNotificationManager.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt create mode 100644 common/src/androidMain/res/drawable-anydpi-v24/ic_notification.xml create mode 100644 common/src/androidMain/res/drawable-hdpi/ic_notification.png create mode 100644 common/src/androidMain/res/drawable-mdpi/ic_notification.png create mode 100644 common/src/androidMain/res/drawable-xhdpi/ic_notification.png create mode 100644 common/src/androidMain/res/drawable-xxhdpi/ic_notification.png rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt (60%) create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 86ea31844..b20072f9c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -440,9 +440,7 @@ fun UserInfo( user = user, size = UserInfoDefaults.AvatarSize ) { - if (user.profileImage is String) { - navController.navigate(RootRoute.Media.Raw(user.profileImage.toString())) - } + navController.navigate(RootRoute.Media.Raw(user.profileImage)) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt index b1f5c80c0..1e2e60ccd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.notification import android.content.Context -import android.net.Uri import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationChannelGroupCompat import androidx.core.app.NotificationManagerCompat @@ -120,11 +119,3 @@ class NotificationChannelInitializer : Initializer - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.notification - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import com.twidere.twiderex.R -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import java.util.concurrent.TimeUnit - -actual class AppNotificationManager( - private val context: Context, - private val notificationManagerCompat: NotificationManagerCompat -) { - val scope = MainScope() - actual fun notify(notificationId: Int, appNotification: AppNotification) { - val builder = NotificationCompat.Builder( - context, - appNotification.channelId - ).setSmallIcon(R.drawable.ic_notification) - .setCategory(NotificationCompat.CATEGORY_SOCIAL) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setAutoCancel(true) - .setContentTitle(appNotification.title) - .setOngoing(appNotification.ongoing) - .setSilent(appNotification.silent) - .setProgress( - appNotification.progressMax, - appNotification.progress, - appNotification.progressIndeterminate - ) - appNotification.content?.let { - builder.setContentText(it) - .setStyle(NotificationCompat.BigTextStyle().bigText(it)) - } - appNotification.deepLink?.let { - builder.setContentIntent( - PendingIntent.getActivity( - context, - 0, - Intent(Intent.ACTION_VIEW, Uri.parse(it)), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.FLAG_IMMUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - ) - } - appNotification.largeIcon?.let { - builder.setLargeIcon(it) - } - notificationManagerCompat.notify(notificationId, builder.build()) - } - - actual fun notifyTransient( - notificationId: Int, - appNotification: AppNotification, - duration: Long, - durationTimeUnit: TimeUnit - ) { - notify(notificationId, appNotification) - scope.launch { - delay(durationTimeUnit.toMillis(duration)) - notificationManagerCompat.cancel(notificationId) - } - } -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt new file mode 100644 index 000000000..8d3e12b44 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -0,0 +1,108 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.notification + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.graphics.drawable.toBitmap +import coil.Coil +import coil.request.ImageRequest +import coil.request.SuccessResult +import com.twidere.common.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +actual class AppNotificationManager( + private val context: Context, + private val notificationManagerCompat: NotificationManagerCompat +) { + val scope = CoroutineScope(Dispatchers.IO) + actual fun notify(notificationId: Int, appNotification: AppNotification) { + scope.launch { + val builder = NotificationCompat.Builder( + context, + appNotification.channelId + ).setSmallIcon(R.drawable.ic_notification) + .setCategory(NotificationCompat.CATEGORY_SOCIAL) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setAutoCancel(true) + .setContentTitle(appNotification.title) + .setOngoing(appNotification.ongoing) + .setSilent(appNotification.silent) + .setProgress( + appNotification.progressMax, + appNotification.progress, + appNotification.progressIndeterminate + ) + appNotification.content?.let { + builder.setContentText(it) + .setStyle(NotificationCompat.BigTextStyle().bigText(it)) + } + appNotification.deepLink?.let { + builder.setContentIntent( + PendingIntent.getActivity( + context, + 0, + Intent(Intent.ACTION_VIEW, Uri.parse(it)), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + }, + ) + ) + } + appNotification.largeIcon?.let { + val result = Coil.execute( + ImageRequest.Builder(context) + .data(it) + .build() + ) + if (result is SuccessResult) { + builder.setLargeIcon(result.drawable.toBitmap()) + } + } + notificationManagerCompat.notify(notificationId, builder.build()) + } + } + + @OptIn(ExperimentalTime::class) + actual fun notifyTransient( + notificationId: Int, + appNotification: AppNotification, + duration: Duration + ) { + notify(notificationId, appNotification) + scope.launch { + delay(duration) + notificationManagerCompat.cancel(notificationId) + } + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt new file mode 100644 index 000000000..7efd83fd5 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.notification + +import androidx.core.app.NotificationManagerCompat + +val NotificationChannelSpec.importance + get() = when (this) { + NotificationChannelSpec.BackgroundProgresses -> NotificationManagerCompat.IMPORTANCE_HIGH + NotificationChannelSpec.ContentInteractions -> NotificationManagerCompat.IMPORTANCE_HIGH + NotificationChannelSpec.ContentMessages -> NotificationManagerCompat.IMPORTANCE_HIGH + } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt index cf10c11ba..bbcc01cbb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt @@ -41,7 +41,9 @@ class NotificationWorker @AssistedInject constructor( ) : CoroutineWorker(appContext, params) { override suspend fun doWork(): Result { return try { - notificationJob.execute(notificationPreferences.data.first().enableNotification) + if (notificationPreferences.data.first().enableNotification) { + notificationJob.execute() + } Result.success() } catch (e: Throwable) { e.printStackTrace() diff --git a/common/src/androidMain/res/drawable-anydpi-v24/ic_notification.xml b/common/src/androidMain/res/drawable-anydpi-v24/ic_notification.xml new file mode 100644 index 000000000..5542a066d --- /dev/null +++ b/common/src/androidMain/res/drawable-anydpi-v24/ic_notification.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/common/src/androidMain/res/drawable-hdpi/ic_notification.png b/common/src/androidMain/res/drawable-hdpi/ic_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c4145e1da384b7829a1088e47c2feecc7ed292 GIT binary patch literal 331 zcmV-R0kr;!P)Y&lyNiA+bM`>TN{m1DBUw^I{D z$+_4Zp$RM7rnkbkLDAu>iPX!pyoOv+V=Jd+CA`7(&VjI#{L z36Y#s(9XBPz0@Nil1c^b`WU#CdL~4!Qrhux&`S*ok*ZQV3=5GuQiOvLPiiQ8O!eP$ d9LM+(RJJ2#B^1R3kVZPH4lp`XaYk48e zcN!weUHP~sF9fyK4zzrnN(8;n3^WEjiwJs;DU#p=l2ncedc8Sl3>ai-9??(MU>@tf zDvkTp;Vmc!0Eu|;wObk=JcnyFzz;P1c~pVs0ifgg$k)0weyL7GU3ozBfXH)@2ND3@ zji@gVCEBq7<-j(}=wRV2IH7Yz`zS zM;r+N<|2*;08a+oynvf~6vT z)wSY)wgXjmJdXon$5SA2MqnTQ?L4 z&W&`_aX@VCSRgnx(x&6=wEoxY7_s$J0)db8$IU0(jgx5o#o|~bv9(hJK_2OYwj*sn zPNL(H#MYe>2>K%3PMk!CBZ;j!H4yknU-Z`EB-$TIZ2p`;aENsCv^$d6+_{0E=tyG6 zF9`(2MiM)AX&@*vlGyV(0zq++UhL<&0zpxc(x2%$13@{FKzlVs3h=_=Y eh=_=2S^ogfnpVGRFXXcT0000 - launch { - val activities = try { - repository.activities( - accountKey = account.accountKey, - service = account.service - ) - } catch (e: Throwable) { - // Ignore any exception cause there's no needs ot handle it - emptyList() - } - activities.forEach { status -> - notify(account, status) - } + suspend fun execute() = coroutineScope { + accountRepository.getAccounts() + .filter { + it.preferences.isNotificationEnabled.first() + } + .map { account -> + launch { + val activities = try { + repository.activities( + accountKey = account.accountKey, + service = account.service + ) + } catch (e: Throwable) { + // Ignore any exception cause there's no needs ot handle it + emptyList() } - }.joinAll() - } + activities.forEach { status -> + notify(account, status) + } + } + }.joinAll() } private suspend fun notify(account: AccountDetails, status: UiStatus) { val notificationId = "${account.accountKey}_${status.statusKey}" - val builder = AppNotification - .Builder( - account.accountKey.notificationChannelId( - NotificationChannelSpec.ContentInteractions.id - ) + val builder = AppNotification.Builder( + account.accountKey.notificationChannelId( + NotificationChannelSpec.ContentInteractions.id ) + ) val notificationData = when (status.platformType) { PlatformType.Twitter -> { @@ -107,25 +95,13 @@ class NotificationJob( if (notificationData != null) { builder.setContentTitle(notificationData.title) if (notificationData.htmlContent != null) { - val html = HtmlCompat.fromHtml( - notificationData.htmlContent, - HtmlCompat.FROM_HTML_MODE_COMPACT - ) - builder - .setContentText(html) + builder.setContentText(notificationData.htmlContent) } if (notificationData.deepLink != null) { builder.setDeepLink(notificationData.deepLink) } if (notificationData.profileImage != null) { - val result = Coil.execute( - ImageRequest.Builder(applicationContext) - .data(notificationData.profileImage) - .build() - ) - if (result is SuccessResult) { - builder.setLargeIcon(result.drawable.toBitmap()) - } + builder.setLargeIcon(notificationData.profileImage) } notificationManager.notify(notificationId.hashCode(), builder.build()) } @@ -138,7 +114,7 @@ class NotificationJob( if (status.mastodonExtra == null || actualStatus == null) { return null } - return when (status.mastodonExtra?.type) { + return when (status.mastodonExtra.type) { MastodonStatusType.Status -> null MastodonStatusType.NotificationFollow -> { NotificationData( @@ -205,9 +181,9 @@ class NotificationJob( } } -private data class NotificationData( +data class NotificationData( val title: String, - val profileImage: Any? = null, + val profileImage: String? = null, val htmlContent: String? = null, val deepLink: String? = null, ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 5a352a953..a6e3a0744 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.compose -import android.content.Context import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.R import com.twidere.twiderex.kmp.ExifScrambler @@ -37,7 +36,6 @@ import com.twidere.twiderex.repository.AccountRepository import kotlin.math.roundToInt abstract class ComposeJob( - private val applicationContext: Context, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, private val exifScrambler: ExifScrambler, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index c7fbf1c80..015c2032c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.compose -import android.content.Context import com.twidere.services.mastodon.MastodonService import com.twidere.services.mastodon.model.PostPoll import com.twidere.services.mastodon.model.PostStatus @@ -41,7 +40,6 @@ import java.io.File import java.net.URI class MastodonComposeJob( - context: Context, accountRepository: AccountRepository, notificationManager: AppNotificationManager, exifScrambler: ExifScrambler, @@ -49,7 +47,6 @@ class MastodonComposeJob( private val fileResolver: FileResolver, private val cacheDatabase: CacheDatabase, ) : ComposeJob( - context, accountRepository, notificationManager, exifScrambler, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 00ee18264..8bc1b2b27 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.compose -import android.content.Context import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase @@ -36,7 +35,6 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository class TwitterComposeJob constructor( - context: Context, accountRepository: AccountRepository, notificationManager: AppNotificationManager, exifScrambler: ExifScrambler, @@ -45,7 +43,6 @@ class TwitterComposeJob constructor( private val fileResolver: FileResolver, private val cacheDatabase: CacheDatabase, ) : ComposeJob( - context, accountRepository, notificationManager, exifScrambler, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt index 55206df52..d634086e8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.dm -import android.content.Context import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.R @@ -37,7 +36,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull class DirectMessageFetchJob( - private val applicationContext: Context, private val repository: DirectMessageRepository, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index 23c0a89de..c985dd6ea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.dm -import android.content.Context import android.graphics.BitmapFactory import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.R @@ -41,7 +40,6 @@ import com.twidere.twiderex.repository.AccountRepository import java.net.URI abstract class DirectMessageSendJob( - private val applicationContext: Context, protected val cacheDatabase: CacheDatabase, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt index 2fc578732..e9e1286df 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.jobs.dm -import android.content.Context import com.twidere.services.microblog.LookupService import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.dataprovider.mapper.autolink @@ -35,13 +34,12 @@ import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.repository.AccountRepository class TwitterDirectMessageSendJob( - context: Context, accountRepository: AccountRepository, notificationManager: AppNotificationManager, fileResolver: FileResolver, cacheDatabase: CacheDatabase, ) : DirectMessageSendJob( - context, cacheDatabase, accountRepository, notificationManager, fileResolver + cacheDatabase, accountRepository, notificationManager, fileResolver ) { override suspend fun sendMessage( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt index 4ad5fbb3f..5b1003b83 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt @@ -34,7 +34,7 @@ data class UiUser( val acct: MicroBlogKey, val name: String, val screenName: String, - val profileImage: Any, + val profileImage: String, val profileBackgroundImage: String?, val metrics: UserMetrics, val rawDesc: String, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index ddd997a06..f9c00f13d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -20,17 +20,17 @@ */ package com.twidere.twiderex.notification -import android.graphics.Bitmap -import java.util.concurrent.TimeUnit +import kotlin.time.Duration +import kotlin.time.ExperimentalTime expect class AppNotificationManager { fun notify(notificationId: Int, appNotification: AppNotification) + @OptIn(ExperimentalTime::class) fun notifyTransient( notificationId: Int, appNotification: AppNotification, - duration: Long = 5, - durationTimeUnit: TimeUnit = TimeUnit.SECONDS + duration: Duration = Duration.Companion.seconds(5), ) } @@ -38,7 +38,7 @@ class AppNotification( val channelId: String, val title: CharSequence? = null, val content: CharSequence? = null, - val largeIcon: Bitmap? = null, + val largeIcon: String? = null, val deepLink: String? = null, val ongoing: Boolean = false, val progress: Int = 0, @@ -49,7 +49,7 @@ class AppNotification( class Builder(private var channelId: String) { private var title: CharSequence? = null private var content: CharSequence? = null - private var largeIcon: Bitmap? = null + private var largeIcon: String? = null private var deepLink: String? = null private var ongoing: Boolean = false private var progress: Int = 0 @@ -65,7 +65,7 @@ class AppNotification( this.content = content } - fun setLargeIcon(largeIcon: Bitmap?) = this.apply { + fun setLargeIcon(largeIcon: String?) = this.apply { this.largeIcon = largeIcon } diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt similarity index 60% rename from android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index bfb822050..61e0eead5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -20,15 +20,11 @@ */ package com.twidere.twiderex.notification -import androidx.annotation.StringRes -import androidx.core.app.NotificationManagerCompat -import com.twidere.twiderex.R +import android.net.Uri +import com.twidere.twiderex.model.MicroBlogKey enum class NotificationChannelSpec( val id: String, - @StringRes val nameRes: Int, - @StringRes val descriptionRes: Int = 0, - val importance: Int, val showBadge: Boolean = false, val grouped: Boolean = false ) { @@ -38,22 +34,25 @@ enum class NotificationChannelSpec( */ BackgroundProgresses( "background_progresses", - R.string.common_notification_channel_background_progresses_name, - importance = NotificationManagerCompat.IMPORTANCE_HIGH ), ContentInteractions( - "content_interactions", R.string.common_notification_channel_content_interactions_name, - descriptionRes = R.string.common_notification_channel_content_interactions_description, - importance = NotificationManagerCompat.IMPORTANCE_HIGH, showBadge = true, grouped = true + "content_interactions", + showBadge = true, + grouped = true ), ContentMessages( "content_messages", - R.string.common_notification_channel_content_messages_name, - descriptionRes = R.string.common_notification_channel_content_messages_description, - importance = NotificationManagerCompat.IMPORTANCE_HIGH, showBadge = true, grouped = true ) } + +fun MicroBlogKey.notificationChannelId(id: String): String { + return "${id}_${Uri.encode(toString())}" +} + +fun MicroBlogKey.notificationChannelGroupId(): String { + return Uri.encode(toString()) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index 9aca95a12..a8d81ee28 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import com.twidere.twiderex.R import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.MutableStateFlow @@ -110,13 +109,8 @@ class MiscViewModel( } } - fun setProxyPort(value: String) { - try { - proxyPort.value = value.toInt() - } catch (e: NumberFormatException) { - inAppNotification.show(R.string.scene_settings_misc_proxy_port_error) - return - } + fun setProxyPort(value: Int) { + proxyPort.value = value viewModelScope.launch { miscPreferences.updateData { it.copy(proxyPort = value.toInt()) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt new file mode 100644 index 000000000..8733b37e4 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.notification + +import kotlin.time.Duration + +actual class AppNotificationManager { + actual fun notify( + notificationId: Int, + appNotification: AppNotification + ) { + } + + actual fun notifyTransient( + notificationId: Int, + appNotification: AppNotification, + duration: Duration + ) { + } +} From 470dea4877b505d33d796920549c9887f26da428 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 31 Aug 2021 16:52:40 +0800 Subject: [PATCH 108/615] remove hilt from worker --- .../initializer}/DirectMessageInitializer.kt | 3 ++- .../NotificationChannelInitializer.kt | 6 +++++- .../NotificationInitializer.kt | 2 +- .../twidere/twiderex/worker/DownloadMediaWorker.kt | 10 +++------- .../twidere/twiderex/worker/NotificationWorker.kt | 10 +++------- .../com/twidere/twiderex/worker/ShareMediaWorker.kt | 12 +++--------- .../twiderex/worker/compose/MastodonComposeWorker.kt | 10 +++------- .../twiderex/worker/compose/TwitterComposeWorker.kt | 10 +++------- .../twiderex/worker/database/DeleteDbStatusWorker.kt | 10 +++------- .../twiderex/worker/dm/DirectMessageDeleteWorker.kt | 10 +++------- .../twiderex/worker/dm/DirectMessageFetchWorker.kt | 10 +++------- .../worker/dm/TwitterDirectMessageSendWorker.kt | 10 +++------- .../twiderex/worker/draft/RemoveDraftWorker.kt | 10 +++------- .../twidere/twiderex/worker/draft/SaveDraftWorker.kt | 10 +++------- .../twiderex/worker/status/DeleteStatusWorker.kt | 10 +++------- .../com/twidere/twiderex/worker/status/LikeWorker.kt | 10 +++------- .../twiderex/worker/status/MastodonVoteWorker.kt | 10 +++------- .../twidere/twiderex/worker/status/RetweetWorker.kt | 10 +++------- .../twidere/twiderex/worker/status/UnLikeWorker.kt | 10 +++------- .../twiderex/worker/status/UnRetweetWorker.kt | 10 +++------- .../twiderex/worker/status/UpdateStatusWorker.kt | 10 +++------- 21 files changed, 62 insertions(+), 131 deletions(-) rename {common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm => android/src/main/kotlin/com/twidere/twiderex/initializer}/DirectMessageInitializer.kt (94%) rename android/src/main/kotlin/com/twidere/twiderex/{notification => initializer}/NotificationChannelInitializer.kt (94%) rename android/src/main/kotlin/com/twidere/twiderex/{notification => initializer}/NotificationInitializer.kt (97%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt similarity index 94% rename from common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt index 8c0561bb0..94e809e5c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageInitializer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt @@ -18,13 +18,14 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.worker.dm +package com.twidere.twiderex.initializer import android.content.Context import androidx.startup.Initializer import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.WorkManager import com.twidere.twiderex.di.InitializerEntryPoint +import com.twidere.twiderex.worker.dm.DirectMessageFetchWorker import javax.inject.Inject class DirectMessageInitializerHolder diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt index 1e2e60ccd..2d5f38081 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelInitializer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.notification +package com.twidere.twiderex.initializer import android.content.Context import androidx.core.app.NotificationChannelCompat @@ -27,6 +27,10 @@ import androidx.core.app.NotificationManagerCompat import androidx.startup.Initializer import com.twidere.twiderex.di.InitializerEntryPoint import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.notification.NotificationChannelSpec +import com.twidere.twiderex.notification.importance +import com.twidere.twiderex.notification.notificationChannelGroupId +import com.twidere.twiderex.notification.notificationChannelId import com.twidere.twiderex.repository.AccountRepository import javax.inject.Inject diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt b/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt rename to android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt index 22777d58d..631e9a2cb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationInitializer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.notification +package com.twidere.twiderex.initializer import android.content.Context import androidx.startup.Initializer diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt index e49de1745..4afa2b438 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt @@ -22,20 +22,16 @@ package com.twidere.twiderex.worker import android.content.Context import android.net.Uri -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.common.DownloadMediaJob import com.twidere.twiderex.model.MicroBlogKey -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class DownloadMediaWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class DownloadMediaWorker( + context: Context, + workerParams: WorkerParameters, private val downloadMediaJob: DownloadMediaJob, ) : CoroutineWorker(context, workerParams) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt index bbcc01cbb..ab6e02ed3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt @@ -22,19 +22,15 @@ package com.twidere.twiderex.worker import android.content.Context import androidx.datastore.core.DataStore -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.common.NotificationJob import com.twidere.twiderex.preferences.model.NotificationPreferences -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.first -@HiltWorker -class NotificationWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class NotificationWorker( + appContext: Context, + params: WorkerParameters, private val notificationPreferences: DataStore, private val notificationJob: NotificationJob diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt index ddfea7fde..1a80e4645 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt @@ -20,24 +20,18 @@ */ package com.twidere.twiderex.worker -import android.content.ContentResolver import android.content.Context import android.net.Uri -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.common.ShareMediaJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class ShareMediaWorker @AssistedInject constructor( - @Assisted private val context: Context, - @Assisted workerParams: WorkerParameters, +class ShareMediaWorker( + context: Context, + workerParams: WorkerParameters, private val shareMediaJob: ShareMediaJob, - private val contentResolver: ContentResolver, ) : CoroutineWorker(context, workerParams) { companion object { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt index dc03ba136..dbd70c140 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.compose import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters @@ -30,13 +29,10 @@ import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.compose.MastodonComposeJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.ComposeData -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class MastodonComposeWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class MastodonComposeWorker( + context: Context, + workerParams: WorkerParameters, mastodonComposeJob: MastodonComposeJob ) : ComposeWorker(context, workerParams, mastodonComposeJob) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt index a1f94bda2..ab0a66355 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.compose import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters @@ -30,13 +29,10 @@ import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.compose.TwitterComposeJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.ComposeData -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class TwitterComposeWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class TwitterComposeWorker( + context: Context, + workerParams: WorkerParameters, twitterComposeJob: TwitterComposeJob, ) : ComposeWorker( context, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt index 062d7f450..8867f663c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt @@ -21,20 +21,16 @@ package com.twidere.twiderex.worker.database import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import androidx.work.workDataOf import com.twidere.twiderex.jobs.database.DeleteDbStatusJob import com.twidere.twiderex.model.MicroBlogKey -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class DeleteDbStatusWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class DeleteDbStatusWorker( + appContext: Context, + params: WorkerParameters, private val deleteDbStatusJob: DeleteDbStatusJob, ) : CoroutineWorker(appContext, params) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt index f70c7cabe..2e0f71301 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.dm import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters @@ -30,13 +29,10 @@ import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.dm.DirectMessageDeleteJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.DirectMessageDeleteData -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class DirectMessageDeleteWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class DirectMessageDeleteWorker( + context: Context, + workerParams: WorkerParameters, private val deleteJob: DirectMessageDeleteJob ) : CoroutineWorker( context, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt index fc2205c9a..d1b2fdb90 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt @@ -21,19 +21,15 @@ package com.twidere.twiderex.worker.dm import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.dm.DirectMessageFetchJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import java.util.concurrent.TimeUnit -@HiltWorker -class DirectMessageFetchWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class DirectMessageFetchWorker( + context: Context, + workerParams: WorkerParameters, private val directMessageFetchJob: DirectMessageFetchJob ) : CoroutineWorker( context, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt index c23656cce..a1b6bdad1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt @@ -21,20 +21,16 @@ package com.twidere.twiderex.worker.dm import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.dm.TwitterDirectMessageSendJob import com.twidere.twiderex.model.job.DirectMessageSendData -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class TwitterDirectMessageSendWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class TwitterDirectMessageSendWorker( + context: Context, + workerParams: WorkerParameters, twitterDirectMessageSendJob: TwitterDirectMessageSendJob ) : DirectMessageSendWorker( context, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt index ac1933fdf..1c79e5a27 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt @@ -21,19 +21,15 @@ package com.twidere.twiderex.worker.draft import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import androidx.work.workDataOf import com.twidere.twiderex.jobs.draft.RemoveDraftJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class RemoveDraftWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class RemoveDraftWorker( + appContext: Context, + params: WorkerParameters, private val removeDraftJob: RemoveDraftJob ) : CoroutineWorker(appContext, params) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt index 84851b876..3c9ae823d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.draft import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters @@ -29,13 +28,10 @@ import com.twidere.twiderex.db.transform.toComposeData import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.jobs.draft.SaveDraftJob import com.twidere.twiderex.model.job.ComposeData -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class SaveDraftWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, +class SaveDraftWorker( + context: Context, + workerParams: WorkerParameters, private val saveDraftJob: SaveDraftJob ) : CoroutineWorker(context, workerParams) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt index 065c4ac1f..f0595063c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters @@ -29,13 +28,10 @@ import androidx.work.workDataOf import com.twidere.twiderex.jobs.status.DeleteStatusJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiStatus -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class DeleteStatusWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class DeleteStatusWorker( + appContext: Context, + params: WorkerParameters, private val deleteStatusJob: DeleteStatusJob ) : CoroutineWorker(appContext, params) { companion object { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt index 4e6323eb8..69e9be899 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt @@ -21,15 +21,11 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.status.LikeStatusJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class LikeWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class LikeWorker( + appContext: Context, + params: WorkerParameters, likeStatusJob: LikeStatusJob, ) : StatusWorker(appContext, params, likeStatusJob) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt index ae5351043..14b9477ff 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt @@ -21,20 +21,16 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import androidx.work.workDataOf import com.twidere.twiderex.jobs.status.MastodonVoteJob import com.twidere.twiderex.model.MicroBlogKey -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class MastodonVoteWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class MastodonVoteWorker( + appContext: Context, + params: WorkerParameters, private val mastodonVoteJob: MastodonVoteJob ) : CoroutineWorker(appContext, params) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt index 68672a115..9bae59166 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt @@ -21,15 +21,11 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.status.RetweetStatusJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class RetweetWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class RetweetWorker( + appContext: Context, + params: WorkerParameters, retweetStatusJob: RetweetStatusJob, ) : StatusWorker(appContext, params, retweetStatusJob) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt index bf612f76a..3d2b54726 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt @@ -21,15 +21,11 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.status.UnlikeStatusJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class UnLikeWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class UnLikeWorker( + appContext: Context, + params: WorkerParameters, unlikeStatusJob: UnlikeStatusJob, ) : StatusWorker(appContext, params, unlikeStatusJob) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt index 0ac90ff33..92cfa5dfe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt @@ -21,15 +21,11 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.WorkerParameters import com.twidere.twiderex.jobs.status.UnRetweetStatusJob -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class UnRetweetWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class UnRetweetWorker( + appContext: Context, + params: WorkerParameters, unRetweetStatusJob: UnRetweetStatusJob ) : StatusWorker(appContext, params, unRetweetStatusJob) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index e1148efe4..23c4014f4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker.status import android.content.Context -import androidx.hilt.work.HiltWorker import androidx.work.CoroutineWorker import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OverwritingInputMerger @@ -34,13 +33,10 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult import com.twidere.twiderex.repository.ReactionRepository import com.twidere.twiderex.repository.StatusRepository -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -@HiltWorker -class UpdateStatusWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted params: WorkerParameters, +class UpdateStatusWorker( + appContext: Context, + params: WorkerParameters, private val repository: ReactionRepository, private val statusRepository: StatusRepository, ) : CoroutineWorker(appContext, params) { From 10bbf265fe0bba9db1b080933911145e726d990b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 31 Aug 2021 17:00:07 +0800 Subject: [PATCH 109/615] add user list repository --- .../twiderex/repository/UserListRepository.kt | 61 +++++++++++++++++++ .../viewmodel/user/FollowersViewModel.kt | 35 ++++++----- .../viewmodel/user/FollowingViewModel.kt | 35 ++++++----- 3 files changed, 95 insertions(+), 36 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt new file mode 100644 index 000000000..d71bab108 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt @@ -0,0 +1,61 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import com.twidere.services.microblog.RelationshipService +import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.paging.source.FollowersPagingSource +import com.twidere.twiderex.paging.source.FollowingPagingSource + +class UserListRepository { + fun following( + userKey: MicroBlogKey, + service: RelationshipService, + ) = Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + FollowingPagingSource( + userKey = userKey, + service = service, + ) + }.flow + + fun followers( + userKey: MicroBlogKey, + service: RelationshipService + ) = Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + FollowersPagingSource( + userKey = userKey, + service = service, + ) + }.flow +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index d36fd816b..76529629a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -20,31 +20,30 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.Pager -import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.paging.source.FollowersPagingSource +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.UserListRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( - private val account: AccountDetails, - private val userKey: MicroBlogKey + private val accountRepository: AccountRepository, + private val repository: UserListRepository, + private val userKey: MicroBlogKey, ) : UserListViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + override val source by lazy { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - FollowersPagingSource( - userKey = userKey, - account.service as RelationshipService - ) - }.flow.cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + repository.followers(userKey, account.service as RelationshipService) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index f4f030a51..d7a48b747 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -20,31 +20,30 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.Pager -import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.paging.source.FollowingPagingSource +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.UserListRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( - private val account: AccountDetails, - private val userKey: MicroBlogKey + private val accountRepository: AccountRepository, + private val repository: UserListRepository, + private val userKey: MicroBlogKey, ) : UserListViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + override val source by lazy { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - FollowingPagingSource( - userKey = userKey, - account.service as RelationshipService - ) - }.flow.cachedIn(viewModelScope) + account.flatMapLatest { + it?.let { account -> + repository.following(userKey, account.service as RelationshipService) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } From effe970cab22f9ae283bd527c1983d2eea3b5aeb Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 31 Aug 2021 17:06:20 +0800 Subject: [PATCH 110/615] migrate TwitterSearchMediaViewModel --- .../twiderex/repository/SearchRepository.kt | 20 ++++++++++- .../search/TwitterSearchMediaViewModel.kt | 36 +++++++++---------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt index b1d28e121..79a647a39 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt @@ -20,17 +20,35 @@ */ package com.twidere.twiderex.repository +import androidx.paging.flatMap +import androidx.paging.map +import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.paging.mediator.paging.pager +import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator +import kotlinx.coroutines.flow.map class SearchRepository( - private val database: AppDatabase + private val database: AppDatabase, + private val cacheDatabase: CacheDatabase, ) { fun searchHistory(accountKey: MicroBlogKey) = database.searchDao().getAllHistory(accountKey) fun savedSearch(accountKey: MicroBlogKey) = database.searchDao().getAllSaved(accountKey) + fun media(keyword: String, accountKey: MicroBlogKey, service: SearchService) = + SearchMediaMediator(keyword, cacheDatabase, accountKey, service) + .pager() + .flow.map { it.map { it.status } } + .map { + it.flatMap { + it.media.map { media -> media to it } + } + } + suspend fun addOrUpgrade( content: String, accountKey: MicroBlogKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index fb5f2cd54..a2978be93 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -21,33 +21,29 @@ package com.twidere.twiderex.viewmodel.twitter.search import androidx.paging.cachedIn -import androidx.paging.flatMap -import androidx.paging.map -import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.paging.mediator.paging.pager -import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator -import kotlinx.coroutines.flow.map +import com.twidere.services.microblog.SearchService +import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.SearchRepository +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterSearchMediaViewModel( - val database: CacheDatabase, - private val account: AccountDetails, + private val repository: SearchRepository, + private val accountRepository: AccountRepository, keyword: String, ) : ViewModel() { - - private val service by lazy { - account.service as TwitterService + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) } + val source by lazy { - SearchMediaMediator(keyword, database, account.accountKey, service).pager() - .flow.map { it.map { it.status } }.cachedIn(viewModelScope) - .map { - it.flatMap { - it.media.map { media -> media to it } - } - } + account.flatMapLatest { + it?.let { + repository.media(keyword, it.accountKey, it.service as SearchService) + } ?: emptyFlow() + }.cachedIn(viewModelScope) } } From 676544bb56b13d7da593cc2f41dfbf7efc9f796d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 31 Aug 2021 17:57:53 +0800 Subject: [PATCH 111/615] fix initializer inject --- .../kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt b/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt index af2b2e404..4fdcd4e96 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt @@ -22,9 +22,9 @@ package com.twidere.twiderex.di import android.content.Context import com.twidere.twiderex.http.TwidereServiceInitializer -import com.twidere.twiderex.notification.NotificationChannelInitializer -import com.twidere.twiderex.notification.NotificationInitializer -import com.twidere.twiderex.worker.dm.DirectMessageInitializer +import com.twidere.twiderex.initializer.DirectMessageInitializer +import com.twidere.twiderex.initializer.NotificationChannelInitializer +import com.twidere.twiderex.initializer.NotificationInitializer import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.EntryPointAccessors From 1b4ffd05f64b48d5cbc2b781fdab8e01701e3933 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 1 Sep 2021 15:13:03 +0800 Subject: [PATCH 112/615] add user block api for twitter and mastodon --- .../services/mastodon/MastodonService.kt | 22 +++++++++--- .../mastodon/api/FriendshipResources.kt | 10 ++++++ .../services/microblog/RelationshipService.kt | 2 ++ .../services/microblog/model/IRelationship.kt | 4 +++ .../services/twitter/TwitterService.kt | 19 ++++++++++ .../services/twitter/api/UsersResources.kt | 19 ++++++++++ .../twidere/services/twitter/model/BlockV2.kt | 36 +++++++++++++++++++ 7 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt index 46109d5fe..816293179 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt @@ -34,6 +34,7 @@ import com.twidere.services.mastodon.model.PostAccounts import com.twidere.services.mastodon.model.PostList import com.twidere.services.mastodon.model.PostStatus import com.twidere.services.mastodon.model.PostVote +import com.twidere.services.mastodon.model.RelationshipResponse import com.twidere.services.mastodon.model.SearchType import com.twidere.services.mastodon.model.UploadResponse import com.twidere.services.mastodon.model.exceptions.MastodonException @@ -162,12 +163,10 @@ class MastodonService( } override suspend fun showRelationship(target_id: String): IRelationship { - val response = resources.showFriendships(listOf(target_id)).firstOrNull() + return resources.showFriendships(listOf(target_id)) + .firstOrNull() + ?.toIRelationShip() ?: throw MastodonException("can not fetch relationship") - return Relationship( - followedBy = response.following ?: false, - following = response.followedBy ?: false, - ) } override suspend fun followers(user_id: String, nextPage: String?) = resources.followers( @@ -184,6 +183,12 @@ class MastodonService( MastodonPaging.from(it) } + override suspend fun blocking(id: String) = resources.block(id = id) + .toIRelationShip() + + override suspend fun unblock(id: String) = resources.unblock(id = id) + .toIRelationShip() + override suspend fun follow(user_id: String) { resources.follow(user_id) } @@ -418,4 +423,11 @@ class MastodonService( remote = false, ) } + + private fun RelationshipResponse.toIRelationShip() = Relationship( + followedBy = following ?: false, + following = followedBy ?: false, + blocking = blocking ?: false, + blockedBy = blockedBy ?: false + ) } diff --git a/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt index fba87e74c..59a7c09d6 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt @@ -40,4 +40,14 @@ interface FriendshipResources { @GET("/api/v1/accounts/relationships") suspend fun showFriendships(@Query("id[]") id: List): List + + @POST("/api/v1/accounts/{id}/block") + suspend fun block( + @Path(value = "id") id: String, + ): RelationshipResponse + + @POST("/api/v1/accounts/{id}/unblock") + suspend fun unblock( + @Path(value = "id") id: String, + ): RelationshipResponse } diff --git a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt index 244dc3b02..beabba6b2 100644 --- a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt +++ b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt @@ -29,4 +29,6 @@ interface RelationshipService { suspend fun unfollow(user_id: String) suspend fun followers(user_id: String, nextPage: String? = null): List suspend fun following(user_id: String, nextPage: String? = null): List + suspend fun blocking(id: String): IRelationship + suspend fun unblock(id: String): IRelationship } diff --git a/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt b/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt index 719820b8c..fc3bb1e70 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt @@ -23,9 +23,13 @@ package com.twidere.services.microblog.model interface IRelationship { val followedBy: Boolean val following: Boolean + val blocking: Boolean + val blockedBy: Boolean } data class Relationship( override val followedBy: Boolean, override val following: Boolean, + override val blocking: Boolean, + override val blockedBy: Boolean ) : IRelationship diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 31aaab8b3..180025b90 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -42,6 +42,7 @@ import com.twidere.services.microblog.model.Relationship import com.twidere.services.twitter.api.TwitterResources import com.twidere.services.twitter.api.UploadResources import com.twidere.services.twitter.model.Attachment +import com.twidere.services.twitter.model.BlockV2Request import com.twidere.services.twitter.model.DirectMessageEvent import com.twidere.services.twitter.model.DirectMessageEventObject import com.twidere.services.twitter.model.MessageCreate @@ -368,6 +369,8 @@ class TwitterService( return Relationship( followedBy = response.relationship?.target?.followedBy ?: false, following = response.relationship?.target?.following ?: false, + blocking = response.relationship?.source?.blocking ?: false, + blockedBy = response.relationship?.source?.blockedBy ?: false ) } @@ -494,6 +497,22 @@ class TwitterService( TwitterPaging(it.data ?: emptyList(), it.meta?.nextToken) } + override suspend fun blocking( + id: String + ) = resources.block( + sourceId = accountId, + target = BlockV2Request(targetUserId = id) + ).run { + showRelationship(target_id = id) + } + + override suspend fun unblock( + id: String + ) = resources.unblock(sourceId = accountId, targetId = id) + .run { + showRelationship(target_id = id) + } + suspend fun verifyCredentials(): User? { return resources.verifyCredentials() } diff --git a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt index 57a2188c8..28e299ee8 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt @@ -20,10 +20,17 @@ */ package com.twidere.services.twitter.api +import com.twidere.services.twitter.model.BlockV2 +import com.twidere.services.twitter.model.BlockV2Request import com.twidere.services.twitter.model.ProfileBanner import com.twidere.services.twitter.model.RelationshipResponse +import com.twidere.services.twitter.model.TwitterResponseV2 import com.twidere.services.twitter.model.User +import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Path import retrofit2.http.Query interface UsersResources { @@ -35,4 +42,16 @@ interface UsersResources { @GET("/1.1/friendships/show.json") suspend fun showFriendships(@Query("target_id") target_id: String): RelationshipResponse + + @POST("/2/users/{sourceId}/blocking") + suspend fun block( + @Path(value = "id") sourceId: String, + @Body target: BlockV2Request + ): TwitterResponseV2 + + @DELETE("/2/users/{sourceId}/blocking/{targetId}") + suspend fun unblock( + @Path(value = "sourceId") sourceId: String, + @Path(value = "targetId") targetId: String, + ): TwitterResponseV2 } diff --git a/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt b/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt new file mode 100644 index 000000000..c49681889 --- /dev/null +++ b/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.twitter.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BlockV2( + @SerialName("blocking") + val blocking: Boolean? = null +) + +@Serializable +data class BlockV2Request( + @SerialName("target_user_id") + val targetUserId: String +) From 749312bac0e8708c6e35bf1810b3d7b24a760192 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 1 Sep 2021 16:52:54 +0800 Subject: [PATCH 113/615] add block action to UserScene --- .../twiderex/component/UserComponent.kt | 113 ++++++++++-------- .../twidere/twiderex/scenes/user/UserScene.kt | 110 ++++++++++++++++- .../twiderex/viewmodel/user/UserViewModel.kt | 26 ++++ .../services/mastodon/MastodonService.kt | 2 +- .../services/microblog/RelationshipService.kt | 2 +- .../services/twitter/TwitterService.kt | 2 +- .../services/twitter/api/UsersResources.kt | 2 +- 7 files changed, 203 insertions(+), 54 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 74f768fc4..bbdec25d9 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -632,58 +632,73 @@ private fun UserRelationship(viewModel: UserViewModel) { val relationship by viewModel.relationship.observeAsState(initial = null) val loadingRelationship by viewModel.loadingRelationship.observeAsState(initial = false) val shape = RoundedCornerShape(percent = 50) - relationship?.takeIf { !loadingRelationship }?.let { relationshipResult -> - Surface( - modifier = Modifier - .clip(RoundedCornerShape(percent = 50)) - .let { - if (relationshipResult.followedBy) { - it - } else { - it.border( - 1.dp, - MaterialTheme.colors.primary, - shape = shape, - ) + val blockingBackgroundColor = Color(0xFFFF2D55) + relationship?.takeIf { !loadingRelationship } + ?.takeIf { !it.blockedBy } + ?.let { relationshipResult -> + Surface( + modifier = Modifier + .clip(RoundedCornerShape(percent = 50)) + .let { + if (relationshipResult.followedBy) { + it + } else { + it.border( + 1.dp, + if (relationshipResult.blocking) blockingBackgroundColor else MaterialTheme.colors.primary, + shape = shape, + ) + } + } + .clip(shape), + contentColor = when { + relationshipResult.blocking -> MaterialTheme.colors.onPrimary + relationshipResult.followedBy -> contentColorFor(backgroundColor = MaterialTheme.colors.primary) + else -> MaterialTheme.colors.primary + }, + color = when { + relationshipResult.blocking -> blockingBackgroundColor + relationshipResult.followedBy -> MaterialTheme.colors.primary + else -> MaterialTheme.colors.background + }, + onClick = { + when { + relationshipResult.blocking -> { + viewModel.unblock() + } + relationshipResult.followedBy -> { + viewModel.unfollow() + } + else -> { + viewModel.follow() + } } } - .clip(shape), - contentColor = if (relationshipResult.followedBy) { - contentColorFor(backgroundColor = MaterialTheme.colors.primary) - } else { - MaterialTheme.colors.primary - }, - color = if (relationshipResult.followedBy) { - MaterialTheme.colors.primary - } else { - MaterialTheme.colors.background - }, - onClick = { - if (relationshipResult.followedBy) { - viewModel.unfollow() - } else { - viewModel.follow() - } + ) { + Text( + modifier = Modifier + .padding(ButtonDefaults.ContentPadding), + text = when { + relationshipResult.blocking -> { + "Blocked" + } + relationshipResult.followedBy -> { + stringResource(id = R.string.common_controls_friendship_actions_unfollow) + } + else -> { + stringResource(id = R.string.common_controls_friendship_actions_follow) + } + }, + ) } - ) { - Text( - modifier = Modifier - .padding(ButtonDefaults.ContentPadding), - text = if (relationshipResult.followedBy) { - stringResource(id = R.string.common_controls_friendship_actions_unfollow) - } else { - stringResource(id = R.string.common_controls_friendship_actions_follow) - }, - ) - } - Spacer(modifier = Modifier.height(UserRelationshipDefaults.FollowingSpacing)) - if (relationshipResult.following) { - Text( - text = stringResource(id = R.string.common_controls_friendship_follows_you), - style = MaterialTheme.typography.caption, - ) - } - } ?: run { + Spacer(modifier = Modifier.height(UserRelationshipDefaults.FollowingSpacing)) + if (relationshipResult.following) { + Text( + text = stringResource(id = R.string.common_controls_friendship_follows_you), + style = MaterialTheme.typography.caption, + ) + } + } ?: run { CircularProgressIndicator() } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index 5ccdf1357..dcc413f95 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -20,11 +20,22 @@ */ package com.twidere.twiderex.scenes.user +import androidx.compose.foundation.layout.Box +import androidx.compose.material.AlertDialog +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreHoriz import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -65,6 +76,10 @@ fun UserScene( } val user by viewModel.user.observeAsState(initial = null) val navController = LocalNavController.current + var expanded by remember { mutableStateOf(false) } + var showBlockAlert by remember { mutableStateOf(false) } + val relationship by viewModel.relationship.observeAsState(initial = null) + val loadingRelationship by viewModel.loadingRelationship.observeAsState(initial = false) TwidereScene { InAppNotificationScaffold( // TODO: Show top bar with actions @@ -101,6 +116,48 @@ fun UserScene( } } } + Box { + if (userKey != account.accountKey) { + IconButton( + onClick = { + expanded = true + } + ) { + Icon( + imageVector = Icons.Default.MoreHoriz, + contentDescription = stringResource( + id = R.string.accessibility_common_more + ), + tint = MaterialTheme.colors.onSurface + ) + } + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + relationship.takeIf { !loadingRelationship } + ?.blocking?.let { blocking -> + DropdownMenuItem( + onClick = { + if (blocking) + viewModel.unblock() + else + showBlockAlert = true + expanded = false + } + ) { + Text( + text = stringResource( + id = if (blocking) R.string.common_controls_friendship_actions_unblock + else R.string.common_controls_friendship_actions_block + ) + ) + } + } + } + } }, elevation = 0.dp, title = { @@ -111,7 +168,58 @@ fun UserScene( ) } ) { - UserComponent(userKey) + Box { + UserComponent(userKey) + if (showBlockAlert) { + user?.let { + BlockAlert( + screenName = it.getDisplayScreenName(it.userKey.host), + onDismissRequest = { showBlockAlert = false }, + onConfirm = { + viewModel.block() + } + ) + } + } + } } } } + +@Composable +fun BlockAlert( + screenName: String, + onDismissRequest: () -> Unit, + onConfirm: () -> Unit +) { + AlertDialog( + onDismissRequest = { + onDismissRequest.invoke() + }, + title = { + Text( + text = "Block $screenName?", + style = MaterialTheme.typography.subtitle1 + ) + }, + dismissButton = { + TextButton( + onClick = { + onDismissRequest.invoke() + } + ) { + Text(text = stringResource(id = R.string.common_controls_actions_cancel)) + } + }, + confirmButton = { + TextButton( + onClick = { + onConfirm() + onDismissRequest.invoke() + } + ) { + Text(text = stringResource(id = R.string.common_controls_actions_yes)) + } + }, + ) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 5ef62593b..566a96b28 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -98,6 +98,32 @@ class UserViewModel @AssistedInject constructor( } } + fun block() = viewModelScope.launch { + loadingRelationship.value = true + runCatching { + relationshipService.block(id = userKey.id) + }.onSuccess { + relationship.value = it + loadingRelationship.value = false + }.onFailure { + it.notify(inAppNotification) + loadingRelationship.value = false + } + } + + fun unblock() = viewModelScope.launch { + loadingRelationship.value = true + runCatching { + relationshipService.unblock(id = userKey.id) + }.onSuccess { + relationship.value = it + loadingRelationship.value = false + }.onFailure { + it.notify(inAppNotification) + loadingRelationship.value = false + } + } + private fun loadRelationShip() = viewModelScope.launch { loadingRelationship.value = true try { diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt index 816293179..a79f854e3 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt @@ -183,7 +183,7 @@ class MastodonService( MastodonPaging.from(it) } - override suspend fun blocking(id: String) = resources.block(id = id) + override suspend fun block(id: String) = resources.block(id = id) .toIRelationShip() override suspend fun unblock(id: String) = resources.unblock(id = id) diff --git a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt index beabba6b2..b3a2e5500 100644 --- a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt +++ b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt @@ -29,6 +29,6 @@ interface RelationshipService { suspend fun unfollow(user_id: String) suspend fun followers(user_id: String, nextPage: String? = null): List suspend fun following(user_id: String, nextPage: String? = null): List - suspend fun blocking(id: String): IRelationship + suspend fun block(id: String): IRelationship suspend fun unblock(id: String): IRelationship } diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 180025b90..75931474c 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -497,7 +497,7 @@ class TwitterService( TwitterPaging(it.data ?: emptyList(), it.meta?.nextToken) } - override suspend fun blocking( + override suspend fun block( id: String ) = resources.block( sourceId = accountId, diff --git a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt index 28e299ee8..eb69ac197 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt @@ -45,7 +45,7 @@ interface UsersResources { @POST("/2/users/{sourceId}/blocking") suspend fun block( - @Path(value = "id") sourceId: String, + @Path(value = "sourceId") sourceId: String, @Body target: BlockV2Request ): TwitterResponseV2 From 38bf048d3e3dd1a89e7d5d6f38c4ae75f87e70e3 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 1 Sep 2021 17:20:53 +0800 Subject: [PATCH 114/615] add permission denied Ui to UserScene --- .../twiderex/component/UserComponent.kt | 41 ++++++++++++++++++- .../twidere/twiderex/scenes/user/UserScene.kt | 3 +- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index bbdec25d9..32145f137 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -211,7 +211,9 @@ fun UserComponent( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter, ) { - tabs[page].compose.invoke() + UserTimeline(viewModel = viewModel) { + tabs[page].compose.invoke() + } } } } @@ -226,6 +228,40 @@ data class UserTabComponent( val compose: @Composable () -> Unit, ) +@Composable +private fun UserTimeline(viewModel: UserViewModel, content: @Composable () -> Unit) { + val relationship by viewModel.relationship.observeAsState(initial = null) + val loadingRelationship by viewModel.loadingRelationship.observeAsState(initial = false) + relationship.takeIf { !loadingRelationship }?.let { + when { + it.blockedBy -> PermissionDeniedInfo( + title = stringResource(id = R.string.scene_profile_permission_denied_profile_blocked_title), + message = stringResource(id = R.string.scene_profile_permission_denied_profile_blocked_message) + ) + else -> content.invoke() + } + } ?: content.invoke() +} + +@Composable +private fun PermissionDeniedInfo(title: String, message: String) { + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { + Row(modifier = Modifier.fillMaxSize().padding(PermissionDeniedInfoDefaults.contentPaddingValues)) { + Icon(painter = painterResource(id = R.drawable.ic_eye_off), contentDescription = title) + Spacer(modifier = Modifier.width(PermissionDeniedInfoDefaults.contentSpacing)) + Column { + Text(text = title, style = MaterialTheme.typography.subtitle2) + Text(text = message, style = MaterialTheme.typography.caption) + } + } + } +} + +private object PermissionDeniedInfoDefaults { + val contentPaddingValues = PaddingValues(22.dp) + val contentSpacing = 16.dp +} + @OptIn(ExperimentalMaterialApi::class) @Composable fun UserStatusTimeline( @@ -634,7 +670,7 @@ private fun UserRelationship(viewModel: UserViewModel) { val shape = RoundedCornerShape(percent = 50) val blockingBackgroundColor = Color(0xFFFF2D55) relationship?.takeIf { !loadingRelationship } - ?.takeIf { !it.blockedBy } + ?.takeIf { !it.blockedBy || it.blocking } ?.let { relationshipResult -> Surface( modifier = Modifier @@ -680,6 +716,7 @@ private fun UserRelationship(viewModel: UserViewModel) { .padding(ButtonDefaults.ContentPadding), text = when { relationshipResult.blocking -> { + // TODO LOCALIZE "Blocked" } relationshipResult.followedBy -> { diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index dcc413f95..0672438cb 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -197,8 +197,9 @@ fun BlockAlert( onDismissRequest.invoke() }, title = { + // TODO LOCALIZE Text( - text = "Block $screenName?", + text = "Do you want to block $screenName?", style = MaterialTheme.typography.subtitle1 ) }, From 7e2a9f4e68bb6b76f95c8d4b6cdc8febf56bf5d1 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 1 Sep 2021 17:54:35 +0800 Subject: [PATCH 115/615] localized strings for block user --- .../com/twidere/twiderex/component/UserComponent.kt | 9 ++++++--- .../kotlin/com/twidere/twiderex/scenes/user/UserScene.kt | 3 +-- app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 32145f137..42fc14c78 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -246,7 +246,11 @@ private fun UserTimeline(viewModel: UserViewModel, content: @Composable () -> Un @Composable private fun PermissionDeniedInfo(title: String, message: String) { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { - Row(modifier = Modifier.fillMaxSize().padding(PermissionDeniedInfoDefaults.contentPaddingValues)) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(PermissionDeniedInfoDefaults.contentPaddingValues) + ) { Icon(painter = painterResource(id = R.drawable.ic_eye_off), contentDescription = title) Spacer(modifier = Modifier.width(PermissionDeniedInfoDefaults.contentSpacing)) Column { @@ -716,8 +720,7 @@ private fun UserRelationship(viewModel: UserViewModel) { .padding(ButtonDefaults.ContentPadding), text = when { relationshipResult.blocking -> { - // TODO LOCALIZE - "Blocked" + stringResource(id = R.string.common_controls_friendship_actions_blocked) } relationshipResult.followedBy -> { stringResource(id = R.string.common_controls_friendship_actions_unfollow) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index 0672438cb..82607a43f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -197,9 +197,8 @@ fun BlockAlert( onDismissRequest.invoke() }, title = { - // TODO LOCALIZE Text( - text = "Do you want to block $screenName?", + text = stringResource(id = R.string.common_alerts_block_user_confirm_title, screenName), style = MaterialTheme.typography.subtitle1 ) }, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 940f4ccde..ced0f9c87 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,6 +48,7 @@ Failed to mute %s Please try again Sending tweet + Do you want to block %s? Failed to Delete Tweet Please try again Photo Saved @@ -85,6 +86,7 @@ Mute %s %s is following you Unblock + Blocked Report and Block Following Pending From 11a2cc6c0f8fe98a9ab295e8e9874b8bede223f6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 2 Sep 2021 14:03:53 +0800 Subject: [PATCH 116/615] upgrade jetpack packages --- .../twidere/twiderex/component/foundation/BlurImage.kt | 2 ++ .../twiderex/model/transform/StatusTransform.kt | 2 ++ .../twidere/twiderex/model/transform/UserTransform.kt | 2 ++ .../kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 1 + app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt | 3 +++ .../twiderex/viewmodel/timeline/TimelineViewModel.kt | 1 + .../viewmodel/twitter/user/TwitterUserViewModel.kt | 3 ++- .../twiderex/paging/crud/MemoryCachePagingMediator.kt | 2 ++ .../twidere/twiderex/paging/lists/ListsMediatorTest.kt | 2 ++ .../paging/lists/ListsUserPagingMediatorTest.kt | 1 + .../twidere/twiderex/paging/trend/TrendMediatorTest.kt | 2 ++ buildSrc/src/main/kotlin/Versions.kt | 10 +++++----- .../services/http/config/HttpConfigClientFactory.kt | 1 + .../src/main/java/com/twidere/services/utils/Json.kt | 2 ++ 14 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt index 873db16b7..7fbd87d81 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt @@ -46,6 +46,7 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toDrawable import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import coil.ImageLoader +import coil.annotation.ExperimentalCoilApi import coil.bitmap.BitmapPool import coil.compose.rememberImagePainter import coil.memory.MemoryCache @@ -89,6 +90,7 @@ fun BlurImage( ) } +@OptIn(ExperimentalCoilApi::class) @Composable fun NetworkBlurImage( data: Any, diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt b/app/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt index e1cfc0691..3399ede37 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/transform/StatusTransform.kt @@ -45,9 +45,11 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.model.ui.mastodon.MastodonMention import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +@OptIn(ExperimentalSerializationApi::class) fun DbStatusV2.toUi( user: UiUser, media: List, diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt b/app/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt index 760b6e5a8..b16e8593c 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/transform/UserTransform.kt @@ -31,6 +31,7 @@ import com.twidere.twiderex.model.ui.UserMetrics import com.twidere.twiderex.model.ui.mastodon.Field import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -52,6 +53,7 @@ fun DbUser.toAmUser() = isProtected = isProtected, ) +@OptIn(ExperimentalSerializationApi::class) fun DbUser.toUi() = UiUser( id = userId, diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 90e05382a..b321604e5 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -371,6 +371,7 @@ private object StatusMediaInfoDefaults { val NameSpacing = 8.dp } +@OptIn(ExperimentalPagerApi::class) @Composable fun RawMediaScene(url: String) { TwidereDialog( diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt index 3539babb5..f8c3768bb 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/Json.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.utils +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -32,8 +33,10 @@ private val JSON by lazy { } } +@OptIn(ExperimentalSerializationApi::class) internal inline fun T.json(): String = JSON.encodeToString(this) +@OptIn(ExperimentalSerializationApi::class) internal inline fun String.fromJson() = JSON.decodeFromString(this) diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 5758d23ab..ac55a55eb 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -44,6 +44,7 @@ abstract class TimelineViewModel( val loadingBetween: Flow> get() = pagingMediator.loadingBetween + @OptIn(androidx.paging.ExperimentalPagingApi::class) fun loadBetween( maxStatusKey: MicroBlogKey, sinceStatueKey: MicroBlogKey, diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 9cea5da60..d0fcb2b3d 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.twitter.user import androidx.lifecycle.ViewModel import com.twidere.services.microblog.LookupService import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notify @@ -48,7 +49,7 @@ class TwitterUserViewModel @AssistedInject constructor( val error = MutableStateFlow(null) - val user = flow { + val user = flow { runCatching { repository.lookupUserByName( screenName, diff --git a/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt index e2e66908d..fed6332cf 100644 --- a/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt +++ b/app/src/test/java/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.paging.crud +import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingSource @@ -41,6 +42,7 @@ class TestMemoryCachePagingMediator(pagingMemoryCache: PagingMemoryCache class MemoryCachePagingMediatorTest { private val pagingMemoryCache = PagingMemoryCache() + @OptIn(ExperimentalPagingApi::class) @Test fun load_saveToPagingMemoryCacheAfterSuccess() = runBlocking { val mediator = TestMemoryCachePagingMediator(pagingMemoryCache) diff --git a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index 435fbc84b..c2a9b371a 100644 --- a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/app/src/test/java/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.paging.lists +import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState @@ -44,6 +45,7 @@ class ListsMediatorTest { private var mockService = MockCenter.mockListsService() as ListsService + @OptIn(ExperimentalPagingApi::class) @Test fun load_saveToDatabaseWhenSuccess() { runBlocking { diff --git a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt b/app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt index 0cc63c40b..e9ed9b214 100644 --- a/app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt +++ b/app/src/test/java/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt @@ -55,6 +55,7 @@ class TestListsUserPagingMediator( } class ListsUserPagingMediatorTest { + @OptIn(ExperimentalPagingApi::class) @Test fun load_nextKeyIsCorrect() { runBlocking { diff --git a/app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt index d68ae25b7..1a3ebd097 100644 --- a/app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt +++ b/app/src/test/java/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.paging.trend +import androidx.paging.ExperimentalPagingApi import androidx.paging.LoadType import androidx.paging.PagingConfig import androidx.paging.PagingState @@ -44,6 +45,7 @@ class TrendMediatorTest { private var mockService = MockCenter.mockTrendService() as TrendService + @OptIn(ExperimentalPagingApi::class) @Test fun load_saveToDatabaseWhenSuccess() { runBlocking { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index be551e13f..71f28deb7 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.5.21" + const val lang = "1.5.30" const val coroutines = "1.5.1" const val serialization = "1.2.2" } @@ -12,7 +12,7 @@ object Versions { val java = JavaVersion.VERSION_11 } - const val ksp = "${Kotlin.lang}-1.0.0-beta07" + const val ksp = "${Kotlin.lang}-1.0.0-beta09" const val agp = "7.0.1" const val spotless = "5.14.2" const val ktlint = "0.41.0" @@ -20,17 +20,17 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose = "1.1.0-alpha02" + const val compose = "1.1.0-alpha03" const val constraintLayout = "1.0.0-beta02" const val paging = "3.1.0-alpha03" const val paging_compose = "1.0.0-alpha12" - const val activity = "1.3.1" + const val activity = "1.4.0-alpha01" const val datastore = "1.0.0" const val androidx_hilt = "1.0.0" const val room = "2.4.0-alpha04" const val lifecycle = "2.4.0-alpha03" const val lifecycle_compose = "1.0.0-alpha07" - const val work = "2.7.0-alpha05" + const val work = "2.7.0-beta01" const val placeholder = "0.7.0" const val zoomable = "1.0.1" const val swiper = "0.6.0" diff --git a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt index 66a65d3a4..55a5e16ad 100644 --- a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt +++ b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt @@ -51,6 +51,7 @@ import java.net.Proxy class HttpConfigClientFactory(private val configProvider: HttpConfigProvider) : HttpClientFactory { private val resourceCache = mutableMapOf, Pair<*, CacheIdentifier>>() + @OptIn(ExperimentalSerializationApi::class) @Suppress("UNCHECKED_CAST") override fun createResources( clazz: Class, diff --git a/services/src/main/java/com/twidere/services/utils/Json.kt b/services/src/main/java/com/twidere/services/utils/Json.kt index 47b5b6144..a6e49fd59 100644 --- a/services/src/main/java/com/twidere/services/utils/Json.kt +++ b/services/src/main/java/com/twidere/services/utils/Json.kt @@ -21,6 +21,7 @@ package com.twidere.services.utils import com.twidere.services.http.MicroBlogException +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromJsonElement @@ -35,6 +36,7 @@ internal val JSON by lazy { class MicroBlogJsonException(override val microBlogErrorMessage: String?) : MicroBlogException() +@OptIn(ExperimentalSerializationApi::class) internal inline fun T.encodeJson(): String = JSON.encodeToString(this) From fd7381cd03b2b424961a3c5386c74503f2949a2c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 2 Sep 2021 15:14:18 +0800 Subject: [PATCH 117/615] update OverScrollConfiguration --- .../main/kotlin/com/twidere/twiderex/ui/Theme.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt b/app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt index 61f58aa10..fcdacf876 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt @@ -22,7 +22,10 @@ package com.twidere.twiderex.ui import android.os.Build import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.LocalOverScrollConfiguration +import androidx.compose.foundation.gestures.OverScrollConfiguration import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues @@ -61,6 +64,7 @@ import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.preferences.proto.AppearancePreferences +@OptIn(ExperimentalFoundationApi::class) @Composable fun TwidereTheme( darkTheme: Boolean = false, @@ -80,7 +84,15 @@ fun TwidereTheme( colors = colors, typography = typography, shapes = shapes, - content = content, + content = { + CompositionLocalProvider( + LocalOverScrollConfiguration provides OverScrollConfiguration( + glowColor = MaterialTheme.colors.primary, + ) + ) { + content.invoke() + } + }, ) } } From 1b0e853b0b06d1509ed3636ed2ef28bf064d2387 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 2 Sep 2021 17:31:09 +0800 Subject: [PATCH 118/615] add mute button to video play scene --- .../foundation/RemainingTimeVideo.kt | 99 +++++++++++++++++++ .../component/foundation/VideoPlayer.kt | 81 +++++++-------- .../foundation/VideoPlayerController.kt | 61 ++++++++++++ .../com/twidere/twiderex/scenes/MediaScene.kt | 24 ++++- .../twidere/twiderex/scenes/PureMediaScene.kt | 26 ++++- .../drawable/ic_drafts_more.xml | 0 .../drawable/ic_keyboard.xml | 0 .../drawable/ic_trash_can.xml | 0 app/src/main/res/drawable/ic_volume.xml | 27 +++++ app/src/main/res/drawable/ic_volume_mute.xml | 20 ++++ .../res/layout/exo_player_control_view.xml | 49 ++------- 11 files changed, 298 insertions(+), 89 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt rename app/src/main/{res-localized => res}/drawable/ic_drafts_more.xml (100%) rename app/src/main/{res-localized => res}/drawable/ic_keyboard.xml (100%) rename app/src/main/{res-localized => res}/drawable/ic_trash_can.xml (100%) create mode 100644 app/src/main/res/drawable/ic_volume.xml create mode 100644 app/src/main/res/drawable/ic_volume_mute.xml diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt new file mode 100644 index 000000000..c462a0525 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt @@ -0,0 +1,99 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation + +import android.content.Context +import android.util.AttributeSet +import com.google.android.exoplayer2.SimpleExoPlayer +import com.google.android.exoplayer2.ui.DefaultTimeBar +import com.google.android.exoplayer2.ui.TimeBar + +class RemainingTimeExoPlayer(builder: Builder) : SimpleExoPlayer(builder) { + override fun getContentPosition(): Long { + return super.getContentPosition() - contentDuration + } +} + +class RemainingTimeBar : DefaultTimeBar { + constructor(context: Context) : this(context, null) + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + timebarAttrs: AttributeSet? + ) : this(context, attrs, defStyleAttr, timebarAttrs, 0) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + timebarAttrs: AttributeSet?, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, timebarAttrs, defStyleRes) + + private var duration: Long = 0 + private val listeners = mutableListOf() + override fun setDuration(duration: Long) { + this.duration = duration + super.setDuration(duration) + } + override fun setPosition(position: Long) { + super.setPosition(if (position < 0) duration + position else position) + } + + override fun addListener(listener: TimeBar.OnScrubListener) { + val wrapper = RemainingScrubListenerWrapper(listener) { + if (it < 0) it else it - duration + }.also { + listeners.add(it) + } + super.addListener(wrapper) + } + + override fun removeListener(listener: TimeBar.OnScrubListener) { + listeners.removeAll { it.listener == listener } + super.removeListener(listener) + } + + private class RemainingScrubListenerWrapper( + val listener: TimeBar.OnScrubListener, + private val transform: (position: Long) -> Long + ) : TimeBar.OnScrubListener { + + override fun onScrubStart(timeBar: TimeBar, position: Long) { + listener.onScrubStart(timeBar, transform(position)) + } + + override fun onScrubMove(timeBar: TimeBar, position: Long) { + listener.onScrubMove(timeBar, transform(position)) + } + + override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { + // do not transform position here + listener.onScrubStop(timeBar, position, canceled) + } + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 3ac33fa92..5a58248c6 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -96,51 +96,52 @@ fun VideoPlayer( Box { if (playInitial) { val player = remember(url) { - SimpleExoPlayer.Builder(context) - .apply { - if (httpConfig.proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } - } - } - .build().apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - shouldShowThumb = state != Player.STATE_READY - } - - override fun onIsPlayingChanged(isPlaying: Boolean) { - playing = isPlaying + RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context) + .apply { + if (httpConfig.proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } } - }) + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + shouldShowThumb = state != Player.STATE_READY + } - setVolume(volume) - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) + override fun onIsPlayingChanged(isPlaying: Boolean) { + playing = isPlaying } - prepare() - seekTo(VideoPool.get(url)) + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) } + prepare() + seekTo(VideoPool.get(url)) + } } + player.volume = volume fun updateState() { autoPlay = player.playWhenReady diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt new file mode 100644 index 000000000..ba2d8083c --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt @@ -0,0 +1,61 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.viewinterop.AndroidView +import com.google.android.exoplayer2.ui.PlayerControlView +import com.twidere.twiderex.R + +@Composable +fun VideoPlayerController( + videoControl: PlayerControlView, + mute: Boolean, + onMute: (isMute: Boolean) -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + AndroidView( + modifier = Modifier.weight(1f), + factory = { videoControl } + ) + IconButton( + onClick = { onMute(!mute) }, + ) { + Icon( + painter = painterResource(id = if (mute) R.drawable.ic_volume_mute else R.drawable.ic_volume), + contentDescription = stringResource(id = R.string.accessibility_common_video_play), + tint = MaterialTheme.colors.onSurface + ) + } + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 77f957776..93348efbd 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -67,7 +67,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsHeight import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsPadding @@ -83,6 +82,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer +import com.twidere.twiderex.component.foundation.VideoPlayerController import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.status.LikeButton import com.twidere.twiderex.component.status.ReplyButton @@ -174,6 +174,10 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM null } } + var isMute by remember { + // todo use settings + mutableStateOf(false) + } val swiperState = rememberSwiperState( onDismiss = { navController.popBackStack() @@ -212,7 +216,12 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM .navigationBarsPadding() .clickable { navigator.status(status = status) }, ) { - StatusMediaInfo(videoControl, status, viewModel, currentMedia) + StatusMediaInfo( + videoControl, status, viewModel, currentMedia, isMute, + onMute = { + isMute = it + } + ) } } } @@ -244,6 +253,7 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM swiperState = swiperState, customControl = videoControl, pagerState = pagerState, + volume = if (isMute) 0f else 1f ) DisposableEffect(Unit) { window.setOnSystemBarsVisibilityChangeListener { visibility -> @@ -297,7 +307,9 @@ private fun StatusMediaInfo( videoControl: PlayerControlView?, status: UiStatus, viewModel: MediaViewModel, - currentMedia: UiMedia + currentMedia: UiMedia, + mute: Boolean, + onMute: (isMute: Boolean) -> Unit ) { val context = LocalContext.current Column( @@ -305,7 +317,7 @@ private fun StatusMediaInfo( .padding(StatusMediaInfoDefaults.ContentPadding), ) { if (videoControl != null) { - AndroidView(factory = { videoControl }) + VideoPlayerController(videoControl = videoControl, mute = mute, onMute = onMute) } StatusText(status = status, maxLines = 2, showMastodonPoll = false) Spacer(modifier = Modifier.height(StatusMediaInfoDefaults.TextSpacing)) @@ -411,6 +423,7 @@ fun MediaView( pageCount = media.size, ), customControl: PlayerControlView? = null, + volume: Float = 1f ) { Box( modifier = Modifier @@ -449,7 +462,8 @@ fun MediaView( customControl = customControl, showControls = false, zOrderMediaOverlay = true, - keepScreenOn = true + keepScreenOn = true, + volume = volume ) } MediaType.other -> Unit diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index f2a4a6804..487e1229b 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -53,7 +53,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.pager.ExperimentalPagerApi @@ -61,6 +60,7 @@ import com.google.accompanist.pager.rememberPagerState import com.google.android.exoplayer2.ui.PlayerControlView import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.foundation.VideoPlayerController import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.hideControls import com.twidere.twiderex.extensions.observeAsState @@ -117,6 +117,10 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { navController.popBackStack() }, ) + var isMute by remember { + // todo use settings + mutableStateOf(false) + } InAppNotificationScaffold( backgroundColor = Color.Transparent, contentColor = contentColorFor(backgroundColor = MaterialTheme.colors.background), @@ -125,7 +129,9 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { controlVisibility = controlVisibility, swiperState = swiperState, controlPanelColor = controlPanelColor, - videoControl = videoControl + videoControl = videoControl, + mute = isMute, + onMute = { isMute = it } ) } ) { @@ -155,6 +161,7 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { swiperState = swiperState, customControl = videoControl, pagerState = pagerState, + volume = if (isMute) 0f else 1f ) DisposableEffect(Unit) { window.setOnSystemBarsVisibilityChangeListener { visibility -> @@ -181,7 +188,14 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { @OptIn(ExperimentalAnimationApi::class) @Composable -fun PureMediaBottomInfo(controlVisibility: Boolean, swiperState: SwiperState, controlPanelColor: Color, videoControl: PlayerControlView?) { +fun PureMediaBottomInfo( + controlVisibility: Boolean, + swiperState: SwiperState, + controlPanelColor: Color, + videoControl: PlayerControlView?, + mute: Boolean, + onMute: (Boolean) -> Unit +) { AnimatedVisibility( visible = controlVisibility && swiperState.progress == 0f, enter = fadeIn() + expandVertically(), @@ -195,7 +209,11 @@ fun PureMediaBottomInfo(controlVisibility: Boolean, swiperState: SwiperState, co .navigationBarsPadding(), ) { if (videoControl != null) { - AndroidView(factory = { videoControl }) + VideoPlayerController( + videoControl = videoControl, + mute = mute, + onMute = onMute + ) } } } diff --git a/app/src/main/res-localized/drawable/ic_drafts_more.xml b/app/src/main/res/drawable/ic_drafts_more.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_drafts_more.xml rename to app/src/main/res/drawable/ic_drafts_more.xml diff --git a/app/src/main/res-localized/drawable/ic_keyboard.xml b/app/src/main/res/drawable/ic_keyboard.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_keyboard.xml rename to app/src/main/res/drawable/ic_keyboard.xml diff --git a/app/src/main/res-localized/drawable/ic_trash_can.xml b/app/src/main/res/drawable/ic_trash_can.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_trash_can.xml rename to app/src/main/res/drawable/ic_trash_can.xml diff --git a/app/src/main/res/drawable/ic_volume.xml b/app/src/main/res/drawable/ic_volume.xml new file mode 100644 index 000000000..f1e577fa7 --- /dev/null +++ b/app/src/main/res/drawable/ic_volume.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_volume_mute.xml b/app/src/main/res/drawable/ic_volume_mute.xml new file mode 100644 index 000000000..21d81cbce --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_mute.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/layout/exo_player_control_view.xml b/app/src/main/res/layout/exo_player_control_view.xml index 1d4056def..4fae4a3ec 100644 --- a/app/src/main/res/layout/exo_player_control_view.xml +++ b/app/src/main/res/layout/exo_player_control_view.xml @@ -12,56 +12,26 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 141d5c7cac7f982caaaf2ffc0a9123a703ecb342 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 2 Sep 2021 17:54:32 +0800 Subject: [PATCH 119/615] add mute by defaut to display settings --- .../component/foundation/VideoPlayerController.kt | 10 +++++++++- .../kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 5 +++-- .../com/twidere/twiderex/scenes/PureMediaScene.kt | 5 +++-- .../twidere/twiderex/scenes/settings/DisplayScene.kt | 9 +++++++++ .../twiderex/viewmodel/settings/DisplayViewModel.kt | 6 ++++++ app/src/main/proto/DisplayPreferences.proto | 1 + app/src/main/res/layout/exo_player_control_view.xml | 12 ++++++------ app/src/main/res/values/strings.xml | 11 +++++++++++ 8 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt index ba2d8083c..c41386da3 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerController.kt @@ -21,7 +21,9 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -30,6 +32,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.android.exoplayer2.ui.PlayerControlView import com.twidere.twiderex.R @@ -42,7 +45,8 @@ fun VideoPlayerController( ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.padding(VideoPlayerControllerDefaults.contentPadding) ) { AndroidView( modifier = Modifier.weight(1f), @@ -59,3 +63,7 @@ fun VideoPlayerController( } } } + +private object VideoPlayerControllerDefaults { + val contentPadding = PaddingValues(vertical = 8.dp) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 93348efbd..350158663 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -101,6 +101,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.preferences.proto.DisplayPreferences import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -174,9 +175,9 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM null } } + val display = LocalDisplayPreferences.current var isMute by remember { - // todo use settings - mutableStateOf(false) + mutableStateOf(display.muteByDefault) } val swiperState = rememberSwiperState( onDismiss = { diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index 487e1229b..059dbcbd8 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -68,6 +68,7 @@ import com.twidere.twiderex.extensions.setOnSystemBarsVisibilityChangeListener import com.twidere.twiderex.extensions.showControls import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.preferences.proto.DisplayPreferences import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback @@ -117,9 +118,9 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { navController.popBackStack() }, ) + val display = LocalDisplayPreferences.current var isMute by remember { - // todo use settings - mutableStateOf(false) + mutableStateOf(display.muteByDefault) } InAppNotificationScaffold( backgroundColor = Color.Transparent, diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 66380e8a2..e1eb5cb71 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -183,6 +183,15 @@ fun DisplayScene() { Text(text = stringResource(id = R.string.scene_settings_display_media_media_previews)) } ) + switchItem( + value = display.muteByDefault, + onChanged = { + viewModel.setMuteByDefault(it) + }, + title = { + Text(text = stringResource(id = R.string.scene_settings_display_media_mute_by_default)) + } + ) if (display.mediaPreview) { RadioItem( options = listOf( diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index 0be86b177..66479c159 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -71,4 +71,10 @@ class DisplayViewModel @AssistedInject constructor( it.toBuilder().setFontScale(value).build() } } + + fun setMuteByDefault(value: Boolean) = viewModelScope.launch { + displayPreferences.updateData { + it.toBuilder().setMuteByDefault(value).build() + } + } } diff --git a/app/src/main/proto/DisplayPreferences.proto b/app/src/main/proto/DisplayPreferences.proto index 6a64d0be1..7dc1c3368 100644 --- a/app/src/main/proto/DisplayPreferences.proto +++ b/app/src/main/proto/DisplayPreferences.proto @@ -21,4 +21,5 @@ message DisplayPreferences { bool mediaPreview = 4; AutoPlayback autoPlayback = 5; bool urlPreview = 6; + bool muteByDefault = 7; } \ No newline at end of file diff --git a/app/src/main/res/layout/exo_player_control_view.xml b/app/src/main/res/layout/exo_player_control_view.xml index 4fae4a3ec..c2a952d5f 100644 --- a/app/src/main/res/layout/exo_player_control_view.xml +++ b/app/src/main/res/layout/exo_player_control_view.xml @@ -16,16 +16,18 @@ android:gravity="center_vertical" android:orientation="horizontal"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 940f4ccde..ad194babf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,6 +48,7 @@ Failed to mute %s Please try again Sending tweet + Do you want to block %s? Failed to Delete Tweet Please try again Photo Saved @@ -85,6 +86,7 @@ Mute %s %s is following you Unblock + Blocked Report and Block Following Pending @@ -113,7 +115,10 @@ Take photo OK Sign in + %s boosted %s retweeted + You retweeted + You boosted Media %s person %s vote @@ -122,11 +127,16 @@ %s people Show this thread Share link + Bookmark Delete tweet Quote Retweet + Pin on Profile Copy text + Unpin from Profile + Share Vote + Translate Copy link %s quote %s quotes @@ -269,6 +279,7 @@ Always Media previews Automatic + Mute by default Auto playback Off Display From 97f9d7d7ae03936b05db52dd0913ca3194dda293 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 2 Sep 2021 18:20:57 +0800 Subject: [PATCH 120/615] migrate strings to common --- android/build.gradle.kts | 69 +--- .../com/twidere/twiderex/TwidereXActivity.kt | 2 +- .../twidere/twiderex/component/LoginLogo.kt | 2 +- .../twiderex/component/UserComponent.kt | 30 +- .../foundation/AppBarNavigationButton.kt | 2 +- .../component/foundation/ErrorPlaceholder.kt | 2 +- .../component/foundation/NetworkImage.kt | 2 +- .../component/foundation/SignInScaffold.kt | 2 +- .../component/foundation/VideoPlayer.kt | 2 +- .../twiderex/component/lazy/itemsPaging.kt | 2 +- .../lazy/ui/LazyUiDMConversationList.kt | 2 +- .../component/lazy/ui/LazyUiDMEventList.kt | 2 +- .../component/lazy/ui/LazyUiListsList.kt | 8 +- .../component/lazy/ui/LazyUiStatusList.kt | 6 +- .../component/lazy/ui/LazyUiUserList.kt | 2 +- .../lists/MastodonListsModifyComponent.kt | 6 +- .../lists/TwitterListsModifyComponent.kt | 6 +- .../status/DetailedStatusComponent.kt | 8 +- .../twiderex/component/status/MastodonPoll.kt | 12 +- .../component/status/StatusActions.kt | 20 +- .../twiderex/component/status/StatusThread.kt | 4 +- .../status/TimelineStatusComponent.kt | 18 +- .../twiderex/component/status/TweetHeader.kt | 4 +- .../component/trend/MastodonTrendItem.kt | 2 +- .../twiderex/extensions/MastodonExtensions.kt | 8 +- .../twiderex/jobs/common/DownloadMediaJob.kt | 2 +- .../twiderex/jobs/common/NotificationJob.kt | 14 +- .../twiderex/jobs/compose/ComposeJob.kt | 6 +- .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 4 +- .../twiderex/jobs/dm/DirectMessageSendJob.kt | 2 +- .../notification/NotificationChannelSpec.kt | 11 +- .../twidere/twiderex/scenes/DraftListScene.kt | 8 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 10 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 6 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 2 +- .../twidere/twiderex/scenes/SignInScene.kt | 20 +- .../twidere/twiderex/scenes/StatusScene.kt | 2 +- .../twiderex/scenes/compose/ComposeScene.kt | 44 +-- .../compose/ComposeSearchHashtagScene.kt | 4 +- .../scenes/compose/ComposeSearchUserScene.kt | 4 +- .../scenes/dm/DMConversationListScene.kt | 4 +- .../twiderex/scenes/dm/DMConversationScene.kt | 14 +- .../scenes/dm/DMNewConversationScene.kt | 6 +- .../scenes/home/AllNotificationItem.kt | 4 +- .../scenes/home/DMConversationListItem.kt | 2 +- .../scenes/home/DraftNavigationItem.kt | 2 +- .../twiderex/scenes/home/HomeTimelineItem.kt | 6 +- .../scenes/home/ListsNavigationItem.kt | 2 +- .../twidere/twiderex/scenes/home/MeItem.kt | 4 +- .../twiderex/scenes/home/MentionItem.kt | 4 +- .../twiderex/scenes/home/NotificationItem.kt | 4 +- .../twiderex/scenes/home/SearchItem.kt | 18 +- .../home/mastodon/FederatedTimelineItem.kt | 2 +- .../scenes/home/mastodon/LocalTimelineItem.kt | 2 +- .../home/mastodon/MastodonNotificationItem.kt | 4 +- .../scenes/lists/ListsAddMembersScene.kt | 12 +- .../scenes/lists/ListsMembersScene.kt | 10 +- .../twiderex/scenes/lists/ListsScene.kt | 6 +- .../scenes/lists/ListsSubscribersScene.kt | 2 +- .../scenes/lists/ListsTimelineScene.kt | 26 +- .../platform/MastodonListsCreateDialog.kt | 2 +- .../lists/platform/MastodonListsEditDialog.kt | 2 +- .../lists/platform/TwitterListsCreateScene.kt | 4 +- .../lists/platform/TwitterListsEditScene.kt | 4 +- .../scenes/mastodon/MastodonSignInScene.kt | 6 +- .../scenes/search/SearchInputScene.kt | 8 +- .../twiderex/scenes/search/SearchScene.kt | 2 +- .../search/tabs/MastodonSearchHashtagItem.kt | 2 +- .../scenes/search/tabs/SearchTweetsItem.kt | 2 +- .../scenes/search/tabs/SearchUserItem.kt | 2 +- .../search/tabs/TwitterSearchMediaItem.kt | 2 +- .../twiderex/scenes/settings/AboutScene.kt | 18 +- .../scenes/settings/AccountManagementScene.kt | 8 +- .../settings/AccountNotificationScene.kt | 2 +- .../scenes/settings/AppearanceScene.kt | 32 +- .../twiderex/scenes/settings/DisplayScene.kt | 32 +- .../twiderex/scenes/settings/LayoutScene.kt | 10 +- .../twiderex/scenes/settings/MiscScene.kt | 50 +-- .../scenes/settings/NotificationScene.kt | 6 +- .../twiderex/scenes/settings/SettingsScene.kt | 20 +- .../twiderex/scenes/settings/StorageScene.kt | 12 +- .../twiderex/scenes/user/FollowersScene.kt | 2 +- .../twiderex/scenes/user/FollowingScene.kt | 2 +- .../twidere/twiderex/scenes/user/UserScene.kt | 2 +- .../twiderex/utils/TwitterErrorHandling.kt | 8 +- .../twiderex/viewmodel/MediaViewModel.kt | 2 +- .../viewmodel/compose/ComposeViewModel.kt | 14 +- .../viewmodel/settings/MiscViewModel.kt | 2 +- .../res-localized/values-ar-rSA/strings.xml | 41 --- .../res-localized/values-fr-rFR/strings.xml | 7 - buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 85 ++++- .../com/twidere/twiderex/model/ui/UiStatus.kt | 2 +- .../resources/MR/ar-rSA/strings.xml | 138 ++++++++ .../commonMain/resources/MR/base}/strings.xml | 12 +- .../resources/MR/ca-rES}/strings.xml | 0 .../resources/MR/de-rDE}/strings.xml | 4 + .../resources/MR/el-rGR/strings.xml | 54 +++ .../resources/MR/en-rUS}/strings.xml | 0 .../resources/MR/es-rES}/strings.xml | 15 +- .../resources/MR/fr-rFR/strings.xml | 332 ++++++++++++++++++ .../resources/MR/it-rIT}/strings.xml | 0 .../resources/MR/ja-rJP}/strings.xml | 5 +- .../resources/MR/ko-rKR}/strings.xml | 6 +- .../resources/MR/pl-rPL/strings.xml | 3 + .../resources/MR/pt-rBR}/strings.xml | 3 +- .../resources/MR/si-rLK}/strings.xml | 0 .../resources/MR/tr-rTR}/strings.xml | 3 +- .../resources/MR/vi-rVN}/strings.xml | 0 .../resources/MR/zh-rCN}/strings.xml | 4 +- .../resources/MR/zh-rTW}/strings.xml | 0 localization | 2 +- 112 files changed, 992 insertions(+), 479 deletions(-) delete mode 100644 android/src/main/res-localized/values-ar-rSA/strings.xml delete mode 100644 android/src/main/res-localized/values-fr-rFR/strings.xml create mode 100644 common/src/commonMain/resources/MR/ar-rSA/strings.xml rename {android/src/main/res/values => common/src/commonMain/resources/MR/base}/strings.xml (97%) rename {android/src/main/res-localized/values-ca-rES => common/src/commonMain/resources/MR/ca-rES}/strings.xml (100%) rename {android/src/main/res-localized/values-de-rDE => common/src/commonMain/resources/MR/de-rDE}/strings.xml (98%) create mode 100644 common/src/commonMain/resources/MR/el-rGR/strings.xml rename {android/src/main/res-localized/values-en-rUS => common/src/commonMain/resources/MR/en-rUS}/strings.xml (100%) rename {android/src/main/res-localized/values-es-rES => common/src/commonMain/resources/MR/es-rES}/strings.xml (98%) create mode 100644 common/src/commonMain/resources/MR/fr-rFR/strings.xml rename {android/src/main/res-localized/values-it-rIT => common/src/commonMain/resources/MR/it-rIT}/strings.xml (100%) rename {android/src/main/res-localized/values-ja-rJP => common/src/commonMain/resources/MR/ja-rJP}/strings.xml (98%) rename {android/src/main/res-localized/values-ko-rKR => common/src/commonMain/resources/MR/ko-rKR}/strings.xml (99%) create mode 100644 common/src/commonMain/resources/MR/pl-rPL/strings.xml rename {android/src/main/res-localized/values-pt-rBR => common/src/commonMain/resources/MR/pt-rBR}/strings.xml (99%) rename {android/src/main/res-localized/values-si-rLK => common/src/commonMain/resources/MR/si-rLK}/strings.xml (100%) rename {android/src/main/res-localized/values-tr-rTR => common/src/commonMain/resources/MR/tr-rTR}/strings.xml (99%) rename {android/src/main/res-localized/values-vi-rVN => common/src/commonMain/resources/MR/vi-rVN}/strings.xml (100%) rename {android/src/main/res-localized/values-zh-rCN => common/src/commonMain/resources/MR/zh-rCN}/strings.xml (99%) rename {android/src/main/res-localized/values-zh-rTW => common/src/commonMain/resources/MR/zh-rTW}/strings.xml (100%) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 872265206..b0e96505b 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,5 +1,4 @@ -import org.gradle.kotlin.dsl.support.unzipTo -import org.json.JSONObject + import java.util.Properties buildscript { @@ -189,69 +188,3 @@ dependencies { mockito() androidTest() } - -tasks.register("generateTranslation") { - val localizationFolder = File(rootDir, "localization") - val appJson = File(localizationFolder, "app.json") - val target = project.file("src/main/res/values/strings.xml") - generateLocalization(appJson, target) -} - -tasks.register("generateTranslationFromZip") { - val zip = File(rootProject.buildDir, "Twidere X (translations).zip") - val unzipTarget = File(rootProject.buildDir, "translation").apply { - mkdirs() - } - unzipTo(unzipTarget, zip) - unzipTarget.listFiles()?.forEach { file -> - val source = File(file, "app.json") - val target = project.file( - "src/main/res-localized" + "/values-" + file.name.split('_') - .first() + "-r" + file.name.split('_').last() + "/strings.xml" - ) - generateLocalization(source, target) - } -} - -fun generateLocalization(appJson: File, target: File) { - val json = appJson.readText(Charsets.UTF_8) - val obj = JSONObject(json) - val result = flattenJson(obj).filter { - it.value.isNotEmpty() && it.value.isNotBlank() - } - if (result.isNotEmpty()) { - val xml = - """""" + System.lineSeparator() + - result.map { - " ${ - it.value.replace("'", "\\'").replace(System.lineSeparator(), "\\n") - }" - }.joinToString(System.lineSeparator()) + System.lineSeparator() + - "" - target.writeText(xml) - } -} - -fun flattenJson(obj: JSONObject): Map { - return obj.toMap().toList().flatMap { it -> - val (key, value) = it - when (value) { - is JSONObject -> { - flattenJson(value).map { - "${key}_${it.key}" to it.value - }.toList() - } - is Map<*, *> -> { - flattenJson(JSONObject(value)).map { - "${key}_${it.key}" to it.value - }.toList() - } - is String -> { - listOf(key to value) - } - else -> { - listOf(key to value.toString()) - } - } - }.toMap() -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index eb407eca7..aa6b1a13e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -170,7 +170,7 @@ class TwidereXActivity : ComponentActivity() { ) { Image( painter = painterResource(id = R.drawable.ic_login_logo), - contentDescription = stringResource(id = R.string.accessibility_common_logo_twidere) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twidere) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt b/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt index 234fec77b..1fb9708c9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt @@ -36,6 +36,6 @@ fun LoginLogo( modifier = modifier, contentScale = ContentScale.FillWidth, painter = painterResource(id = R.drawable.ic_login_logo), - contentDescription = stringResource(id = R.string.accessibility_common_logo_twidere) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twidere) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 86ea31844..2ece804f2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -133,13 +133,13 @@ fun UserComponent( val tabs = listOf( UserTabComponent( painterResource(id = R.drawable.ic_float_left), - stringResource(id = R.string.accessibility_scene_user_tab_status) + stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_status) ) { UserStatusTimeline(userKey = userKey, viewModel = viewModel) }, UserTabComponent( painterResource(id = R.drawable.ic_photo), - stringResource(id = R.string.accessibility_scene_user_tab_media) + stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_media) ) { UserMediaTimeline(userKey = userKey) }, @@ -147,7 +147,7 @@ fun UserComponent( if (viewModel.isMe || userKey.host == MicroBlogKey.TwitterHost) { it + UserTabComponent( painterResource(id = R.drawable.ic_heart), - stringResource(id = R.string.accessibility_scene_user_tab_favourite) + stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_favourite) ) { UserFavouriteTimeline(userKey = userKey) } @@ -277,12 +277,12 @@ private fun UserStatusTimelineFilter( modifier = Modifier.weight(1f), text = if (user.metrics.status > 1) { stringResource( - id = R.string.common_countable_tweet_single, + id = com.twidere.common.R.string.common_countable_tweet_single, user.metrics.status ) } else { stringResource( - id = R.string.common_countable_tweet_multiple, + id = com.twidere.common.R.string.common_countable_tweet_multiple, user.metrics.status ) } @@ -312,7 +312,7 @@ private fun UserStatusTimelineFilter( ) } ) { - Text(text = stringResource(id = R.string.scene_profile_filter_all)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_filter_all)) } } DropdownMenuItem( @@ -332,7 +332,7 @@ private fun UserStatusTimelineFilter( ) } ) { - Text(text = stringResource(id = R.string.scene_profile_filter_exclude_replies)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_filter_exclude_replies)) } } } @@ -474,7 +474,7 @@ fun UserInfo( .fillMaxWidth(), painter = painterResource(id = R.drawable.ic_globe), contentDescription = stringResource( - id = R.string.accessibility_scene_user_website + id = com.twidere.common.R.string.accessibility_scene_user_website ), text = it, textColor = MaterialTheme.colors.primary, @@ -485,7 +485,7 @@ fun UserInfo( ProfileItem( painter = painterResource(id = R.drawable.ic_map_pin), contentDescription = stringResource( - id = R.string.accessibility_scene_user_location + id = com.twidere.common.R.string.accessibility_scene_user_location ), text = it ) @@ -670,16 +670,16 @@ private fun UserRelationship(viewModel: UserViewModel) { modifier = Modifier .padding(ButtonDefaults.ContentPadding), text = if (relationshipResult.followedBy) { - stringResource(id = R.string.common_controls_friendship_actions_unfollow) + stringResource(id = com.twidere.common.R.string.common_controls_friendship_actions_unfollow) } else { - stringResource(id = R.string.common_controls_friendship_actions_follow) + stringResource(id = com.twidere.common.R.string.common_controls_friendship_actions_follow) }, ) } Spacer(modifier = Modifier.height(UserRelationshipDefaults.FollowingSpacing)) if (relationshipResult.following) { Text( - text = stringResource(id = R.string.common_controls_friendship_follows_you), + text = stringResource(id = com.twidere.common.R.string.common_controls_friendship_follows_you), style = MaterialTheme.typography.caption, ) } @@ -733,7 +733,7 @@ fun UserMetrics( navController.navigate(RootRoute.Following(user.userKey)) }, primaryText = user.metrics.follow.toString(), - secondaryText = stringResource(id = R.string.common_controls_profile_dashboard_following), + secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_following), ) HorizontalDivider( modifier = Modifier.height(LocalTextStyle.current.fontSize.value.dp * 2) @@ -745,7 +745,7 @@ fun UserMetrics( navController.navigate(RootRoute.Followers(user.userKey)) }, primaryText = user.metrics.fans.toString(), - secondaryText = stringResource(id = R.string.common_controls_profile_dashboard_followers), + secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_followers), ) if (user.platformType == PlatformType.Twitter) { HorizontalDivider( @@ -755,7 +755,7 @@ fun UserMetrics( modifier = Modifier .weight(1f), primaryText = user.metrics.listed.toString(), - secondaryText = stringResource(id = R.string.common_controls_profile_dashboard_listed), + secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_listed), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt index b880fbcf7..600361b67 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt @@ -42,7 +42,7 @@ fun AppBarNavigationButton( ) { Icon( imageVector = icon, - contentDescription = stringResource(id = R.string.accessibility_common_back) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_back) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt index b4eb23e31..e40e4f725 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt @@ -60,7 +60,7 @@ fun ErrorPlaceholder( ) { Text( text = message - ?: stringResource(id = R.string.common_alerts_failed_to_load_title), + ?: stringResource(id = com.twidere.common.R.string.common_alerts_failed_to_load_title), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 93284077d..56c383639 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -72,7 +72,7 @@ fun NetworkImage( painter = painter, modifier = modifier, contentScale = contentScale, - contentDescription = stringResource(id = R.string.accessibility_common_network_image) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_network_image) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt index bcf0becb2..91050fdb9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt @@ -101,7 +101,7 @@ fun SignInScaffold( modifier = Modifier .weight(1F) .align(Alignment.Start), - text = stringResource(id = R.string.scene_sign_in_hello_sign_in_to_get_started), + text = stringResource(id = com.twidere.common.R.string.scene_sign_in_hello_sign_in_to_get_started), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h3, color = MaterialTheme.colors.primary, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index c02c327ac..a75881778 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -207,7 +207,7 @@ fun VideoPlayer( .align(Alignment.Center) .size(UserAvatarDefaults.AvatarSize) .background(MaterialTheme.colors.primary, CircleShape), - contentDescription = stringResource(id = R.string.accessibility_common_video_play) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_video_play) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt index 5d7a13712..44773097d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt @@ -39,7 +39,7 @@ fun LazyListScope.loadState( ListItem( modifier = Modifier.clickable(onClick = { onReloadRequested.invoke() }), text = { -// Text(text = stringResource(id = R.string.list_load_state_error)) +// Text(text = stringResource(id = com.twidere.common.R.string.list_load_state_error)) } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt index c8620b8cb..abe6570ce 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt @@ -161,7 +161,7 @@ private fun UiDMEvent.resolveLink( entity != null -> { if (entity.displayUrl.contains("pic.twitter.com")) { ResolvedLink( - expanded = context.getString(R.string.scene_messages_expanded_photo), + expanded = context.getString(com.twidere.common.R.string.scene_messages_expanded_photo), clickable = false ) } else { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 7deea4211..dca25bda2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -155,7 +155,7 @@ private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiD ) { Icon( painter = painterResource(id = R.drawable.ic_alert), - contentDescription = stringResource(id = R.string.scene_messages_icon_failed), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_messages_icon_failed), tint = Color.Unspecified, modifier = Modifier.size(DMOutComeEventDefaults.Error.size) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt index 0492763d6..93ad0aaca 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt @@ -88,7 +88,7 @@ fun LazyUiListsList( if (listType == ListType.All) { item { LazyUiListTitleItem( - title = stringResource(id = R.string.scene_lists_tabs_created).uppercase( + title = stringResource(id = com.twidere.common.R.string.scene_lists_tabs_created).uppercase( Locale.getDefault() ) ) @@ -115,7 +115,7 @@ fun LazyUiListsList( if (listType == ListType.All) { item { LazyUiListTitleItem( - title = stringResource(id = R.string.scene_lists_tabs_subscribed).uppercase( + title = stringResource(id = com.twidere.common.R.string.scene_lists_tabs_subscribed).uppercase( Locale.getDefault() ), divider = true @@ -186,7 +186,7 @@ private fun LazyUiListItem(uiList: UiList, onItemClicked: (UiList) -> Unit = {}) if (uiList.isPrivate) { Icon( painter = painterResource(id = R.drawable.ic_lock), - contentDescription = stringResource(id = R.string.scene_lists_icons_private), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_lists_icons_private), modifier = Modifier .alpha(ContentAlpha.disabled) .size(LazyUiListsItemDefaults.LockIconSize) @@ -254,7 +254,7 @@ private fun EmptyList() { ) { Icon( painter = painterResource(id = R.drawable.ic_empty_list), - contentDescription = stringResource(id = R.string.common_alerts_no_tweets_found_title) + contentDescription = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt index 1eccff026..bd86cac44 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt @@ -253,7 +253,7 @@ private fun LoadMoreButton( Text( modifier = Modifier.padding(12.dp), text = stringResource( - id = R.string.common_controls_timeline_load_more + id = com.twidere.common.R.string.common_controls_timeline_load_more ), color = MaterialTheme.colors.primary, ) @@ -269,12 +269,12 @@ private fun EmptyStatusList() { ) { Icon( painter = painterResource(id = R.drawable.ic_empty_status), - contentDescription = stringResource(id = R.string.common_alerts_no_tweets_found_title) + contentDescription = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title) ) Spacer(modifier = Modifier.height(EmptyStatusListDefaults.VerticalPadding)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = R.string.common_alerts_no_tweets_found_title), + text = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title), style = MaterialTheme.typography.h6 ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt index b2f9a00be..2e92a9f95 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt @@ -96,7 +96,7 @@ fun LazyUiUserList( Row { Text( text = stringResource( - id = R.string.common_controls_profile_dashboard_followers, + id = com.twidere.common.R.string.common_controls_profile_dashboard_followers, ) ) Spacer(modifier = Modifier.width(UiUserListDefaults.HorizontalPadding)) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt index 1701b9fcf..7451a7331 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt @@ -66,7 +66,7 @@ fun MastodonListsModifyComponent( onValueChange = onNameChanged, placeholder = { Text( - text = stringResource(id = R.string.scene_lists_modify_name), + text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_name), style = MaterialTheme.typography.subtitle1, ) }, @@ -79,7 +79,7 @@ fun MastodonListsModifyComponent( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_cancel)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) } }, confirmButton = { @@ -88,7 +88,7 @@ fun MastodonListsModifyComponent( onConfirm(name) } ) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } }, shape = RoundedCornerShape(0.dp) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt index 71985b4a7..ad8f3aa9b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt @@ -84,7 +84,7 @@ fun TwitterListsModifyComponent( } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = R.string.scene_lists_modify_name), + text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_name), style = MaterialTheme.typography.caption, modifier = Modifier.padding(TwitterListsModifyComponentDefaults.TextFieldTitle.ContentPadding) ) @@ -119,7 +119,7 @@ fun TwitterListsModifyComponent( Spacer(modifier = Modifier.height(TwitterListsModifyComponentDefaults.VerticalPadding)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = R.string.scene_lists_modify_description), + text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_description), style = MaterialTheme.typography.caption, modifier = Modifier.padding(TwitterListsModifyComponentDefaults.TextFieldTitle.ContentPadding) ) @@ -152,7 +152,7 @@ fun TwitterListsModifyComponent( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, ) { - Text(text = stringResource(id = R.string.scene_lists_modify_private), style = MaterialTheme.typography.body1) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_private), style = MaterialTheme.typography.body1) ColoredSwitch( checked = isPrivate, onCheckedChange = onPrivateChanged, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt index 7814d1a7d..9c4bdbca6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt @@ -93,7 +93,7 @@ fun DetailedStatusComponent( modifier = Modifier.size(MaterialTheme.typography.body1.fontSize.value.dp), painter = painterResource(id = R.drawable.ic_map_pin), contentDescription = stringResource( - id = R.string.accessibility_common_status_location + id = com.twidere.common.R.string.accessibility_common_status_location ) ) Text(text = status.geo.name) @@ -126,7 +126,7 @@ fun DetailedStatusComponent( count = status.metrics.reply, icon = painterResource(id = R.drawable.ic_corner_up_left), contentDescription = stringResource( - id = R.string.scene_status_reply_mutiple, + id = com.twidere.common.R.string.scene_status_reply_mutiple, status.metrics.reply, ), ) @@ -135,7 +135,7 @@ fun DetailedStatusComponent( count = status.metrics.retweet, icon = painterResource(id = R.drawable.ic_repeat), contentDescription = stringResource( - id = R.string.scene_status_retweet_mutiple, + id = com.twidere.common.R.string.scene_status_retweet_mutiple, status.metrics.retweet, ), ) @@ -152,7 +152,7 @@ fun DetailedStatusComponent( count = status.metrics.like, icon = painterResource(id = R.drawable.ic_heart), contentDescription = stringResource( - id = R.string.scene_status_like_multiple, + id = com.twidere.common.R.string.scene_status_like_multiple, status.metrics.like, ), ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt index 6d0386525..e8f456e8b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt @@ -129,24 +129,24 @@ fun MastodonPoll(status: UiStatus) { val countText = status.poll?.votersCount?.let { if (it > 1) { stringResource( - id = R.string.common_controls_status_poll_total_people, + id = com.twidere.common.R.string.common_controls_status_poll_total_people, it, ) } else { stringResource( - id = R.string.common_controls_status_poll_total_person, + id = com.twidere.common.R.string.common_controls_status_poll_total_person, it, ) } } ?: status.poll?.votesCount?.let { if (it > 1) { stringResource( - id = R.string.common_controls_status_poll_total_votes, + id = com.twidere.common.R.string.common_controls_status_poll_total_votes, it, ) } else { stringResource( - id = R.string.common_controls_status_poll_total_vote, + id = com.twidere.common.R.string.common_controls_status_poll_total_vote, it, ) } @@ -163,7 +163,7 @@ fun MastodonPoll(status: UiStatus) { } Spacer(modifier = Modifier.width(MastodonPollDefaults.VoteTimeSpacing)) if (status.poll?.expired == true) { - Text(text = stringResource(id = R.string.common_controls_status_poll_expired)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_poll_expired)) } else { Text(text = status.poll?.expiresAt?.humanizedTimestamp() ?: "") } @@ -178,7 +178,7 @@ fun MastodonPoll(status: UiStatus) { }, enabled = voteState.isNotEmpty(), ) { - Text(text = stringResource(id = R.string.common_controls_status_actions_vote)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_vote)) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 8ecc5297c..54fd51a00 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -79,7 +79,7 @@ fun ReplyButton( ) { val navigator = LocalNavigator.current val icon = painterResource(id = R.drawable.ic_corner_up_left) - val contentDescription = stringResource(id = R.string.accessibility_common_status_actions_reply) + val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_reply) val action = { navigator.compose(ComposeType.Reply, statusKey = status.statusKey) } @@ -124,7 +124,7 @@ fun LikeButton( } else { LocalContentColor.current } - val contentDescription = stringResource(id = R.string.accessibility_common_status_actions_like) + val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_like) val icon = painterResource(id = R.drawable.ic_heart) val action = { if (account != null) { @@ -174,7 +174,7 @@ fun RetweetButton( } val icon = painterResource(id = R.drawable.ic_repeat) val contentDescription = - stringResource(id = R.string.accessibility_common_status_actions_retweet) + stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_retweet) var expanded by remember { mutableStateOf(false) } val retweetAction = { if (status.platformType == PlatformType.Twitter) { @@ -200,7 +200,7 @@ fun RetweetButton( expanded = false } ) { - Text(text = stringResource(id = R.string.common_controls_status_actions_retweet)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_retweet)) } val navigator = LocalNavigator.current DropdownMenuItem( @@ -209,7 +209,7 @@ fun RetweetButton( } ) { Text( - text = stringResource(id = R.string.common_controls_status_actions_quote), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_quote), ) } } @@ -260,7 +260,7 @@ fun ShareButton( linkResolver = { data.resolveLink(it) }, ) val clipboardManager = LocalClipboardManager.current - val contentDescription = stringResource(id = R.string.accessibility_common_more) + val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_more) Box( modifier = modifier, ) { @@ -316,7 +316,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = R.string.common_controls_status_actions_copy_text), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_copy_text), ) } DropdownMenuItem( @@ -330,7 +330,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = R.string.common_controls_status_actions_copy_link), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_copy_link), ) } DropdownMenuItem( @@ -340,7 +340,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = R.string.common_controls_status_actions_share_link), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_share_link), ) } if (data.user.userKey == accountKey) { @@ -351,7 +351,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = R.string.common_controls_actions_remove), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt index ab04f0165..0244d6637 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt @@ -48,7 +48,7 @@ fun StatusThreadWithAvatar(modifier: Modifier = Modifier, data: UiStatus, onClic .height(StatusThreadDefaults.AvatarSize) ) { Text( - text = stringResource(id = R.string.common_controls_status_thread_show), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_thread_show), style = MaterialTheme.typography.body2 ) } @@ -62,7 +62,7 @@ fun StatusThreadTextOnly(modifier: Modifier = Modifier, onClick: () -> Unit) { onClick = onClick, ) { Text( - text = stringResource(id = R.string.common_controls_status_thread_show), + text = stringResource(id = com.twidere.common.R.string.common_controls_status_thread_show), style = MaterialTheme.typography.body2 ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 7670bd9a0..70c9d7a69 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -259,7 +259,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = R.string.common_notification_follow, + id = com.twidere.common.R.string.common_notification_follow, data.user.displayName ) ) @@ -279,7 +279,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = R.string.common_notification_follow_request, + id = com.twidere.common.R.string.common_notification_follow_request, data.user.displayName ) ) @@ -304,7 +304,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = R.string.common_notification_favourite, + id = com.twidere.common.R.string.common_notification_favourite, data.user.displayName ) ) @@ -325,11 +325,11 @@ private fun MastodonStatusHeader( val text = if (LocalActiveAccount.current?.let { it.accountKey == data.user.userKey } == true) { stringResource( - id = R.string.common_notification_own_poll, + id = com.twidere.common.R.string.common_notification_own_poll, ) } else { stringResource( - id = R.string.common_notification_poll, + id = com.twidere.common.R.string.common_notification_poll, ) } @@ -352,7 +352,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = R.string.common_notification_status, + id = com.twidere.common.R.string.common_notification_status, data.user.displayName ) ) @@ -619,7 +619,7 @@ fun ColumnScope.StatusBody( Icon( modifier = Modifier.size(16.dp), painter = painterResource(id = R.drawable.ic_map_pin), - contentDescription = stringResource(id = R.string.accessibility_common_status_location) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_location) ) Box(modifier = Modifier.width(StatusBodyDefaults.PlaceSpacing)) Text(text = status.geo.name) @@ -723,11 +723,11 @@ fun MediaPreviewButton( Icon( painter = painterResource(id = R.drawable.ic_photo), contentDescription = stringResource( - id = R.string.accessibility_common_status_media + id = com.twidere.common.R.string.accessibility_common_status_media ) ) Spacer(modifier = Modifier.width(MediaPreviewButtonDefaults.IconSpacing)) - Text(text = stringResource(id = R.string.common_controls_status_media)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_media)) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt index 57447c950..c6ff6cd21 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt @@ -50,7 +50,7 @@ fun RetweetHeader( icon = { Icon( painter = painterResource(id = R.drawable.ic_repeat), - contentDescription = stringResource(id = R.string.accessibility_common_status_retweeted), + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_retweeted), tint = Color(0xFF4C9EEB) ) }, @@ -58,7 +58,7 @@ fun RetweetHeader( Text( style = MaterialTheme.typography.caption, text = stringResource( - id = R.string.common_controls_status_user_retweeted, + id = com.twidere.common.R.string.common_controls_status_user_retweeted, data.user.displayName ), ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt b/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt index 9ba06ae8b..055c3ba52 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt @@ -61,7 +61,7 @@ fun MastodonTrendItem(trend: UiTrend, onClick: (UiTrend) -> Unit) { ListItem( modifier = Modifier.weight(1f), secondaryText = { - Text(text = stringResource(id = R.string.scene_trends_accounts, trend.dailyAccounts), style = MaterialTheme.typography.body2) + Text(text = stringResource(id = com.twidere.common.R.string.scene_trends_accounts, trend.dailyAccounts), style = MaterialTheme.typography.body2) }, text = { Text(text = trend.displayName, style = MaterialTheme.typography.subtitle1) diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt index 77f224893..adb595d60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt @@ -40,9 +40,9 @@ fun MastodonVisibility.icon(): Painter { @Composable fun MastodonVisibility.stringName(): String { return when (this) { - MastodonVisibility.Public -> stringResource(id = R.string.scene_compose_visibility_public) - MastodonVisibility.Unlisted -> stringResource(id = R.string.scene_compose_visibility_unlisted) - MastodonVisibility.Private -> stringResource(id = R.string.scene_compose_visibility_private) - MastodonVisibility.Direct -> stringResource(id = R.string.scene_compose_visibility_direct) + MastodonVisibility.Public -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_public) + MastodonVisibility.Unlisted -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_unlisted) + MastodonVisibility.Private -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_private) + MastodonVisibility.Direct -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_direct) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt index ef7cf3729..62c0eb963 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt @@ -49,6 +49,6 @@ class DownloadMediaJob( fileResolver.openOutputStream(target)?.use { service.download(target = source).copyTo(it) } ?: throw Error("Download failed") - inAppNotification.show(R.string.common_controls_actions_save) + inAppNotification.show(com.twidere.common.R.string.common_controls_actions_save) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index e047e459d..c76dd1972 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -88,7 +88,7 @@ class NotificationJob( PlatformType.Twitter -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_mentions, + com.twidere.common.R.string.common_notification_mentions, status.user.displayName ), htmlContent = status.htmlText, @@ -143,7 +143,7 @@ class NotificationJob( MastodonStatusType.NotificationFollow -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_follow, + com.twidere.common.R.string.common_notification_follow, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey), @@ -153,7 +153,7 @@ class NotificationJob( MastodonStatusType.NotificationFollowRequest -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_follow_request, + com.twidere.common.R.string.common_notification_follow_request, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey) @@ -162,7 +162,7 @@ class NotificationJob( MastodonStatusType.NotificationMention -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_mentions, + com.twidere.common.R.string.common_notification_mentions, actualStatus.user.displayName ), htmlContent = actualStatus.htmlText, @@ -173,7 +173,7 @@ class NotificationJob( MastodonStatusType.NotificationReblog -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_reblog, + com.twidere.common.R.string.common_notification_reblog, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), @@ -183,7 +183,7 @@ class NotificationJob( MastodonStatusType.NotificationFavourite -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_favourite, + com.twidere.common.R.string.common_notification_favourite, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), @@ -193,7 +193,7 @@ class NotificationJob( MastodonStatusType.NotificationPoll -> { NotificationData( title = applicationContext.getString( - R.string.common_notification_poll, + com.twidere.common.R.string.common_notification_poll, ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 07f50021a..36a06724e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -46,7 +46,7 @@ abstract class ComposeJob( suspend fun execute(composeData: ComposeData, accountKey: MicroBlogKey) { val builder = AppNotification .Builder(NotificationChannelSpec.BackgroundProgresses.id) - .setContentTitle(applicationContext.getString(R.string.common_alerts_tweet_sending_title)) + .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_sending_title)) .setOngoing(true) .setSilent(true) .setProgress(100, 0, false) @@ -81,7 +81,7 @@ abstract class ComposeJob( builder.setOngoing(false) .setProgress(0, 0, false) .setSilent(false) - .setContentTitle(applicationContext.getString(R.string.common_alerts_tweet_sent_title)) + .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_sent_title)) notificationManager.notifyTransient(notificationId, builder.build()) if (composeData.isThreadMode) { // open compose scene in thread mode @@ -95,7 +95,7 @@ abstract class ComposeJob( builder.setOngoing(false) .setProgress(0, 0, false) .setSilent(false) - .setContentTitle(applicationContext.getString(R.string.common_alerts_tweet_fail_title)) + .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_fail_title)) .setContentText(composeData.content) .setDeepLink(RootDeepLinksRoute.Draft(composeData.draftId)) notificationManager.notify(notificationId, builder.build()) diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt index 55206df52..e7539aedf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt @@ -64,8 +64,8 @@ class DirectMessageFetchJob( NotificationChannelSpec.ContentMessages.id ) ) - .setContentTitle(applicationContext.getString(R.string.common_notification_messages_title)) - .setContentText(applicationContext.getString(R.string.common_notification_messages_content, message.latestMessage.sender.displayName)) + .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_notification_messages_title)) + .setContentText(applicationContext.getString(com.twidere.common.R.string.common_notification_messages_content, message.latestMessage.sender.displayName)) .setDeepLink(RootDeepLinksRoute.Conversation(message.conversation.conversationKey)) notificationManager.notify(message.latestMessage.messageKey.hashCode(), builder.build()) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index 5204acfd9..8e9c38c85 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -86,7 +86,7 @@ abstract class DirectMessageSendJob( NotificationChannelSpec.ContentMessages.id ) ) - .setContentTitle(applicationContext.getString(R.string.common_alerts_failed_to_send_message_message)) + .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_failed_to_send_message_message)) .setContentText(sendData.text) .setDeepLink(RootDeepLinksRoute.Conversation(sendData.conversationKey)) notificationManager.notify(notificationId, builder.build()) diff --git a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index bfb822050..f90be6a93 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.notification import androidx.annotation.StringRes import androidx.core.app.NotificationManagerCompat -import com.twidere.twiderex.R enum class NotificationChannelSpec( val id: String, @@ -38,20 +37,20 @@ enum class NotificationChannelSpec( */ BackgroundProgresses( "background_progresses", - R.string.common_notification_channel_background_progresses_name, + com.twidere.common.R.string.common_notification_channel_background_progresses_name, importance = NotificationManagerCompat.IMPORTANCE_HIGH ), ContentInteractions( - "content_interactions", R.string.common_notification_channel_content_interactions_name, - descriptionRes = R.string.common_notification_channel_content_interactions_description, + "content_interactions", com.twidere.common.R.string.common_notification_channel_content_interactions_name, + descriptionRes = com.twidere.common.R.string.common_notification_channel_content_interactions_description, importance = NotificationManagerCompat.IMPORTANCE_HIGH, showBadge = true, grouped = true ), ContentMessages( "content_messages", - R.string.common_notification_channel_content_messages_name, - descriptionRes = R.string.common_notification_channel_content_messages_description, + com.twidere.common.R.string.common_notification_channel_content_messages_name, + descriptionRes = com.twidere.common.R.string.common_notification_channel_content_messages_description, importance = NotificationManagerCompat.IMPORTANCE_HIGH, showBadge = true, grouped = true diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index 62876791c..256da7abd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -63,7 +63,7 @@ fun DraftListScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_drafts_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_drafts_title)) } ) } @@ -102,7 +102,7 @@ fun DraftListSceneContent( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = R.string.accessibility_common_more + id = com.twidere.common.R.string.accessibility_common_more ) ) } @@ -115,7 +115,7 @@ fun DraftListSceneContent( navController.navigate(RootRoute.Draft.Compose(it.draftId)) } ) { - Text(text = stringResource(id = R.string.scene_drafts_actions_edit_draft)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_drafts_actions_edit_draft)) } DropdownMenuItem( onClick = { @@ -123,7 +123,7 @@ fun DraftListSceneContent( } ) { Text( - text = stringResource(id = R.string.common_controls_actions_remove), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index bab6ec1b5..e51968b2f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -459,7 +459,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { } ), text = { - Text(text = stringResource(id = R.string.scene_drawer_sign_in)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_drawer_sign_in)) } ) } @@ -472,7 +472,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { } ), text = { - Text(text = stringResource(id = R.string.scene_manage_accounts_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_manage_accounts_title)) } ) } @@ -521,12 +521,12 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { Icon( painter = painterResource(id = R.drawable.ic_adjustments_horizontal), contentDescription = stringResource( - id = R.string.scene_settings_title + id = com.twidere.common.R.string.scene_settings_title ) ) }, text = { - Text(text = stringResource(id = R.string.scene_settings_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_title)) } ) } @@ -602,7 +602,7 @@ private fun DrawerUserHeader( modifier = Modifier.rotate(rotate), imageVector = Icons.Default.ArrowDropDown, contentDescription = stringResource( - id = R.string.accessibility_scene_home_drawer_account_dropdown + id = com.twidere.common.R.string.accessibility_scene_home_drawer_account_dropdown ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 95655f4bc..9ba4eeda9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -278,7 +278,7 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM Icon( painter = painterResource(id = R.drawable.ic_x), contentDescription = stringResource( - id = R.string.accessibility_common_close + id = com.twidere.common.R.string.accessibility_common_close ) ) } @@ -340,7 +340,7 @@ private fun StatusMediaInfo( } ) { Text( - text = stringResource(id = R.string.common_controls_actions_save), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_save), ) } DropdownMenuItem( @@ -356,7 +356,7 @@ private fun StatusMediaInfo( } ) { Text( - text = stringResource(id = R.string.common_controls_actions_share_media), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_share_media), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index d4a17829e..a94e1facc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -241,7 +241,7 @@ fun PureMediaControlPanel( Icon( painter = painterResource(id = R.drawable.ic_x), contentDescription = stringResource( - id = R.string.accessibility_common_close + id = com.twidere.common.R.string.accessibility_common_close ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index 6f0168d30..eb14e35c9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -96,13 +96,13 @@ private fun MastodonSignIn() { Icon( painter = painterResource(id = R.drawable.ic_mastodon_logo_blue), contentDescription = stringResource( - id = R.string.accessibility_common_logo_mastodon + id = com.twidere.common.R.string.accessibility_common_logo_mastodon ) ) }, text = { Text( - text = stringResource(id = R.string.scene_sign_in_sign_in_with_mastodon) + text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon) ) }, trailing = { @@ -113,7 +113,7 @@ private fun MastodonSignIn() { Icon( imageVector = Icons.Default.KeyboardArrowRight, contentDescription = stringResource( - id = R.string.scene_sign_in_sign_in_with_mastodon + id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon ) ) } @@ -158,20 +158,20 @@ private fun TwitterSignIn() { Icon( painter = painterResource(id = R.drawable.ic_twitter_logo_white), contentDescription = stringResource( - id = R.string.accessibility_common_logo_twitter + id = com.twidere.common.R.string.accessibility_common_logo_twitter ) ) }, text = { Text( - text = stringResource(id = R.string.scene_sign_in_sign_in_with_twitter) + text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_twitter) ) }, trailing = { IconButton(onClick = { showKeyConfiguration = true }) { Icon( imageVector = Icons.Default.MoreHoriz, - contentDescription = stringResource(id = R.string.accessibility_common_more) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_more) ) } } @@ -192,11 +192,11 @@ private fun TwitterCustomKeySignIn( onDismissRequest.invoke() }, title = { - Text(text = stringResource(id = R.string.scene_sign_in_twitter_options_sign_in_with_custom_twitter_key)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_twitter_options_sign_in_with_custom_twitter_key)) }, text = { Column { - Text(text = stringResource(id = R.string.scene_sign_in_twitter_options_twitter_api_v2_access_is_required)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_twitter_options_twitter_api_v2_access_is_required)) OutlinedTextField( modifier = Modifier.fillMaxWidth(), value = apiKey, @@ -221,7 +221,7 @@ private fun TwitterCustomKeySignIn( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_cancel)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) } }, confirmButton = { @@ -244,7 +244,7 @@ private fun TwitterCustomKeySignIn( }, enabled = apiKey.isNotEmpty() && apiSecret.isNotEmpty() ) { - Text(text = stringResource(id = R.string.scene_drawer_sign_in)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_drawer_sign_in)) } }, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt index 92094a797..225dd50f3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt @@ -87,7 +87,7 @@ fun StatusScene( topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_search_tabs_tweets)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_search_tabs_tweets)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index 74d594780..4ad934b72 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -243,9 +243,9 @@ private fun ComposeBody( title = { Text( text = when (composeType) { - ComposeType.Reply -> stringResource(id = R.string.scene_compose_title_reply) - ComposeType.Quote -> stringResource(id = R.string.scene_compose_title_quote) - else -> stringResource(id = R.string.scene_compose_title_compose) + ComposeType.Reply -> stringResource(id = com.twidere.common.R.string.scene_compose_title_reply) + ComposeType.Quote -> stringResource(id = com.twidere.common.R.string.scene_compose_title_quote) + else -> stringResource(id = com.twidere.common.R.string.scene_compose_title_compose) } ) }, @@ -262,7 +262,7 @@ private fun ComposeBody( Icon( painter = painterResource(id = R.drawable.ic_x), contentDescription = stringResource( - id = R.string.accessibility_common_close + id = com.twidere.common.R.string.accessibility_common_close ) ) } @@ -279,7 +279,7 @@ private fun ComposeBody( Icon( painter = painterResource(id = if (enableThreadMode) R.drawable.ic_send_thread else R.drawable.ic_send), contentDescription = stringResource( - id = if (enableThreadMode) R.string.accessibility_scene_compose_thread else R.string.accessibility_scene_compose_send + id = if (enableThreadMode) com.twidere.common.R.string.accessibility_scene_compose_thread else com.twidere.common.R.string.accessibility_scene_compose_send ), tint = if (canSend) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) @@ -631,7 +631,7 @@ private fun LocationDisplay(it: Location) { Icon( painter = painterResource(id = R.drawable.ic_map_pin), contentDescription = stringResource( - id = R.string.accessibility_common_status_location + id = com.twidere.common.R.string.accessibility_common_status_location ) ) Text(text = "${it.latitude}, ${it.longitude}") @@ -775,13 +775,13 @@ private fun ReplySheetContent( Icon( painter = painterResource(id = R.drawable.ic_x), contentDescription = stringResource( - id = R.string.accessibility_common_close + id = com.twidere.common.R.string.accessibility_common_close ) ) } }, text = { - Text(text = stringResource(id = R.string.scene_compose_replying_to)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_replying_to)) } ) status?.let { @@ -810,7 +810,7 @@ private fun ReplySheetContent( } if (replyToUser.any()) { ListItem { - Text(text = stringResource(id = R.string.scene_compose_others_in_this_conversation)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_others_in_this_conversation)) } Divider(modifier = Modifier.padding(horizontal = 16.dp)) replyToUser.forEach { user -> @@ -865,18 +865,18 @@ private fun ConfirmDraftDialog( onDismissRequest = onDismiss, text = { Text( - text = stringResource(id = R.string.scene_compose_save_draft_message), + text = stringResource(id = com.twidere.common.R.string.scene_compose_save_draft_message), style = MaterialTheme.typography.body2 ) }, dismissButton = { TextButton(onClick = onCancel) { - Text(text = stringResource(id = R.string.common_controls_actions_cancel)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) } }, confirmButton = { TextButton(onClick = onConfirm) { - Text(text = stringResource(id = R.string.scene_compose_save_draft_action)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_save_draft_action)) } }, ) @@ -923,7 +923,7 @@ private fun ComposeInput( autoFocus = autoFocus, placeholder = { CompositionLocalProvider(LocalContentAlpha.provides(ContentAlpha.medium)) { - Text(text = stringResource(id = R.string.scene_compose_placeholder)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_placeholder)) } }, keyboardOptions = KeyboardOptions( @@ -973,7 +973,7 @@ private fun ColumnScope.MastodonContentWarningInput(viewModel: ComposeViewModel) CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.disabled, ) { - Text(text = stringResource(id = R.string.scene_compose_cw_placeholder)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_cw_placeholder)) } }, keyboardOptions = KeyboardOptions( @@ -1110,7 +1110,7 @@ private fun ComposeVote(voteState: VoteState) { modifier = Modifier.fillMaxWidth(), checked = multiple, onCheckedChange = { voteState.setMultiple(!multiple) }, - text = stringResource(id = R.string.scene_compose_vote_multiple) + text = stringResource(id = com.twidere.common.R.string.scene_compose_vote_multiple) ) } } @@ -1173,7 +1173,7 @@ private fun ComposeActions( Icon( painter = painterResource(id = R.drawable.ic_camera), contentDescription = stringResource( - id = R.string.accessibility_scene_compose_image + id = com.twidere.common.R.string.accessibility_scene_compose_image ) ) } @@ -1230,7 +1230,7 @@ private fun ComposeActions( Icon( painter = painterResource(id = R.drawable.ic_at_sign), contentDescription = stringResource( - id = R.string.accessibility_scene_compose_add_mention, + id = com.twidere.common.R.string.accessibility_scene_compose_add_mention, ) ) } @@ -1272,9 +1272,9 @@ private fun ComposeActions( painter = painterResource(id = R.drawable.ic_map_pin), contentDescription = stringResource( id = if (locationEnabled) { - R.string.accessibility_scene_compose_location_disable + com.twidere.common.R.string.accessibility_scene_compose_location_disable } else { - R.string.accessibility_scene_compose_location_enable + com.twidere.common.R.string.accessibility_scene_compose_location_enable } ) ) @@ -1287,7 +1287,7 @@ private fun ComposeActions( ) { Icon( painter = painterResource(id = R.drawable.ic_thread_mode), - contentDescription = stringResource(id = R.string.accessibility_scene_compose_thread), + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_compose_thread), tint = if (enableThreadMode) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) } @@ -1307,7 +1307,7 @@ private fun ComposeActions( R.drawable.ic_draft_number ), contentDescription = stringResource( - id = R.string.accessibility_scene_compose_draft + id = com.twidere.common.R.string.accessibility_scene_compose_draft ) ) if (draftCount.value < 9) { @@ -1365,7 +1365,7 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { } ) { Text( - text = stringResource(id = R.string.common_controls_actions_remove), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index 2e5e71764..b61c3cad3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -80,7 +80,7 @@ fun ComposeSearchHashtagScene() { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = R.string.scene_compose_hashtag_search_search_placeholder)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_hashtag_search_search_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -107,7 +107,7 @@ fun ComposeSearchHashtagScene() { Icon( imageVector = Icons.Default.Done, contentDescription = stringResource( - id = R.string.accessibility_common_done + id = com.twidere.common.R.string.accessibility_common_done ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index 8df5d5ca8..a79dd1fdf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -76,7 +76,7 @@ fun ComposeSearchUserScene() { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = R.string.scene_compose_user_search_search_placeholder)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_user_search_search_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -103,7 +103,7 @@ fun ComposeSearchUserScene() { Icon( imageVector = Icons.Default.Done, contentDescription = stringResource( - id = R.string.accessibility_common_done + id = com.twidere.common.R.string.accessibility_common_done ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index 82763e681..52ba14d85 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -54,7 +54,7 @@ fun DMConversationListScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_messages_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_title)) }, ) }, @@ -78,7 +78,7 @@ fun DMConversationListSceneFab() { Icon( painter = painterResource(id = R.drawable.ic_add), contentDescription = stringResource( - id = R.string.scene_lists_icons_create + id = com.twidere.common.R.string.scene_lists_icons_create ), ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index 0c1e39573..eabd41ebf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -112,7 +112,7 @@ fun DMConversationScene(conversationKey: MicroBlogKey) { }, ) { if (!account.supportDirectMessage) { - Text(text = stringResource(id = R.string.scene_messages_error_not_supported)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_error_not_supported)) } else { // user might enter this page by notifications after switch platform NormalContent(viewModel) @@ -130,7 +130,7 @@ fun NormalContent(viewModel: DMEventViewModel) { viewModel.inputImage.value = it }, ) - val copyText = stringResource(id = R.string.scene_messages_action_copy_text) + val copyText = stringResource(id = com.twidere.common.R.string.scene_messages_action_copy_text) val source = viewModel.source.collectAsLazyPagingItems() val input by viewModel.input.observeAsState(initial = "") val inputImage by viewModel.inputImage.observeAsState(null) @@ -214,7 +214,7 @@ fun MessageActionComponent( onDismissRequest() } ) { - Text(text = stringResource(id = R.string.scene_messages_action_copy_text)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_action_copy_text)) } ListItem( modifier = Modifier.clickable { @@ -222,7 +222,7 @@ fun MessageActionComponent( onDismissRequest() } ) { - Text(text = stringResource(id = R.string.scene_messages_action_delete)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_action_delete)) } } } @@ -262,7 +262,7 @@ fun InputPhotoPreview(inputImage: Uri?, onRemove: () -> Unit) { ) { Icon( painter = painterResource(id = R.drawable.ic_x), - contentDescription = stringResource(id = R.string.common_controls_actions_remove), + contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), tint = MaterialTheme.colors.surface, modifier = Modifier.padding(InputPhotoPreviewDefaults.IconPadding) ) @@ -303,7 +303,7 @@ fun InputComponent( Icon( painter = painterResource(id = R.drawable.ic_camera), contentDescription = stringResource( - id = R.string.accessibility_scene_compose_image + id = com.twidere.common.R.string.accessibility_scene_compose_image ) ) } @@ -323,7 +323,7 @@ fun InputComponent( Icon( painter = painterResource(id = R.drawable.ic_send), contentDescription = stringResource( - id = R.string.accessibility_scene_compose_send + id = com.twidere.common.R.string.accessibility_scene_compose_send ), tint = if (enableSend) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index b3ad7718d..ed26c0322 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -83,7 +83,7 @@ fun DMNewConversationScene() { ) }, title = { - Text(text = stringResource(id = R.string.scene_messages_new_conversation_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_new_conversation_title)) }, elevation = 0.dp ) @@ -124,7 +124,7 @@ fun SearchInput(modifier: Modifier = Modifier, input: String, onValueChanged: (v Icon( painter = painterResource(id = R.drawable.ic_search), contentDescription = stringResource( - id = R.string.scene_search_title + id = com.twidere.common.R.string.scene_search_title ) ) Spacer(modifier = Modifier.width(SearchInputDefaults.ContentSpacing)) @@ -132,7 +132,7 @@ fun SearchInput(modifier: Modifier = Modifier, input: String, onValueChanged: (v value = input, onValueChange = onValueChanged, modifier = Modifier.weight(1f), placeholder = { - Text(text = stringResource(id = R.string.scene_messages_new_conversation_search)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_new_conversation_search)) }, maxLines = 1 ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt index 84d4f8a2c..31808d578 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt @@ -40,7 +40,7 @@ import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel class AllNotificationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_notification_tabs_all) + return stringResource(id = com.twidere.common.R.string.scene_notification_tabs_all) } override val route: String @@ -68,7 +68,7 @@ fun AllNotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_notification_tabs_all)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_notification_tabs_all)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt index ed0888bef..39b74d335 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.scenes.dm.DMConversationListSceneFab class DMConversationListItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_messages_title) + return stringResource(id = com.twidere.common.R.string.scene_messages_title) } override val route: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt index 5abb0db1e..58200bf5f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.scenes.DraftListSceneContent class DraftNavigationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_drafts_title) + return stringResource(id = com.twidere.common.R.string.scene_drafts_title) } override val route: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 7898a0bc3..3e585185f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -46,7 +46,7 @@ import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel class HomeTimelineItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(R.string.scene_timeline_title) + override fun name(): String = stringResource(com.twidere.common.R.string.scene_timeline_title) override val route: String get() = RootRoute.HomeTimeline @@ -76,7 +76,7 @@ fun HomeTimelineScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_timeline_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_timeline_title)) }, navigationIcon = { AppBarNavigationButton() @@ -103,7 +103,7 @@ private fun HomeTimelineFab() { Icon( painter = painterResource(id = R.drawable.ic_feather), contentDescription = stringResource( - id = R.string.accessibility_scene_home_compose + id = com.twidere.common.R.string.accessibility_scene_home_compose ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt index 07975af2c..50f2c4410 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.scenes.lists.ListsSceneFab class ListsNavigationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_lists_title) + return stringResource(id = com.twidere.common.R.string.scene_lists_title) } override val route: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index 90ffee4fd..fd70457ff 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.ui.TwidereScene class MeItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(R.string.scene_profile_title) + override fun name(): String = stringResource(com.twidere.common.R.string.scene_profile_title) override val route: String get() = RootRoute.Me @@ -60,7 +60,7 @@ fun MeScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_profile_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index b7858c36d..a528862c3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -39,7 +39,7 @@ import com.twidere.twiderex.viewmodel.timeline.MentionsTimelineViewModel class MentionItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(R.string.scene_mentions_title) + override fun name(): String = stringResource(com.twidere.common.R.string.scene_mentions_title) override val route: String get() = RootRoute.Mentions @@ -69,7 +69,7 @@ fun MentionScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_mentions_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_mentions_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index e8eea74c8..a3120c343 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -40,7 +40,7 @@ import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel class NotificationItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(R.string.scene_notification_title) + override fun name(): String = stringResource(com.twidere.common.R.string.scene_notification_title) override val route: String get() = RootRoute.Notification @@ -62,7 +62,7 @@ fun NotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_mentions_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_mentions_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index 0392f21a6..a57ddce68 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -67,7 +67,7 @@ import com.twidere.twiderex.viewmodel.trend.TrendViewModel class SearchItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(R.string.scene_search_title) + override fun name(): String = stringResource(com.twidere.common.R.string.scene_search_title) override val route: String get() = RootRoute.Search.Home @@ -90,7 +90,7 @@ fun SearchScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_search_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_search_title)) }, navigationIcon = { AppBarNavigationButton() @@ -144,7 +144,7 @@ fun SearchSceneContent() { modifier = Modifier .weight(1F) .align(Alignment.CenterVertically), - text = stringResource(id = R.string.scene_search_search_bar_placeholder), + text = stringResource(id = com.twidere.common.R.string.scene_search_search_bar_placeholder), ) } IconButton( @@ -155,7 +155,7 @@ fun SearchSceneContent() { Icon( painter = painterResource(id = R.drawable.ic_search), contentDescription = stringResource( - id = R.string.scene_search_title + id = com.twidere.common.R.string.scene_search_title ) ) } @@ -169,7 +169,7 @@ fun SearchSceneContent() { item { if (source.isNotEmpty()) ListItem { Text( - text = stringResource(id = R.string.scene_search_saved_search), + text = stringResource(id = com.twidere.common.R.string.scene_search_saved_search), style = MaterialTheme.typography.button ) } @@ -191,7 +191,7 @@ fun SearchSceneContent() { Icon( painter = painterResource(id = R.drawable.ic_trash_can), contentDescription = stringResource( - id = R.string.common_controls_actions_remove + id = com.twidere.common.R.string.common_controls_actions_remove ) ) } @@ -211,7 +211,7 @@ fun SearchSceneContent() { } ) { Text( - text = if (expandSearch) stringResource(id = R.string.scene_search_show_less) else stringResource(id = R.string.scene_search_show_more), + text = if (expandSearch) stringResource(id = com.twidere.common.R.string.scene_search_show_less) else stringResource(id = com.twidere.common.R.string.scene_search_show_more), style = MaterialTheme.typography.subtitle1, color = MaterialTheme.colors.primary ) @@ -225,14 +225,14 @@ fun SearchSceneContent() { when (account.type) { PlatformType.Twitter -> Text( - text = stringResource(id = R.string.scene_trends_world_wide), + text = stringResource(id = com.twidere.common.R.string.scene_trends_world_wide), style = MaterialTheme.typography.button ) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> Text( - text = stringResource(id = R.string.scene_trends_world_wide), + text = stringResource(id = com.twidere.common.R.string.scene_trends_world_wide), style = MaterialTheme.typography.button ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index f6c61aed7..ea3b185ae 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -42,7 +42,7 @@ import com.twidere.twiderex.viewmodel.timeline.mastodon.FederatedTimelineViewMod class FederatedTimelineItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_federated_title) + return stringResource(id = com.twidere.common.R.string.scene_federated_title) } override val route: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index 119a72742..1ce101ac0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -42,7 +42,7 @@ import com.twidere.twiderex.viewmodel.timeline.mastodon.LocalTimelineViewModel class LocalTimelineItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_local_title) + return stringResource(id = com.twidere.common.R.string.scene_local_title) } override val route: String diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 25ebfe809..044684ebf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.launch class MastodonNotificationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = R.string.scene_notification_title) + return stringResource(id = com.twidere.common.R.string.scene_notification_title) } override val route: String @@ -79,7 +79,7 @@ fun MastodonNotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_notification_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_notification_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index 71dd62931..d637c9f5f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -114,12 +114,12 @@ fun ListsAddMembersScene( ) { Icon( imageVector = Icons.Default.Close, - contentDescription = stringResource(id = R.string.accessibility_common_back) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_back) ) } }, title = { - Text(text = stringResource(id = R.string.scene_lists_users_add_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_title)) }, elevation = 0.dp, ) @@ -129,13 +129,13 @@ fun ListsAddMembersScene( ) { Icon( painter = painterResource(id = R.drawable.ic_search), - contentDescription = stringResource(id = R.string.scene_search_title), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_search_title), modifier = Modifier.padding(ListsAddMembersSceneDefaults.SearchInput.Icon.Padding) ) TextInput( value = keyword, placeholder = { - Text(text = stringResource(id = R.string.scene_lists_users_add_search)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_search)) }, onValueChange = { searchViewModel.text.value = it @@ -188,13 +188,13 @@ private fun SearchResultsContent(source: LazyPagingItems, onAction: (use val pending = statusChecker(it) if (pending) { Text( - text = stringResource(id = R.string.scene_lists_users_menu_actions_remove), + text = stringResource(id = com.twidere.common.R.string.scene_lists_users_menu_actions_remove), style = MaterialTheme.typography.button, color = Color(0xFFFF3B30), ) } else { Text( - text = stringResource(id = R.string.scene_lists_users_menu_actions_add), + text = stringResource(id = com.twidere.common.R.string.scene_lists_users_menu_actions_add), style = MaterialTheme.typography.button, color = MaterialTheme.colors.primary, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index bbe4c4f26..62fadc979 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -87,7 +87,7 @@ fun ListsMembersScene( AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_lists_details_tabs_members)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_members)) } ) }, @@ -107,12 +107,12 @@ fun ListsMembersScene( Icon( painter = painterResource(id = R.drawable.ic_add), contentDescription = stringResource( - id = R.string.scene_lists_details_add_members + id = com.twidere.common.R.string.scene_lists_details_add_members ), modifier = Modifier.padding(ListsMembersSceneDefaults.Fab.IconPadding) ) Text( - text = stringResource(id = R.string.scene_lists_users_add_title) + text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_title) .uppercase(Locale.getDefault()), style = MaterialTheme.typography.button ) @@ -138,7 +138,7 @@ fun ListsMembersScene( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = R.string.scene_lists_users_menu_actions_remove + id = com.twidere.common.R.string.scene_lists_users_menu_actions_remove ) ) } @@ -153,7 +153,7 @@ fun ListsMembersScene( ) { Text( text = stringResource( - R.string.scene_lists_users_menu_actions_remove + com.twidere.common.R.string.scene_lists_users_menu_actions_remove ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index 48d6f0f92..99396c72b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -61,7 +61,7 @@ fun ListsScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_lists_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_title)) }, ) }, @@ -96,12 +96,12 @@ fun ListsSceneFab() { Icon( painter = painterResource(id = R.drawable.ic_add), contentDescription = stringResource( - id = R.string.scene_lists_icons_create + id = com.twidere.common.R.string.scene_lists_icons_create ), modifier = Modifier.padding(ListsSceneDefaults.Fab.IconPadding) ) Text( - text = stringResource(id = R.string.scene_lists_modify_create_title) + text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_create_title) .uppercase(Locale.getDefault()), style = MaterialTheme.typography.button ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt index fb75c284a..eaa01fde4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt @@ -52,7 +52,7 @@ fun ListsSubscribersScene( AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_lists_details_tabs_subscriber)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_subscriber)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index 3c975a342..46cbefafd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -101,14 +101,14 @@ fun ListTimeLineScene( title = { Row(verticalAlignment = Alignment.CenterVertically) { Text( - text = source?.title ?: stringResource(id = R.string.scene_lists_details_title), + text = source?.title ?: stringResource(id = com.twidere.common.R.string.scene_lists_details_title), maxLines = 1, overflow = TextOverflow.Ellipsis ) if (source?.isPrivate == true) Icon( painter = painterResource(id = R.drawable.ic_lock), - contentDescription = stringResource(id = R.string.scene_lists_icons_private), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_lists_icons_private), modifier = Modifier .alpha(ContentAlpha.disabled) .padding(start = ListTimelineSceneDefaults.LockIconPadding) @@ -124,7 +124,7 @@ fun ListTimeLineScene( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = R.string.scene_lists_details_menu_actions_edit_list + id = com.twidere.common.R.string.scene_lists_details_menu_actions_edit_list ) ) } @@ -148,9 +148,9 @@ fun ListTimeLineScene( Text( text = stringResource( id = if (following) - R.string.scene_lists_details_menu_actions_unfollow + com.twidere.common.R.string.scene_lists_details_menu_actions_unfollow else - R.string.scene_lists_details_menu_actions_follow + com.twidere.common.R.string.scene_lists_details_menu_actions_follow ) ) } @@ -167,7 +167,7 @@ fun ListTimeLineScene( ) } ) { - Text(text = stringResource(id = R.string.scene_lists_details_tabs_members)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_members)) } if (uiList.allowToSubscribe) { @@ -181,7 +181,7 @@ fun ListTimeLineScene( ) } ) { - Text(text = stringResource(id = R.string.scene_lists_details_tabs_subscriber)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_subscriber)) } } @@ -199,9 +199,9 @@ fun ListTimeLineScene( ) { Text( text = if (account.type == PlatformType.Mastodon) - stringResource(id = R.string.scene_lists_details_menu_actions_rename_list) + stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_rename_list) else - stringResource(id = R.string.scene_lists_details_menu_actions_edit_list) + stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_edit_list) ) } } @@ -213,7 +213,7 @@ fun ListTimeLineScene( showDeleteConfirmDialog = true } ) { - Text(text = stringResource(id = R.string.scene_lists_details_menu_actions_delete_list)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_delete_list)) } } } @@ -282,7 +282,7 @@ private fun ListDeleteConfirmDialog(title: String, onDismissRequest: () -> Unit, }, title = { Text( - text = stringResource(id = R.string.scene_lists_details_delete_list_title, title), + text = stringResource(id = com.twidere.common.R.string.scene_lists_details_delete_list_title, title), style = MaterialTheme.typography.subtitle1 ) }, @@ -297,7 +297,7 @@ private fun ListDeleteConfirmDialog(title: String, onDismissRequest: () -> Unit, onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_cancel)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) } }, confirmButton = { @@ -307,7 +307,7 @@ private fun ListDeleteConfirmDialog(title: String, onDismissRequest: () -> Unit, onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_yes)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_yes)) } }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index b0d3316b8..9cab38831 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -81,7 +81,7 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { if (showMastodonComponent) { MastodonListsModifyComponent( onDismissRequest = { dismiss() }, - title = stringResource(id = R.string.scene_lists_modify_dialog_create), + title = stringResource(id = com.twidere.common.R.string.scene_lists_modify_dialog_create), name = name, onNameChanged = { name = it } ) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt index 088e27d0a..6bdb9574d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt @@ -71,7 +71,7 @@ fun MastodonListsEditDialog(listKey: MicroBlogKey, onDismissRequest: () -> Unit) } MastodonListsModifyComponent( onDismissRequest = { dismiss() }, - title = stringResource(id = R.string.scene_lists_modify_dialog_edit), + title = stringResource(id = com.twidere.common.R.string.scene_lists_modify_dialog_edit), name = name, onNameChanged = { name = it } ) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index 67fb115f7..a34435421 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -88,7 +88,7 @@ fun TwitterListsCreateScene() { AppBar( navigationIcon = { AppBarNavigationButton(Icons.Default.Close) }, title = { - Text(text = stringResource(id = R.string.scene_lists_modify_create_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_create_title)) }, actions = { IconButton( @@ -103,7 +103,7 @@ fun TwitterListsCreateScene() { ) { Icon( imageVector = Icons.Default.Done, - contentDescription = stringResource(id = R.string.common_controls_actions_confirm), + contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt index 7bc362eea..1f0c67a41 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt @@ -71,7 +71,7 @@ fun TwitterListsEditScene( AppBar( navigationIcon = { AppBarNavigationButton(Icons.Default.Close) }, title = { - Text(text = stringResource(id = R.string.scene_lists_modify_edit_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_edit_title)) }, actions = { IconButton( @@ -89,7 +89,7 @@ fun TwitterListsEditScene( ) { Icon( imageVector = Icons.Default.Done, - contentDescription = stringResource(id = R.string.common_controls_actions_confirm), + contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt index 4636263b5..f65c73af3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt @@ -108,12 +108,12 @@ fun MastodonSignInScene() { Icon( painter = painterResource(id = R.drawable.ic_mastodon_logo_white), contentDescription = stringResource( - id = R.string.accessibility_common_logo_mastodon + id = com.twidere.common.R.string.accessibility_common_logo_mastodon ) ) }, text = { - Text(text = stringResource(id = R.string.scene_sign_in_sign_in_with_mastodon)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon)) }, trailing = { IconButton( @@ -123,7 +123,7 @@ fun MastodonSignInScene() { Icon( imageVector = Icons.Default.KeyboardArrowRight, contentDescription = stringResource( - id = R.string.scene_sign_in_sign_in_with_mastodon + id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index 9cad8bc67..41d3feaaf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -110,7 +110,7 @@ fun SearchInputScene(initial: String? = null) { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = R.string.scene_search_search_bar_placeholder)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_search_search_bar_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -140,7 +140,7 @@ fun SearchInputScene(initial: String? = null) { Icon( painter = painterResource(id = R.drawable.ic_search), contentDescription = stringResource( - id = R.string.scene_search_title + id = com.twidere.common.R.string.scene_search_title ) ) } @@ -161,7 +161,7 @@ fun SearchInputScene(initial: String? = null) { Icon( imageVector = Icons.Default.History, contentDescription = stringResource( - id = R.string.accessibility_scene_search_history + id = com.twidere.common.R.string.accessibility_scene_search_history ) ) }, @@ -174,7 +174,7 @@ fun SearchInputScene(initial: String? = null) { Icon( painter = painterResource(id = R.drawable.ic_x), contentDescription = stringResource( - id = R.string.common_controls_actions_remove + id = com.twidere.common.R.string.common_controls_actions_remove ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt index fe6b5e7ab..eb3bb1295 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt @@ -149,7 +149,7 @@ fun SearchScene(keyword: String) { Icon( painter = painterResource(id = R.drawable.ic_device_floppy), contentDescription = stringResource( - id = R.string.accessibility_scene_search_save + id = com.twidere.common.R.string.accessibility_scene_search_save ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt index 680820ea3..83ca6dbd9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt @@ -42,7 +42,7 @@ import com.twidere.twiderex.viewmodel.mastodon.MastodonSearchHashtagViewModel class MastodonSearchHashtagItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = R.string.scene_search_tabs_hashtag) + return stringResource(id = com.twidere.common.R.string.scene_search_tabs_hashtag) } @OptIn(ExperimentalMaterialApi::class) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt index 311c3dc77..657f82a66 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt @@ -35,7 +35,7 @@ import com.twidere.twiderex.viewmodel.search.SearchTweetsViewModel class SearchTweetsItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = R.string.scene_search_tabs_tweets) + return stringResource(id = com.twidere.common.R.string.scene_search_tabs_tweets) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt index ec3ac3fb5..42c16c1e5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt @@ -36,7 +36,7 @@ import com.twidere.twiderex.viewmodel.search.SearchUserViewModel class SearchUserItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = R.string.scene_search_tabs_users) + return stringResource(id = com.twidere.common.R.string.scene_search_tabs_users) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index 13b78ea80..72d98566d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -38,7 +38,7 @@ import com.twidere.twiderex.viewmodel.twitter.search.TwitterSearchMediaViewModel class TwitterSearchMediaItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = R.string.scene_search_tabs_media) + return stringResource(id = com.twidere.common.R.string.scene_search_tabs_media) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt index 6b870f45b..14c025299 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt @@ -77,7 +77,7 @@ fun AboutScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_about_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_about_title)) } ) } @@ -117,7 +117,7 @@ private fun AboutContent() { backContent = { BlurImage( resource = R.drawable.ic_about_gray_logo_shadow, - contentDescription = stringResource(id = R.string.scene_settings_about_logo_background_shadow), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_settings_about_logo_background_shadow), modifier = Modifier .aspectRatio(aspectRatio) .fillMaxHeight() @@ -131,7 +131,7 @@ private fun AboutContent() { ) { Image( painter = grayLogoPainter, - contentDescription = stringResource(id = R.string.scene_settings_about_logo_background), + contentDescription = stringResource(id = com.twidere.common.R.string.scene_settings_about_logo_background), modifier = Modifier .aspectRatio(aspectRatio) .fillMaxHeight() @@ -169,14 +169,14 @@ private fun AboutContent() { ) { Text( text = stringResource( - id = R.string.scene_settings_about_version, + id = com.twidere.common.R.string.scene_settings_about_version, BuildConfig.VERSION_NAME ), ) Box(modifier = Modifier.height(AboutContentDefaults.VersionName.Spacing)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( - text = stringResource(id = R.string.scene_settings_about_description), + text = stringResource(id = com.twidere.common.R.string.scene_settings_about_description), style = MaterialTheme.typography.body2, ) } @@ -199,7 +199,7 @@ private fun AboutContent() { Icon( painter = painterResource(id = R.drawable.ic_twitter), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = R.string.accessibility_common_logo_twitter) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twitter) ) } Box(modifier = Modifier.width(AboutContentDefaults.Icon.Spacing)) @@ -211,7 +211,7 @@ private fun AboutContent() { Icon( painter = painterResource(id = R.drawable.ic_github), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = R.string.accessibility_common_logo_github) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_github) ) } Box(modifier = Modifier.width(AboutContentDefaults.Icon.Spacing)) @@ -223,7 +223,7 @@ private fun AboutContent() { Icon( painter = painterResource(id = R.drawable.ic_telegram), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = R.string.accessibility_common_logo_github) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_github) ) } } @@ -240,7 +240,7 @@ private fun AboutContent() { .width(IntrinsicSize.Max) ) { Text( - text = stringResource(id = R.string.scene_settings_about_license), + text = stringResource(id = com.twidere.common.R.string.scene_settings_about_license), style = MaterialTheme.typography.body1, modifier = Modifier .padding(AboutContentDefaults.License.TextPadding) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt index 9d5f92972..872f0f210 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt @@ -64,7 +64,7 @@ fun AccountManagementScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_manage_accounts_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_manage_accounts_title)) }, actions = { val navController = LocalNavController.current @@ -76,7 +76,7 @@ fun AccountManagementScene() { Icon( imageVector = Icons.Default.Add, contentDescription = stringResource( - id = R.string.accessibility_scene_manage_accounts_add + id = com.twidere.common.R.string.accessibility_scene_manage_accounts_add ) ) } @@ -113,7 +113,7 @@ fun AccountManagementScene() { Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = R.string.accessibility_common_more + id = com.twidere.common.R.string.accessibility_common_more ) ) } @@ -127,7 +127,7 @@ fun AccountManagementScene() { }, ) { Text( - text = stringResource(id = R.string.common_controls_actions_remove), + text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt index eec3d1cca..e6c369f60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt @@ -72,7 +72,7 @@ fun AccountNotificationScene( topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_settings_notification_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index 168d65940..ac8ae9684 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -88,7 +88,7 @@ fun AppearanceScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_title)) }, ) } @@ -114,7 +114,7 @@ fun AppearanceScene() { } ), text = { - Text(text = stringResource(id = R.string.scene_settings_appearance_highlight_color)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_highlight_color)) }, trailing = { Box( @@ -139,14 +139,14 @@ fun AppearanceScene() { viewModel.setTabPosition(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_section_header_tab_position)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_tab_position)) }, itemContent = { Text( text = stringResource( arrayOf( - R.string.scene_settings_appearance_tab_position_top, - R.string.scene_settings_appearance_tab_position_bottom + com.twidere.common.R.string.scene_settings_appearance_tab_position_top, + com.twidere.common.R.string.scene_settings_appearance_tab_position_bottom )[it.ordinal] ) ) @@ -155,7 +155,7 @@ fun AppearanceScene() { ItemDivider() // Scrolling Timeline ItemHeader() { - Text(text = stringResource(id = R.string.scene_settings_appearance_section_header_scrolling_timeline)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_scrolling_timeline)) } switchItem( value = appearance.hideTabBarWhenScroll, @@ -163,7 +163,7 @@ fun AppearanceScene() { viewModel.setHideTabBarWhenScrolling(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_scrolling_timeline_tab_bar)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_tab_bar)) }, ) switchItem( @@ -172,7 +172,7 @@ fun AppearanceScene() { viewModel.setHideAppBarWhenScrolling(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_scrolling_timeline_app_bar)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_app_bar)) }, ) switchItem( @@ -181,7 +181,7 @@ fun AppearanceScene() { viewModel.setHideFabWhenScrolling(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_scrolling_timeline_fab)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_fab)) }, ) ItemDivider() @@ -196,15 +196,15 @@ fun AppearanceScene() { viewModel.setTheme(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_section_header_theme)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_theme)) }, itemContent = { Text( text = stringResource( arrayOf( - R.string.scene_settings_appearance_theme_auto, - R.string.scene_settings_appearance_theme_light, - R.string.scene_settings_appearance_theme_dark, + com.twidere.common.R.string.scene_settings_appearance_theme_auto, + com.twidere.common.R.string.scene_settings_appearance_theme_light, + com.twidere.common.R.string.scene_settings_appearance_theme_dark, )[it.ordinal] ) ) @@ -218,7 +218,7 @@ fun AppearanceScene() { viewModel.setIsDarkModePureBlack(it) }, ) { - Text(text = stringResource(id = R.string.scene_settings_appearance_AMOLED_optimized_mode)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_AMOLED_optimized_mode)) } } } @@ -241,7 +241,7 @@ fun PrimaryColorDialog( AlertDialog( onDismissRequest = onDismiss, title = { - Text(text = stringResource(id = R.string.scene_settings_appearance_pick_color)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_pick_color)) }, text = { Row( @@ -278,7 +278,7 @@ fun PrimaryColorDialog( }, confirmButton = { TextButton(onClick = onDismiss) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } } ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 77d84fce4..faacc4480 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -75,7 +75,7 @@ fun DisplayScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_title)) } ) } @@ -87,7 +87,7 @@ fun DisplayScene() { ) ) { ItemHeader() { - Text(text = stringResource(id = R.string.scene_settings_display_section_header_preview)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_preview)) } CompositionLocalProvider( LocalNavigator provides FakeNavigator, @@ -97,7 +97,7 @@ fun DisplayScene() { } ItemDivider() ItemHeader() { - Text(text = stringResource(id = R.string.scene_settings_display_section_header_text)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_text)) } switchItem( value = display.useSystemFontSize, @@ -105,7 +105,7 @@ fun DisplayScene() { viewModel.setUseSystemFontSize(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_text_use_the_system_font_size)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_text_use_the_system_font_size)) }, ) if (!display.useSystemFontSize) { @@ -114,7 +114,7 @@ fun DisplayScene() { Icon( modifier = Modifier.size(12.dp), imageVector = Icons.Default.TextFields, - contentDescription = stringResource(id = R.string.accessibility_scene_settings_display_font_size) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_settings_display_font_size) ) }, text = { @@ -132,7 +132,7 @@ fun DisplayScene() { trailing = { Icon( imageVector = Icons.Default.TextFields, - contentDescription = stringResource(id = R.string.accessibility_scene_settings_display_font_size) + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_settings_display_font_size) ) } ) @@ -148,14 +148,14 @@ fun DisplayScene() { viewModel.setAvatarStyle(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_text_avatar_style)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_text_avatar_style)) }, itemContent = { Text( text = stringResource( arrayOf( - R.string.scene_settings_display_text_circle, - R.string.scene_settings_display_text_rounded_square, + com.twidere.common.R.string.scene_settings_display_text_circle, + com.twidere.common.R.string.scene_settings_display_text_rounded_square, )[it.ordinal] ) ) @@ -163,7 +163,7 @@ fun DisplayScene() { ) ItemDivider() ItemHeader() { - Text(text = stringResource(id = R.string.scene_settings_display_section_header_media)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_media)) } switchItem( value = display.urlPreview, @@ -171,7 +171,7 @@ fun DisplayScene() { viewModel.setUrlPreview(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_url_preview)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_url_preview)) } ) switchItem( @@ -180,7 +180,7 @@ fun DisplayScene() { viewModel.setMediaPreview(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_media_media_previews)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_media_media_previews)) } ) if (display.mediaPreview) { @@ -195,15 +195,15 @@ fun DisplayScene() { viewModel.setAutoPlayback(it) }, title = { - Text(text = stringResource(id = R.string.scene_settings_display_media_auto_playback)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_media_auto_playback)) }, itemContent = { Text( text = stringResource( arrayOf( - R.string.scene_settings_display_media_automatic, - R.string.scene_settings_display_media_always, - R.string.scene_settings_display_media_off, + com.twidere.common.R.string.scene_settings_display_media_automatic, + com.twidere.common.R.string.scene_settings_display_media_always, + com.twidere.common.R.string.scene_settings_display_media_off, )[it.ordinal] ) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index 27ce43577..1e7d48775 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -105,7 +105,7 @@ fun LayoutScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_layout_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_title)) } ) } @@ -128,10 +128,10 @@ fun LayoutScene() { } ListItem( text = { - Text(text = stringResource(id = R.string.scene_settings_layout_desc_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_desc_title)) }, secondaryText = { - Text(text = stringResource(id = R.string.scene_settings_layout_desc_content)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_desc_content)) } ) ReorderableColumn( @@ -171,9 +171,9 @@ private fun LayoutItemContent( ItemHeader { Text( text = if (it) { - stringResource(id = R.string.scene_settings_layout_actions_tabbar) + stringResource(id = com.twidere.common.R.string.scene_settings_layout_actions_tabbar) } else { - stringResource(id = R.string.scene_settings_layout_actions_drawer) + stringResource(id = com.twidere.common.R.string.scene_settings_layout_actions_drawer) } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index c8beb6e98..6825517a1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -82,7 +82,7 @@ fun MiscScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_misc_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_title)) } ) } @@ -156,7 +156,7 @@ fun ColumnScope.ProxyPreference( val proxyPassword by viewModel.proxyPassword.observeAsState("") ItemHeader { - Text(text = stringResource(id = R.string.scene_settings_misc_proxy_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_title)) } switchItem( value = useProxy, @@ -164,21 +164,21 @@ fun ColumnScope.ProxyPreference( viewModel.setUseProxy(it) }, describe = { - Text(text = stringResource(id = R.string.scene_settings_misc_proxy_enable_description)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_enable_description)) } ) { - Text(text = stringResource(id = R.string.scene_settings_misc_proxy_enable_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_enable_title)) } ItemProxy( enable = useProxy, - title = stringResource(id = R.string.scene_settings_misc_proxy_type_title), + title = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_title), content = proxyTypeValue(type = proxyType), onClick = { showProxyTypeDialog.value = true } ) - val serverTitle = stringResource(id = R.string.scene_settings_misc_proxy_server) + val serverTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_server) ItemProxy( enable = useProxy, title = serverTitle, @@ -193,7 +193,7 @@ fun ColumnScope.ProxyPreference( } ) - val portTitle = stringResource(id = R.string.scene_settings_misc_proxy_port_title) + val portTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_port_title) ItemProxy( enable = useProxy, title = portTitle, @@ -208,7 +208,7 @@ fun ColumnScope.ProxyPreference( } ) - val userNameTitle = stringResource(id = R.string.scene_settings_misc_proxy_username) + val userNameTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_username) ItemProxy( enable = useProxy, title = userNameTitle, @@ -223,7 +223,7 @@ fun ColumnScope.ProxyPreference( } ) - val passwordTitle = stringResource(id = R.string.scene_settings_misc_proxy_password) + val passwordTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_password) ItemProxy( enable = useProxy, title = passwordTitle, @@ -242,9 +242,9 @@ fun ColumnScope.ProxyPreference( @Composable fun proxyTypeValue(type: MiscPreferences.ProxyType): String { return when (type) { - MiscPreferences.ProxyType.HTTP -> stringResource(id = R.string.scene_settings_misc_proxy_type_http) - MiscPreferences.ProxyType.REVERSE -> stringResource(id = R.string.scene_settings_misc_proxy_type_reverse) - else -> stringResource(id = R.string.scene_settings_misc_proxy_type_http) + MiscPreferences.ProxyType.HTTP -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_http) + MiscPreferences.ProxyType.REVERSE -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_reverse) + else -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_http) } } @@ -314,7 +314,7 @@ fun NitterPreference(viewModel: MiscViewModel) { } } ) { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_title)) } ListItem( text = { @@ -323,7 +323,7 @@ fun NitterPreference(viewModel: MiscViewModel) { onValueChange = { viewModel.setNitterInstance(it) }, placeholder = { Text( - text = stringResource(id = R.string.scene_settings_misc_nitter_input_placeholder) + text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_input_placeholder) ) }, trailingIcon = { @@ -341,7 +341,7 @@ fun NitterPreference(viewModel: MiscViewModel) { ) }, secondaryText = { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_input_description)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_input_description)) } ) } @@ -354,14 +354,14 @@ fun NitterUsageDialog( AlertDialog( onDismissRequest = onDismissRequest, title = { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_dialog_usage_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_title)) }, text = { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_dialog_usage_content)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_content)) }, confirmButton = { TextButton(onClick = onDismissRequest) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } }, dismissButton = { @@ -373,7 +373,7 @@ fun NitterUsageDialog( ) } ) { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_dialog_usage_project_button)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_project_button)) } } ) @@ -388,16 +388,16 @@ fun NitterInformationDialog( onDismissRequest.invoke() }, title = { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_dialog_information_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_information_title)) }, text = { - Text(text = stringResource(id = R.string.scene_settings_misc_nitter_dialog_information_content)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_information_content)) }, confirmButton = { TextButton( onClick = onDismissRequest, ) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } } ) @@ -415,7 +415,7 @@ fun ProxyTypeSelectDialog( AlertDialog( onDismissRequest = onDismissRequest, title = { - Text(text = stringResource(id = R.string.scene_settings_misc_proxy_type_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_title)) }, text = { Column { @@ -444,7 +444,7 @@ fun ProxyTypeSelectDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } } ) @@ -491,7 +491,7 @@ fun ProxyInputDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = R.string.common_controls_actions_ok)) + Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) } } ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index ab027ba03..8b39caaef 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -69,7 +69,7 @@ fun NotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = R.string.scene_settings_notification_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_title)) }, navigationIcon = { AppBarNavigationButton() @@ -86,7 +86,7 @@ fun NotificationScene() { viewModel.setEnabled(!notificationEnabled) }, text = { - Text(text = stringResource(id = R.string.scene_settings_notification_notification_switch)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_notification_switch)) }, trailing = { ColoredSwitch( @@ -102,7 +102,7 @@ fun NotificationScene() { ) } ItemHeader { - Text(text = stringResource(id = R.string.scene_settings_notification_accounts)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_accounts)) } val navController = LocalNavController.current LazyColumn { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt index d2e4a0c3b..1901debe5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt @@ -53,41 +53,41 @@ data class SettingItem( fun SettingsScene() { val settings = mapOf( - stringResource(id = R.string.scene_settings_section_header_general) to listOf( + stringResource(id = com.twidere.common.R.string.scene_settings_section_header_general) to listOf( SettingItem( - stringResource(id = R.string.scene_settings_appearance_title), + stringResource(id = com.twidere.common.R.string.scene_settings_appearance_title), painterResource(id = R.drawable.ic_shirt), route = RootRoute.Settings.Appearance, ), SettingItem( - stringResource(id = R.string.scene_settings_display_title), + stringResource(id = com.twidere.common.R.string.scene_settings_display_title), painterResource(id = R.drawable.ic_template), route = RootRoute.Settings.Display, ), SettingItem( - stringResource(id = R.string.scene_settings_layout_title), + stringResource(id = com.twidere.common.R.string.scene_settings_layout_title), painterResource(id = R.drawable.ic_layout_sidebar), route = RootRoute.Settings.Layout, ), SettingItem( - stringResource(id = R.string.scene_settings_notification_title), + stringResource(id = com.twidere.common.R.string.scene_settings_notification_title), painterResource(id = R.drawable.ic_settings_notification), route = RootRoute.Settings.Notification, ), SettingItem( - stringResource(id = R.string.scene_settings_storage_title), + stringResource(id = com.twidere.common.R.string.scene_settings_storage_title), painterResource(id = R.drawable.ic_database), route = RootRoute.Settings.Storage, ), SettingItem( - stringResource(id = R.string.scene_settings_misc_title), + stringResource(id = com.twidere.common.R.string.scene_settings_misc_title), painterResource(id = R.drawable.ic_triangle_square_circle), route = RootRoute.Settings.Misc, ), ), - stringResource(id = R.string.scene_settings_section_header_about) to listOf( + stringResource(id = com.twidere.common.R.string.scene_settings_section_header_about) to listOf( SettingItem( - stringResource(id = R.string.scene_settings_about_title), + stringResource(id = com.twidere.common.R.string.scene_settings_about_title), painterResource(id = R.drawable.ic_info_circle), route = RootRoute.Settings.About, ), @@ -102,7 +102,7 @@ fun SettingsScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_title)) } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 19874e669..5de1bf4dc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -72,7 +72,7 @@ fun StorageScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = R.string.scene_settings_storage_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_title)) } ) } @@ -86,7 +86,7 @@ fun StorageScene() { viewModel.clearSearchHistory() }, ) { - Text(text = stringResource(id = R.string.scene_settings_storage_search_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_search_title)) } ListItem( modifier = Modifier @@ -94,10 +94,10 @@ fun StorageScene() { viewModel.clearImageCache() }, text = { - Text(text = stringResource(id = R.string.scene_settings_storage_media_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_media_title)) }, secondaryText = { - Text(text = stringResource(id = R.string.scene_settings_storage_media_sub_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_media_sub_title)) }, ) ListItem( @@ -106,10 +106,10 @@ fun StorageScene() { viewModel.clearDatabaseCache() }, text = { - Text(text = stringResource(id = R.string.scene_settings_storage_all_title), color = Color.Red) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_all_title), color = Color.Red) }, secondaryText = { - Text(text = stringResource(id = R.string.scene_settings_storage_all_sub_title)) + Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_all_sub_title)) }, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt index b72875ea8..173845dc4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt @@ -53,7 +53,7 @@ fun FollowersScene( AppBarNavigationButton() }, title = { - Text(stringResource(id = R.string.scene_followers_title)) + Text(stringResource(id = com.twidere.common.R.string.scene_followers_title)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt index 61541db6b..6dcaa83b9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt @@ -53,7 +53,7 @@ fun FollowingScene( AppBarNavigationButton() }, title = { - Text(stringResource(id = R.string.scene_following_title)) + Text(stringResource(id = com.twidere.common.R.string.scene_following_title)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index 5ccdf1357..c84afa04d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -93,7 +93,7 @@ fun UserScene( Icon( painter = painterResource(id = R.drawable.ic_mail), contentDescription = stringResource( - id = R.string.scene_messages_title + id = com.twidere.common.R.string.scene_messages_title ), tint = MaterialTheme.colors.onSurface ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt index 983a91391..f77a38454 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -42,7 +42,7 @@ fun Throwable.generateNotificationEvent(): NotificationEvent? { is MicroBlogHttpException -> { when (this.httpCode) { HttpErrorCodes.TooManyRequests -> { - return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) + return StringResNotificationEvent(messageId = com.twidere.common.R.string.common_alerts_too_many_requests_title) } else -> null } @@ -54,9 +54,9 @@ fun Throwable.generateNotificationEvent(): NotificationEvent? { when (this.errors?.firstOrNull()?.code) { TwitterErrorCodes.TemporarilyLocked -> { StringResWithActionNotificationEvent( - R.string.common_alerts_account_temporarily_locked_title, - R.string.common_alerts_account_temporarily_locked_message, - actionId = R.string.common_controls_actions_ok + com.twidere.common.R.string.common_alerts_account_temporarily_locked_title, + com.twidere.common.R.string.common_alerts_account_temporarily_locked_message, + actionId = com.twidere.common.R.string.common_controls_actions_ok ) { context.startActivity( Intent( diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 1be35a12f..6a6f21e8c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -63,7 +63,7 @@ class MediaViewModel @AssistedInject constructor( fun shareMedia(currentMedia: UiMedia, target: String, context: Context) { val uri = FileProviderHelper.getUriFromMedia(target, context) - inAppNotification.show(R.string.common_alerts_media_sharing_title) + inAppNotification.show(com.twidere.common.R.string.common_alerts_media_sharing_title) currentMedia.mediaUrl?.let { DownloadMediaWorker.create( accountKey = account.accountKey, diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 58ac50a69..02291a288 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -147,13 +147,13 @@ enum class VoteExpired(val value: Long) { @Composable fun stringName(): String { return when (this) { - Min_5 -> stringResource(id = R.string.scene_compose_vote_expiration_5_Min) - Min_30 -> stringResource(id = R.string.scene_compose_vote_expiration_30_Min) - Hour_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Hour) - Hour_6 -> stringResource(id = R.string.scene_compose_vote_expiration_6_Hour) - Day_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Day) - Day_3 -> stringResource(id = R.string.scene_compose_vote_expiration_3_Day) - Day_7 -> stringResource(id = R.string.scene_compose_vote_expiration_7_Day) + Min_5 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_5_Min) + Min_30 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_30_Min) + Hour_1 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_1_Hour) + Hour_6 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_6_Hour) + Day_1 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_1_Day) + Day_3 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_3_Day) + Day_7 -> stringResource(id = com.twidere.common.R.string.scene_compose_vote_expiration_7_Day) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index 14aafd602..ea3a3de57 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -122,7 +122,7 @@ class MiscViewModel @AssistedInject constructor( try { proxyPort.value = value.toInt() } catch (e: NumberFormatException) { - inAppNotification.show(R.string.scene_settings_misc_proxy_port_error) + inAppNotification.show(com.twidere.common.R.string.scene_settings_misc_proxy_port_error) return } viewModelScope.launch { diff --git a/android/src/main/res-localized/values-ar-rSA/strings.xml b/android/src/main/res-localized/values-ar-rSA/strings.xml deleted file mode 100644 index 73588b0c1..000000000 --- a/android/src/main/res-localized/values-ar-rSA/strings.xml +++ /dev/null @@ -1,41 +0,0 @@ - - إلغاء متابعة المستخدم %s؟ - الرجاء المحاولة مرة أخرى - تم إرسال التغريدة - الرجاء المحاولة مرة أخرى - قواعد تويتر - الحساب معلق - تويتر يعلق الحسابات التي تخالف %s - تعذر تحميل الوسائط - الرجاء المحاولة مرة أخرى - إلغاء طلب متابعة %s؟ - الرجاء المحاولة مرة أخرى - تعذر حفظ الصورة - الرجاء المحاولة مرة أخرى - لم يتم العثور على تغريدات - يتم حفظ الوسائط - تم رفض الإذن - عذراً، غير مصرح لك - الرجاء المحاولة مرة أخرى - تم إجتياز الحد المسموح به - وصلت إلى حد استخدام واجهة برمجة تطبيقات تويتر - فشل التحميل - الرجاء المحاولة مرة أخرى - تم رفض الإذن - تم حظرك من متابعة هذا الحساب بناء على طلب المستخدم - تم حفظ الوسائط - تم حذف التغريدة - الرجاء المحاولة مرة أخرى - يتم إرسال التغريدة - تعذر حذف التغريدة - الرجاء المحاولة مرة أخرى - تم حفظ الصورة - الحساب مقفل مؤقتاً - إفتح تويتر لفك القفل - الرجاء المحاولة مرة أخرى - تعذر إرسال التغريدة - الرجاء المحاولة مرة أخرى - الرجاء المحاولة مرة أخرى - الرجاء المحاولة مرة أخرى - تم رفض الإذن - \ No newline at end of file diff --git a/android/src/main/res-localized/values-fr-rFR/strings.xml b/android/src/main/res-localized/values-fr-rFR/strings.xml deleted file mode 100644 index 7a58d9d62..000000000 --- a/android/src/main/res-localized/values-fr-rFR/strings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - %s a été bloqué(e) - %s a été débloqué(e) - Échec du blocage de %s - Merci de réessayer - Merci de réessayer - \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index bc68b080a..b076107c9 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -47,4 +47,5 @@ object Versions { const val extJUnitVersion = "1.1.3-rc01" const val espressoVersion = "3.4.0-rc01" const val koin = "3.1.2" + const val moko = "0.17.2" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 63388f9c1..6733e6294 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,5 +1,5 @@ -import Versions.ksp import org.jetbrains.compose.compose +import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated plugins { kotlin("multiplatform") @@ -8,6 +8,7 @@ plugins { id("com.android.library") kotlin("kapt") id("com.google.devtools.ksp").version(Versions.ksp) + id("dev.icerock.mobile.multiplatform-resources") version Versions.moko } group = Package.group @@ -44,6 +45,7 @@ kotlin { implementation("org.jsoup:jsoup:1.13.1") implementation(projects.routeProcessor) ksp(projects.routeProcessor) + implementation("dev.icerock.moko:resources:${Versions.moko}") } } val commonTest by getting { @@ -80,6 +82,10 @@ kotlin { } } +multiplatformResources { + multiplatformResourcesPackage = Package.id +} + fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kapt(dependencyNotation: String) { configurations["kapt"].dependencies.add(project.dependencies.create(dependencyNotation)) } @@ -119,3 +125,80 @@ android { } } } + +afterEvaluate { + tasks.register("generateTranslation") { + val localizationFolder = File(rootDir, "localization") + val appJson = File(localizationFolder, "app.json") + val target = project.file("src/commonMain/resources/MR/base/strings.xml").apply { + ensureParentDirsCreated() + if (!exists()) { + createNewFile() + } + } + generateLocalization(appJson, target) + } + + tasks.register("generateTranslationFromZip") { + val zip = File(rootProject.buildDir, "Twidere X (translations).zip") + val unzipTarget = rootProject.buildDir + org.gradle.kotlin.dsl.support.unzipTo(unzipTarget, zip) + File(unzipTarget, "translation").listFiles()?.forEach { file -> + val source = File(file, "app.json") + val target = project.file( + "src/commonMain/resources/MR/" + file.name.split('_') + .first() + "-r" + file.name.split('_').last() + "/strings.xml" + ) + generateLocalization(source, target) + } + } +} + +fun generateLocalization(appJson: File, target: File) { + val json = appJson.readText(Charsets.UTF_8) + val obj = org.json.JSONObject(json) + val result = flattenJson(obj).filter { + it.value.isNotEmpty() && it.value.isNotBlank() + } + if (result.isNotEmpty()) { + target.apply { + ensureParentDirsCreated() + if (!exists()) { + createNewFile() + } + } + val xml = + """""" + System.lineSeparator() + + result.map { + " ${ + it.value.replace("'", "\\'").replace(System.lineSeparator(), "\\n") + }" + }.joinToString(System.lineSeparator()) + System.lineSeparator() + + "" + target.writeText(xml) + } +} + +fun flattenJson(obj: org.json.JSONObject): Map { + return obj.toMap().toList().flatMap { it -> + val (key, value) = it + when (value) { + is org.json.JSONObject -> { + flattenJson(value).map { + "${key}_${it.key}" to it.value + }.toList() + } + is Map<*, *> -> { + flattenJson(org.json.JSONObject(value)).map { + "${key}_${it.key}" to it.value + }.toList() + } + is String -> { + listOf(key to value) + } + else -> { + listOf(key to value.toString()) + } + } + }.toMap() +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt index d3d2b233b..e9780a4b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt @@ -96,7 +96,7 @@ data class UiStatus( @Composable fun sample() = UiStatus( statusId = "", - htmlText = "", // stringResource(id = R.string.scene_settings_display_preview_thank_for_using_twidere_x), + htmlText = "", // stringResource(id = com.twidere.common.R.string.scene_settings_display_preview_thank_for_using_twidere_x), timestamp = System.currentTimeMillis(), metrics = StatusMetrics( retweet = 1200, diff --git a/common/src/commonMain/resources/MR/ar-rSA/strings.xml b/common/src/commonMain/resources/MR/ar-rSA/strings.xml new file mode 100644 index 000000000..4cec04131 --- /dev/null +++ b/common/src/commonMain/resources/MR/ar-rSA/strings.xml @@ -0,0 +1,138 @@ + + فُك الكتم عن %s + إلغاء متابعة المستخدم %s؟ + سيُشارك الوسيط بعد اكتمال تنزيله + فشل إلغاء المتابعة + الرجاء المحاولة مرة أخرى + تم إرسال التغريدة + فشل الولوج + رابط الخادم خاطئ. + نجحت المتابعة + فشلت المتابعة + الرجاء المحاولة مرة أخرى + قواعد تويتر + الحساب معلق + تويتر يعلق الحسابات التي تخالف %s + تعذر تحميل الوسائط + الرجاء المحاولة مرة أخرى + حُجب %s + إلغاء طلب متابعة %s؟ + فشل الابلاغ عن %s + الرجاء المحاولة مرة أخرى + تعذر حفظ الصورة + الرجاء المحاولة مرة أخرى + لم يتم العثور على تغريدات + يتم حفظ الوسائط + فشل الولوج + انتهت مهلة الاتصال. + فُك الحجب عن %s + كُتم %s + تم رفض الإذن + عذراً، غير مصرح لك + ارسال رسالة + فشل ارسال الرسالة + فشل فك الكتم عن %s + الرجاء المحاولة مرة أخرى + تم إجتياز الحد المسموح به + وصلت إلى حد استخدام واجهة برمجة تطبيقات تويتر + فشل التحميل + الرجاء المحاولة مرة أخرى + تم رفض الإذن + تم حظرك من متابعة هذا الحساب بناء على طلب المستخدم + تم حفظ الوسائط + تم حذف التغريدة + أُرسل طلب المتابعة + فشل كتم %s + الرجاء المحاولة مرة أخرى + يتم إرسال التغريدة + تعذر حذف التغريدة + الرجاء المحاولة مرة أخرى + تم حفظ الصورة + الحساب مقفل مؤقتاً + إفتح تويتر لفك القفل + فشل حجب %s + الرجاء المحاولة مرة أخرى + تعذر إرسال التغريدة + الرجاء المحاولة مرة أخرى + نجح إلغاء المتابعة + الرجاء المحاولة مرة أخرى + فشل فك الحجب عن %s + الرجاء المحاولة مرة أخرى + الرسائل + التفاعلات + تفاعلات مثل الذكر و إعادة التغريد + %s يطلب متابعتك + انتهى الاستطلاع + ذكرك %s + ارسل %s لك رسالة + انتهت مهلة الاستطلاع الذي اشتركت به + أُعجِب %s بتبويقك + تابعك %s + نشر %s للتو + تابعه + الغ متابعته + حمّل المزيد + أضف + الغِ + معاينة + شارك وسيطًا + افتحه في سفاري + نعم + احفظ + أزِل + أكِّد + احفظ الصورة + التقط صورة + موافق + لِج + %s أعاد التغريد + مغلق + اظهر هذا النقاش + شارك الرابط + احذف التغريدة + انسخ النص + صوت + انسخ الرابط + احفظ + أضف + محلي + أضف + أزِل + الكل + التنبيهات + احذف الحساب + الحسابات + اعرض أقلّ + المستخدمون + الوسوم + اعرض المزيد + تابعه + الغ متابعته + شائع الآن + الشائع + الشائع - عالميا + لِج عبر ماستودون + مرحبًا!\nلِج لتبدأ الاستخدام. + لج عبر تويتر + المصادقة + الإعجابات + الحسابات + التنبيهات + معاينة + كل التغريدات + اخف الرد + انا + تم رفض الإذن + حجب هذا المستخدم عنك ملفه الشخصي. + لِج + رد على … + اكتب التحذير + و + ماذا يحدث ؟ + ابحث عن اشخاص + فشل إرسال الرسالة + انسخ نص الرسالة + الرسائل + تبويقة + تغريدة + \ No newline at end of file diff --git a/android/src/main/res/values/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml similarity index 97% rename from android/src/main/res/values/strings.xml rename to common/src/commonMain/resources/MR/base/strings.xml index bff316365..af82dfb46 100644 --- a/android/src/main/res/values/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -48,6 +48,7 @@ Failed to mute %s Please try again Sending tweet + Do you want to block %s? Failed to Delete Tweet Please try again Photo Saved @@ -85,6 +86,7 @@ Mute %s %s is following you Unblock + Blocked Report and Block Following Pending @@ -113,7 +115,10 @@ Take photo OK Sign in + %s boosted %s retweeted + You retweeted + You boosted Media %s person %s vote @@ -122,11 +127,16 @@ %s people Show this thread Share link + Bookmark Delete tweet Quote Retweet + Pin on Profile Copy text + Unpin from Profile + Share Vote + Translate Copy link %s quote %s quotes @@ -362,10 +372,10 @@ Search people Find people Send message failed - The Current account does not support direct messages Copy message text Delete message for you Messages + The Current account does not support direct messages Search hashtag 1 Quote %d Quotes diff --git a/android/src/main/res-localized/values-ca-rES/strings.xml b/common/src/commonMain/resources/MR/ca-rES/strings.xml similarity index 100% rename from android/src/main/res-localized/values-ca-rES/strings.xml rename to common/src/commonMain/resources/MR/ca-rES/strings.xml diff --git a/android/src/main/res-localized/values-de-rDE/strings.xml b/common/src/commonMain/resources/MR/de-rDE/strings.xml similarity index 98% rename from android/src/main/res-localized/values-de-rDE/strings.xml rename to common/src/commonMain/resources/MR/de-rDE/strings.xml index 09d62cdb8..8d980f49b 100644 --- a/android/src/main/res-localized/values-de-rDE/strings.xml +++ b/common/src/commonMain/resources/MR/de-rDE/strings.xml @@ -12,6 +12,7 @@ Twitter-Regeln Konto gesperrt Twitter sperrt Konten die gegen %s verstoßen + Datei speichern fehlgeschlagen Bitte versuche es erneut %s wurde blockiert Follow Anfrage für %s abbrechen? @@ -21,6 +22,7 @@ Foto konnte nicht gespeichert werden Bitte versuche es erneut Keine Tweets gefunden + Datei speichern %s wurde freigegeben %s wurde stumm geschaltet Zugriff verweigert @@ -33,10 +35,12 @@ Bitte versuche es erneut Zugriff verweigert Dieser Nutzer hat dich blockiert, du kannst ihm nicht folgen + Datei gespeichert Tweet gelöscht Anfrage zum Folgen gesendet Fehler beim Stummschalten von %s Bitte versuche es erneut + Tweet wird gesendet Tweet konnte nicht gelöscht werden Bitte versuche es erneut Foto gespeichert diff --git a/common/src/commonMain/resources/MR/el-rGR/strings.xml b/common/src/commonMain/resources/MR/el-rGR/strings.xml new file mode 100644 index 000000000..468886d99 --- /dev/null +++ b/common/src/commonMain/resources/MR/el-rGR/strings.xml @@ -0,0 +1,54 @@ + + %s αναιρέθηκε η σίγαση + Κατάργηση ακολούθησης χρήστη %s; + Τα πολυμέσα θα κοινοποιηθούν μετά την ολοκλήρωση της λήψης + Αποτυχία κατάργησης Ακολούθησης + Παρακαλούμε προσπαθήστε ξανά + Tweet Στάλθηκε + Πάρα Πολλά Αιτήματα + Ακολούθηση Επιτυχής + Αποτυχία ακολούθησης + Παρακαλούμε προσπαθήστε ξανά + Κανόνες Twitter + Ο Λογαριασμός έχει Ανασταλεί + Το Twitter αναστέλλει λογαριασμούς που παραβιάζουν το %s + Αποτυχία αποθήκευσης των πολυμέσων + Παρακαλούμε προσπαθήστε ξανά + %s έχει αποκλειστεί + Ακύρωση αιτήματος ακολούθησης για %s; + Παρακαλούμε προσπαθήστε ξανά + Αποτυχία Αποθήκευσης Φωτογραφίας + Παρακαλούμε προσπαθήστε ξανά + Δεν Βρέθηκαν Tweets + Αποθήκευση πολυμέσων + %s έχει γίνει σίγαση + Η Άδεια Απορρίφθηκε + Λυπούμαστε, δεν είστε εξουσιοδοτημένοι + Αποτυχία αναίρεσης σίγασης %s + Παρακαλούμε προσπαθήστε ξανά + Υπέρβαση Όριου Αξιολόγησης + Φτάσατε στο όριο χρήσης του Twitter API + Αποτυχία φόρτωσης + Παρακαλούμε προσπαθήστε ξανά + Η Άδεια Απορρίφθηκε + Έχετε αποκλειστεί από την ακολούθηση αυτού του λογαριασμού κατόπιν αιτήματος του χρήστη + Τα πολυμέσα αποθηκεύτηκαν + Το Tweet Διαγράφηκε + Το Αίτημα Ακολούθησης Στάλθηκε + Αποτυχία σίγασης %s + Παρακαλούμε προσπαθήστε ξανά + Γίνεται αποστολή tweet + Η Διαγραφή του Tweet Απέτυχε + Παρακαλούμε προσπαθήστε ξανά + Η Φωτογραφία Αποθηκεύτηκε + Προσωρινά Κλειδωμένος Λογαριασμός + Άνοιγμα του Twitter για ξεκλείδωμα + Αποτυχία αποκλεισμού %s + Παρακαλούμε προσπαθήστε ξανά + Αποτυχία Tweet + Παρακαλούμε προσπαθήστε ξανά + Κατάργηση της Ακολούθησης Επιτυχής + Παρακαλούμε προσπαθήστε ξανά + Παρακαλούμε προσπαθήστε ξανά + Η Άδεια Απορρίφθηκε + \ No newline at end of file diff --git a/android/src/main/res-localized/values-en-rUS/strings.xml b/common/src/commonMain/resources/MR/en-rUS/strings.xml similarity index 100% rename from android/src/main/res-localized/values-en-rUS/strings.xml rename to common/src/commonMain/resources/MR/en-rUS/strings.xml diff --git a/android/src/main/res-localized/values-es-rES/strings.xml b/common/src/commonMain/resources/MR/es-rES/strings.xml similarity index 98% rename from android/src/main/res-localized/values-es-rES/strings.xml rename to common/src/commonMain/resources/MR/es-rES/strings.xml index b3ac19745..63ac579de 100644 --- a/android/src/main/res-localized/values-es-rES/strings.xml +++ b/common/src/commonMain/resources/MR/es-rES/strings.xml @@ -3,14 +3,14 @@ ¿Dejar de seguir al usuario %s? Los medios serán compartidos una vez completada la descarga Error al dejar de seguir - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo Tweet enviado Error al iniciar sesión La URL del servidor es incorrecta. Demasiadas solicitudes Seguido con éxito Error al seguir - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo %s ha sido reportado por spam Reglas de Twitter Cuenta suspendida @@ -23,7 +23,7 @@ Por favor, inténtelo de nuevo %s ha sido reportado por spam y bloqueado Error al guardar la foto - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo No se encontraron Tweets Guardando multimedia Error al iniciar sesión @@ -39,17 +39,17 @@ Límite de transferencia excedido Límite de uso de la API de Twitter alcanzado Error al cargar - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo Permiso denegado - Se le ha bloqueado el acceso a esta cuenta a petición del usuario + Has sido bloqueado para seguir esta cuenta a petición del usuario Archivo multimedia guardado Tweet eliminado Solicitud de seguimiento enviada Error al silenciar %s - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo Enviando tweet Error al eliminar el Tweet - Por favor, inténtalo de nuevo + Por favor, inténtelo de nuevo Foto guardada Cuenta bloqueada temporalmente Abre Twitter para desbloquear @@ -365,6 +365,7 @@ Copiar texto del mensaje Eliminar mensaje para ti Mensajes + La cuenta actual no admite mensajes directos Buscar hashtags 1 Cita %d Citas diff --git a/common/src/commonMain/resources/MR/fr-rFR/strings.xml b/common/src/commonMain/resources/MR/fr-rFR/strings.xml new file mode 100644 index 000000000..253c0a4d2 --- /dev/null +++ b/common/src/commonMain/resources/MR/fr-rFR/strings.xml @@ -0,0 +1,332 @@ + + Ne plus suivre l\'utilisateur %s? + Le média sera partagé une fois le téléchargement terminé + Échec de l\'annulation de l\'abonnement + Merci de réessayer + Tweet envoyé + Échec de connexion + L’adresse du serveur est incorrecte. + Trop de Requêtes + Abonnement réussi + Échec de l\'abonnement + Merci de réessayer + %s a été signalé pour spam + Règles de Twitter + Compte suspendu + Twitter suspend les comptes qui violent les %s + Impossible d\'enregistrer le média + Merci de réessayer + %s a été bloqué·e + Annuler la demande de suivi pour %s? + Échec du signalement de %s + Merci de réessayer + %s a été bloqué et signalé pour spam + Échec de l\'enregistrement de la photo + Merci de réessayer + Aucun tweet trouvé + Enregistrement du média + Échec de connexion + Délai de connexion expiré. + %s a été débloqué·e + Permission refusée + Désolé, vous n\'êtes pas autorisé + Envoi du message + Échec de l\'envoi du message + Merci de réessayer + Limite d\'utilisation de l\'API Twitter atteinte + Échec du chargement + Merci de réessayer + Permission refusée + Vous avez été empêché de suivre ce compte à la demande de l\'utilisateur + Média enregistré + Tweet supprimé + Demande de suivi envoyée + Merci de réessayer + Envoi du tweet + La suppression du Tweet a échoué + Merci de réessayer + Photo Enregistrée + Compte verrouillé temporairement + Ouvrez Twitter pour le déverrouiller + Échec du blocage de %s + Merci de réessayer + Echec de l\'envoi du tweet + Merci de réessayer + Désabonnement réussi + Impossible de signaler et de bloquer %s + Merci de réessayer + Merci de réessayer + Messages + Messages directs + Interactions + %s a boosté votre pouet + %s a demandé à vous suivre + Votre sondage est terminé + %s vous a mentionné + Nouveau message + %s vous a envoyé un message + Un sondage auquel vous avez participé est maintenant terminé + %s a aimé votre pouet + %s vous suit + %s vient de poster + Bloquer %s + abonné·e + abonnés + %s ne vous suit pas + Vous suit + Mettre en sourdine %s + %s vous suit + Débloquer + Signaler et bloquer + Abonnements + En attente + Signaler + Mettre en sourdine + Rétablir le son + Bloquer + S’abonner + Se désabonner + Abonnés + Listé + Abonnements + Charger plus + Galerie photo + Ajouter + Annuler + Aperçu + Partager le média + Éditer + Ouvrir dans Safari + Oui + Sauvegarder + Supprimer + Confirmer + Enregistrer la photo + Prendre une photo + OK + Se connecter + %s a retweeté + Média + %s personne + %s voix + Fermé + %s voix + %s personnes + Afficher ce fil de discussion + Partager le lien + Supprimer le tweet + Citation + Retweeter + Copier le texte + Voter + Copier le lien + %s citation + %s citations + %s a retweeté + %s a aimé + %s membre + %s membres + %s photo + %s photos + %s tweet + %s tweets + %s réponse + %s réponses + %s liste + %s listes + Plus + Retour + Logo de Twidere X + Logo de Twitter + Logo de Github + Logo de Mastodon + Logo de Telegram + Lire la vidéo + Fermer + Fait + Emplacement + Média + Retweeter + Réponse + Taille du texte + Sauvegarder + Ajouter + Ajouter une image + Ajouter une mention + Ouvrir le brouillon + Activer la localisation + Désactiver la localisation + Mode fil de discussion + Envoyer + Site Web + Média + Emplacement + Menu + Renommer la liste + Créer une liste + Privé + Modifier la liste + Nom + Nouvelle liste + Description  + Local + Rechercher des personnes + Ajouter un membre + Ajouter + Supprimer + Toutes + Notification + Supprimer le compte + Comptes + Masquer + Rechercher des tweets ou des utilisateurs + Média + Tweets + Utilisateurs + Mot clé + Recherche + Afficher plus + Recherche sauvegardée + Fédéré + Aucun membre trouvé. + Abonné·e·s + Membres de la liste + Supprimer de cette liste + Ajouter des membres + %d membres + 1 abonné·e + 1 membre + %d abonné·e·s + Supprimer de cette liste : %s + Modifier la liste + Renommer la liste + Supprimer la liste + S’abonner + Se désabonner + Ajouter un membre + En tendances maintenant + %d personne en parle + Tendances + Tendances - Mondiales + Se connecter avec Mastodon + Bonjour !\nConnectez-vous pour commencer. + Se connecter avec Twitter + Se connecter avec une clé Twitter personnalisée + L\'accès à l’API v2 Twitter est requis. + Authentification + J\'aimes + Agencement + Afficher la notification + Comptes + Notification + Choisissez une couleur + En haut + En bas + Couleur de surbrillance + Automatique + Clair + Sombre + Aspect + Position de l’onglet + Thème + Merci d’avoir utilisé @TwidereProject ! + Aperçu des URL + Absolu + Relatif + Utiliser la taille de la police système + Carré arrondi + Style de l’avatar + Cercle + Toujours + Aperçu des médias + Automatique + Lecture automatique + Affichage + Aperçu + Format de la date + Texte + Média + Licence + À propos + Ver %s + Vider tout le cache Twidere X. Les informations d’identification de votre compte ne seront pas perdues. + Vider tout le cache + Vider l’historique de recherche + Vider le cache des médias sauvegardés. + Vider le cache des médias + Stockage + Paramètres + Général + À propos + Serveur + Mot de passe + Port + Proxy + Paramètres du proxy + HTTP + Type de proxy + Nom d’utilisateur + Interface alternative ouverte à Twitter axée sur la confidentialité. + Instance Nitter + URL du projet + Autres + Tous les tweets + Exclure les réponses + Masquer la réponse + Moi + Permission refusée + Vous avez été empêché de consulter le profil de cet utilisateur. + Gestion des comptes + Se connecter + Brouillons + Supprimer le brouillon + Modifier le brouillon + Chercher des utilisateur·rice·s + Signet + Abonnés + Listé + Enregistrer le brouillon + Enregistrer le brouillon ? + Répondre à … + Privé + Public + Non-listé + Direct + , + Écrivez votre avertissement ici + et + Quoi de neuf ? + Citation + Réponse + Choix multiples + 1 jour + 30 minutes + 7 jours + 1 heure + 3 jours + 5 minutes + 6 heures + Réponse à + MES LISTES + Listes + Créer une liste + Mentions + Abonnements + Fil d’actualité + [Photo] + Rechercher des personnes + Trouver des gens + L\'envoi de message a échoué + Copier le texte du message + Messages + 1 citation + %d citations + 1 retweet + %d retweets + 1 j’aime + %d J\'aimes + Pouet + Tweet + 1 réponse + %d réponses + \ No newline at end of file diff --git a/android/src/main/res-localized/values-it-rIT/strings.xml b/common/src/commonMain/resources/MR/it-rIT/strings.xml similarity index 100% rename from android/src/main/res-localized/values-it-rIT/strings.xml rename to common/src/commonMain/resources/MR/it-rIT/strings.xml diff --git a/android/src/main/res-localized/values-ja-rJP/strings.xml b/common/src/commonMain/resources/MR/ja-rJP/strings.xml similarity index 98% rename from android/src/main/res-localized/values-ja-rJP/strings.xml rename to common/src/commonMain/resources/MR/ja-rJP/strings.xml index 3d1cd0cc7..c7ce2fc2e 100644 --- a/android/src/main/res-localized/values-ja-rJP/strings.xml +++ b/common/src/commonMain/resources/MR/ja-rJP/strings.xml @@ -26,10 +26,13 @@ ツイートが見つかりませんでした メディアの保存 ログインに失敗しました + 接続がタイムアウトしました。 %s はブロック解除されました %s はミュートされました 権限がありません 権限がありません + メッセージの送信 + メッセージの送信に失敗しました %s のミュート解除に失敗 もう一度やり直してください レート制限を超えました @@ -136,7 +139,7 @@ 新規作成 アカウント ドロップダウン メニュー - プライベート + フォロワー限定 リストを編集 名前 新規リスト diff --git a/android/src/main/res-localized/values-ko-rKR/strings.xml b/common/src/commonMain/resources/MR/ko-rKR/strings.xml similarity index 99% rename from android/src/main/res-localized/values-ko-rKR/strings.xml rename to common/src/commonMain/resources/MR/ko-rKR/strings.xml index 738d65df1..599b8bc60 100644 --- a/android/src/main/res-localized/values-ko-rKR/strings.xml +++ b/common/src/commonMain/resources/MR/ko-rKR/strings.xml @@ -96,7 +96,7 @@ 팔로우 끊기 팔로워 담긴 리스트 - 팔로우하는 계정 + 팔로우 중 더 불러오기 갤러리 새로 만들기 @@ -322,7 +322,7 @@ 다시 쓰기 사용자 찾기 즐겨찾기 - 나를 팔로우하는 계정 + 팔로워 담긴 리스트 임시 저장하기 임시 저장할까요? @@ -354,7 +354,7 @@ 잠근 계정 보기 리스트 만들기 답글 - 내가 팔로우하는 계정 + 팔로우 중 타임라인 [사진] 사람 찾기 diff --git a/common/src/commonMain/resources/MR/pl-rPL/strings.xml b/common/src/commonMain/resources/MR/pl-rPL/strings.xml new file mode 100644 index 000000000..917868b6a --- /dev/null +++ b/common/src/commonMain/resources/MR/pl-rPL/strings.xml @@ -0,0 +1,3 @@ + + Konto tymczasowo zablokowane + \ No newline at end of file diff --git a/android/src/main/res-localized/values-pt-rBR/strings.xml b/common/src/commonMain/resources/MR/pt-rBR/strings.xml similarity index 99% rename from android/src/main/res-localized/values-pt-rBR/strings.xml rename to common/src/commonMain/resources/MR/pt-rBR/strings.xml index 2d5957648..6a72c93ee 100644 --- a/android/src/main/res-localized/values-pt-rBR/strings.xml +++ b/common/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -239,7 +239,7 @@ Ações da barra de abas Ações do menu Layout Personalizado - Escolha e organize até 5 ações que aparecerão na barra de abas (os cronogramas locais e federais serão exibidos apenas no Mastodon.) + Escolha e ordene até 5 ações que aparecerão na barra de abas (as linhas do tempo locais e federais só serão exibidas no Mastodon.) Mostrar Notificação Contas Notificação @@ -365,6 +365,7 @@ Copiar texto da mensagem Excluir mensagem para você Mensagens + A conta atual não suporta mensagens diretas Buscar hashtag 1 Citação %d Citações diff --git a/android/src/main/res-localized/values-si-rLK/strings.xml b/common/src/commonMain/resources/MR/si-rLK/strings.xml similarity index 100% rename from android/src/main/res-localized/values-si-rLK/strings.xml rename to common/src/commonMain/resources/MR/si-rLK/strings.xml diff --git a/android/src/main/res-localized/values-tr-rTR/strings.xml b/common/src/commonMain/resources/MR/tr-rTR/strings.xml similarity index 99% rename from android/src/main/res-localized/values-tr-rTR/strings.xml rename to common/src/commonMain/resources/MR/tr-rTR/strings.xml index 4956a5bf9..f70539f6f 100644 --- a/android/src/main/res-localized/values-tr-rTR/strings.xml +++ b/common/src/commonMain/resources/MR/tr-rTR/strings.xml @@ -119,7 +119,7 @@ Bu konuyu göster Linki paylaş Tweeti sil - Alıntıla + Alıntı Metni kopyala Oy ver Linki kopyala @@ -221,7 +221,6 @@ Lisans Android 5.0+ için yeni nesil Twidere. \nHala erken aşamada. Sayfa arka plan logosu hakkında - Hakkında Sür %s Tüm Twidere X önbelleğini sil. Hesap kimliğiniz kaybolmaz. Tüm önbelleği temizle diff --git a/android/src/main/res-localized/values-vi-rVN/strings.xml b/common/src/commonMain/resources/MR/vi-rVN/strings.xml similarity index 100% rename from android/src/main/res-localized/values-vi-rVN/strings.xml rename to common/src/commonMain/resources/MR/vi-rVN/strings.xml diff --git a/android/src/main/res-localized/values-zh-rCN/strings.xml b/common/src/commonMain/resources/MR/zh-rCN/strings.xml similarity index 99% rename from android/src/main/res-localized/values-zh-rCN/strings.xml rename to common/src/commonMain/resources/MR/zh-rCN/strings.xml index 52b6a33b4..e2e2cf6e9 100644 --- a/android/src/main/res-localized/values-zh-rCN/strings.xml +++ b/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -96,7 +96,7 @@ 取消关注 关注者 列表中 - 正在关注 + 已关注 加载更多 照片库 添加 @@ -356,7 +356,7 @@ 私有可见性 创建列表 提及 - 正在关注 + 已关注 时间线 [照片] 搜索用户 diff --git a/android/src/main/res-localized/values-zh-rTW/strings.xml b/common/src/commonMain/resources/MR/zh-rTW/strings.xml similarity index 100% rename from android/src/main/res-localized/values-zh-rTW/strings.xml rename to common/src/commonMain/resources/MR/zh-rTW/strings.xml diff --git a/localization b/localization index 32a49017b..3c7fba16b 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 32a49017b010fe454b6829046c6394158750b84e +Subproject commit 3c7fba16b14d5970003f8d946138450c19112e09 From 4ea81f6e6922eaffe8b11ae48588d2acb1c46725 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 2 Sep 2021 18:27:00 +0800 Subject: [PATCH 121/615] fix spotless --- common/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 6733e6294..d334d4a27 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -171,7 +171,7 @@ fun generateLocalization(appJson: File, target: File) { """""" + System.lineSeparator() + result.map { " ${ - it.value.replace("'", "\\'").replace(System.lineSeparator(), "\\n") + it.value.replace("'", "\\'").replace(System.lineSeparator(), "\\n") }" }.joinToString(System.lineSeparator()) + System.lineSeparator() + "" From c7bbb7bd6b22da7ae08e9dadfb2eb23c971acea9 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 3 Sep 2021 13:10:55 +0800 Subject: [PATCH 122/615] redesigned the ui of insert media in ComposeScene --- .../component/media/MediaInsertMenu.kt | 108 ++++++++++++++++++ .../twiderex/model/enums/MediaInsertType.kt | 28 +++++ .../twiderex/scenes/compose/ComposeScene.kt | 22 ++-- .../main/res-localized/drawable/ic_video.xml | 20 ++++ 4 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt create mode 100644 app/src/main/res-localized/drawable/ic_video.xml diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt new file mode 100644 index 000000000..adb0dde84 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -0,0 +1,108 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.media + +import androidx.compose.foundation.layout.Box +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.ListItem +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.R +import com.twidere.twiderex.model.enums.MediaInsertType + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun MediaInsertMenu( + modifier: Modifier = Modifier, + disableList: List = emptyList(), + onSelect: (MediaInsertType) -> Unit +) { + var showDropdown by remember { + mutableStateOf(false) + } + Box(modifier) { + DropdownMenu(expanded = showDropdown, onDismissRequest = { showDropdown = false }) { + MediaInsertType.values().forEach { + if (!disableList.contains(it)) { + DropdownMenuItem( + onClick = { + onSelect(it) + showDropdown = false + } + ) { + ListItem( + text = { + Text(text = it.stringName()) + }, + icon = { + Icon( + painter = it.icon(), + contentDescription = it.stringName(), + tint = MaterialTheme.colors.primary + ) + } + ) + } + } + } + } + IconButton( + onClick = { + showDropdown = !showDropdown + } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_photo), + contentDescription = stringResource( + id = R.string.accessibility_scene_compose_image + ) + ) + } + } +} + +@Composable +private fun MediaInsertType.stringName() = when (this) { + MediaInsertType.CAMERA -> "Take Photo" + MediaInsertType.RECORD_VIDEO -> "Record Video" + MediaInsertType.LIBRARY -> "Browse Library" + MediaInsertType.GIF -> "Add GIF" +} + +@Composable +private fun MediaInsertType.icon() = when (this) { + MediaInsertType.CAMERA -> painterResource(id = R.drawable.ic_camera) + MediaInsertType.RECORD_VIDEO -> painterResource(id = R.drawable.ic_video) + MediaInsertType.LIBRARY -> painterResource(id = R.drawable.ic_photo) + MediaInsertType.GIF -> painterResource(id = R.drawable.ic_gif) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt b/app/src/main/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt new file mode 100644 index 000000000..55783ac9f --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.enums + +enum class MediaInsertType { + CAMERA, + RECORD_VIDEO, + LIBRARY, + GIF +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d4ac9bf70..e2db8574f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -112,6 +112,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationBottomSheetSca import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.itemsGridIndexed +import com.twidere.twiderex.component.media.MediaInsertMenu import com.twidere.twiderex.component.status.StatusLineComponent import com.twidere.twiderex.component.status.TimelineStatusComponent import com.twidere.twiderex.component.status.UserAvatar @@ -126,6 +127,7 @@ import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonVisibility +import com.twidere.twiderex.model.enums.MediaInsertType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.navigation.RootRoute @@ -1163,20 +1165,16 @@ private fun ComposeActions( Box { Row { AnimatedVisibility(visible = allowImage) { - IconButton( - onClick = { - scope.launch { - filePickerLauncher.launch("image/*") + MediaInsertMenu( + onSelect = { + when (it) { + MediaInsertType.CAMERA -> TODO() + MediaInsertType.RECORD_VIDEO -> TODO() + MediaInsertType.LIBRARY -> { scope.launch { filePickerLauncher.launch("image/*") } } + MediaInsertType.GIF -> TODO() } } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_camera), - contentDescription = stringResource( - id = R.string.accessibility_scene_compose_image - ) - ) - } + ) } if (account.type == PlatformType.Mastodon) { IconButton( diff --git a/app/src/main/res-localized/drawable/ic_video.xml b/app/src/main/res-localized/drawable/ic_video.xml new file mode 100644 index 000000000..860faca85 --- /dev/null +++ b/app/src/main/res-localized/drawable/ic_video.xml @@ -0,0 +1,20 @@ + + + + From ddc1bf0e810ace5839447114a525835625bc190f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 3 Sep 2021 15:34:23 +0800 Subject: [PATCH 123/615] add ResLoader --- .../com/twidere/twiderex/kmp/ResLoader.kt | 35 +++++++++++++++++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 27 ++++++++++++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 32 +++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt new file mode 100644 index 000000000..f6cef3392 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import android.content.Context +import dev.icerock.moko.resources.StringResource + +actual class ResLoader( + private val context: Context, +) { + actual fun getString( + res: StringResource, + vararg args: Any + ): String { + return context.getString(res.resourceId, *args) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt new file mode 100644 index 000000000..99c52f6c9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import dev.icerock.moko.resources.StringResource + +expect class ResLoader { + fun getString(res: StringResource, vararg args: Any): String +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt new file mode 100644 index 000000000..0287c1415 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import dev.icerock.moko.resources.StringResource + +actual class ResLoader { + actual fun getString( + res: StringResource, + vararg args: Any + ): String { + return res.localized(args = args) + } +} From 1e5686caf8abd17cfbcdf0a785732d0be6e8af4d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 3 Sep 2021 15:38:11 +0800 Subject: [PATCH 124/615] fix gradle config --- common/build.gradle.kts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index d334d4a27..53e4961d3 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -126,8 +126,8 @@ android { } } -afterEvaluate { - tasks.register("generateTranslation") { +tasks.create("generateTranslation") { + doLast { val localizationFolder = File(rootDir, "localization") val appJson = File(localizationFolder, "app.json") val target = project.file("src/commonMain/resources/MR/base/strings.xml").apply { @@ -138,8 +138,10 @@ afterEvaluate { } generateLocalization(appJson, target) } +} - tasks.register("generateTranslationFromZip") { +tasks.create("generateTranslationFromZip") { + doLast { val zip = File(rootProject.buildDir, "Twidere X (translations).zip") val unzipTarget = rootProject.buildDir org.gradle.kotlin.dsl.support.unzipTo(unzipTarget, zip) From be506da957b0ababa41a55f439984f8e8ec857b0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 3 Sep 2021 16:52:10 +0800 Subject: [PATCH 125/615] add koin module --- .../twiderex/di/PlatformModule.android.kt | 30 +++++++++++++++++++ .../com/twidere/twiderex/di/PlatformModule.kt | 25 ++++++++++++++++ .../kotlin/com/twidere/twiderex/di/Setup.kt | 1 + .../twiderex/di/PlatformModule.desktop.kt | 30 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt new file mode 100644 index 000000000..0933a7a6a --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import com.twidere.twiderex.kmp.ResLoader +import org.koin.dsl.module + +internal actual val platformModule = module { + single { + ResLoader(get()) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt new file mode 100644 index 000000000..be49f1aa2 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import org.koin.core.module.Module + +internal expect val platformModule: Module diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index 1de30c247..769aab250 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -24,4 +24,5 @@ import org.koin.core.KoinApplication fun KoinApplication.setupModules() { modules(preferencesModule) + modules(platformModule) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt new file mode 100644 index 000000000..6cb005f64 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import com.twidere.twiderex.kmp.ResLoader +import org.koin.dsl.module + +internal actual val platformModule = module { + single { + ResLoader() + } +} From 8f1d056cedd08b6383d873cbb5f8cf33b8bddc14 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 3 Sep 2021 17:01:07 +0800 Subject: [PATCH 126/615] upgrade gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18435 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 B8dv}T delta 17845 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 X49Luk)baa85H9$5YCsTPT`SVRWMtMW diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 344165768..56a30d20e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=bf8b869948901d422e9bb7d1fa61da6a6e19411baa7ad6ee929073df85d6365d +distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd diff --git a/gradlew b/gradlew index 4f906e0c8..744e882ed 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) From 1cc0fa790707c6a0e4aab79b637bd1120c8e8540 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 3 Sep 2021 18:31:46 +0800 Subject: [PATCH 127/615] add video/camera support to compose --- .../twiderex/component/UserComponent.kt | 5 +- .../component/media/MediaInsertMenu.kt | 51 ++++++++++++- .../twiderex/extensions/UriExtensions.kt | 36 ++++++++++ .../http/TwidereNetworkImageLoader.kt | 24 +++++++ .../kmp/android/AndroidExifScrambler.kt | 5 +- .../com/twidere/twiderex/navigation/Root.kt | 3 +- .../com/twidere/twiderex/navigation/Route.kt | 9 ++- .../com/twidere/twiderex/scenes/MediaScene.kt | 4 +- .../twiderex/scenes/compose/ComposeScene.kt | 71 ++++++++++++++----- .../twiderex/utils/FileProviderHelper.kt | 23 +++++- .../twiderex/viewmodel/MediaViewModel.kt | 2 +- .../res/drawable/ic_dots_circle_horiz.xml | 14 ++++ app/src/main/res/drawable/ic_media_tag_bg.xml | 13 ++++ .../drawable/ic_video.xml | 0 app/src/main/res/xml/filer_provider_path.xml | 1 + 15 files changed, 226 insertions(+), 35 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt create mode 100644 app/src/main/res/drawable/ic_dots_circle_horiz.xml create mode 100644 app/src/main/res/drawable/ic_media_tag_bg.xml rename app/src/main/{res-localized => res}/drawable/ic_video.xml (100%) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 42fc14c78..64e5b9a8c 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -101,6 +101,7 @@ import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.model.ui.UiUser @@ -481,7 +482,7 @@ fun UserInfo( size = UserInfoDefaults.AvatarSize ) { if (user.profileImage is String) { - navController.navigate(RootRoute.Media.Raw(user.profileImage)) + navController.navigate(RootRoute.Media.Raw(MediaType.photo, user.profileImage)) } } } @@ -757,7 +758,7 @@ private fun UserBanner( .heightIn(max = maxBannerSize) .clickable( onClick = { - navController.navigate(RootRoute.Media.Raw(bannerUrl)) + navController.navigate(RootRoute.Media.Raw(MediaType.photo, bannerUrl)) }, indication = null, interactionSource = remember { MutableInteractionSource() }, diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index adb0dde84..df24cda42 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -20,6 +20,9 @@ */ package com.twidere.twiderex.component.media +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Box import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenuItem @@ -35,18 +38,51 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.MediaInsertType +import com.twidere.twiderex.utils.FileProviderHelper +import java.util.UUID @OptIn(ExperimentalMaterialApi::class) @Composable fun MediaInsertMenu( modifier: Modifier = Modifier, disableList: List = emptyList(), - onSelect: (MediaInsertType) -> Unit + onResult: (List) -> Unit ) { + val context = LocalContext.current + val filePickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.OpenMultipleDocuments(), + onResult = { + onResult(it) + }, + ) + + var cameraTempUri by remember { + mutableStateOf(Uri.EMPTY) + } + + val cameraLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.TakePicture(), + onResult = { + if (it) onResult(listOf(cameraTempUri)) + }, + ) + + var videoTempUri by remember { + mutableStateOf(Uri.EMPTY) + } + + val videoRecordLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.CaptureVideo(), + onResult = { + if (it) onResult(listOf(videoTempUri)) + }, + ) + var showDropdown by remember { mutableStateOf(false) } @@ -56,7 +92,18 @@ fun MediaInsertMenu( if (!disableList.contains(it)) { DropdownMenuItem( onClick = { - onSelect(it) + when (it) { + MediaInsertType.CAMERA -> { + cameraTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) + cameraLauncher.launch(cameraTempUri) + } + MediaInsertType.RECORD_VIDEO -> { + videoTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) + videoRecordLauncher.launch(videoTempUri) + } + MediaInsertType.LIBRARY -> filePickerLauncher.launch(arrayOf("image/*", "video/*")) + MediaInsertType.GIF -> TODO() + } showDropdown = false } ) { diff --git a/app/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt b/app/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt new file mode 100644 index 000000000..a6e93e814 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.extensions + +import android.content.Context +import android.net.Uri +import com.twidere.twiderex.model.enums.MediaType + +fun Uri.mediaType(context: Context): MediaType { + val mimeType = context.contentResolver.getType(this) ?: "" + return when { + mimeType.startsWith("video") -> MediaType.video + mimeType == "image/gif" -> MediaType.animated_gif + mimeType.startsWith("image") -> MediaType.photo + mimeType.startsWith("audio") -> MediaType.audio + else -> MediaType.other + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt index b7b885732..96af842a4 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt @@ -21,6 +21,8 @@ package com.twidere.twiderex.http import android.content.Context +import android.graphics.Bitmap +import android.media.MediaMetadataRetriever import android.net.Uri import coil.ImageLoader import coil.bitmap.BitmapPool @@ -35,6 +37,7 @@ import com.twidere.twiderex.model.cred.OAuthCredentials import com.twidere.twiderex.model.enums.PlatformType import okhttp3.Headers import okhttp3.Request +import java.lang.Exception import java.net.URL class TwidereNetworkImageLoader( @@ -88,9 +91,30 @@ class TwidereNetworkImageLoader( ) ).build() } else { + if (data is Uri && context.contentResolver.getType(data)?.startsWith("video") == true) { + data = getThumbVideo(context = context, videoUri = data) ?: data + } request.newBuilder(request.context) .data(data) .build() } } + + private fun getThumbVideo(context: Context, videoUri: Uri): Bitmap? { + var bitmap: Bitmap? = null + var mediaMetadataRetriever: MediaMetadataRetriever? = null + try { + mediaMetadataRetriever = MediaMetadataRetriever() + mediaMetadataRetriever.setDataSource(context, videoUri) + bitmap = mediaMetadataRetriever.getFrameAtTime( + 1000, + MediaMetadataRetriever.OPTION_CLOSEST_SYNC + ) + } catch (e: Exception) { + e.printStackTrace() + } finally { + mediaMetadataRetriever?.release() + } + return bitmap + } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt b/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt index fb9112bca..113335311 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/kmp/android/AndroidExifScrambler.kt @@ -70,10 +70,7 @@ class AndroidExifScrambler(private val context: Context) : ExifScrambler { } } ImageType.UNKNOWN -> { - imageCache.outputStream().use { - bitmap.compress(Bitmap.CompressFormat.JPEG, compress, it) - it.flush() - } + return uri.toString() } } return imageCache.toUri().toString() diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt b/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt index 5a7161fd8..31073ac3f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.navigation import com.twidere.route.processor.AppRoute import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.viewmodel.compose.ComposeType @AppRoute @@ -49,7 +50,7 @@ interface Root { interface Media { fun Status(statusKey: MicroBlogKey, selectedIndex: Int?): String - fun Raw(url: String): String + fun Raw(type: MediaType, url: String): String fun Pure(belongToKey: MicroBlogKey, selectedIndex: Int?): String } diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt index bc2293903..444886c87 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.unit.Constraints import com.twidere.twiderex.component.RequireAuthorization import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.scenes.DraftListScene import com.twidere.twiderex.scenes.HomeScene @@ -402,10 +403,12 @@ fun RouteBuilder.route(constraints: Constraints) { authorizedDialog( RootRouteDefinition.Media.Raw, ) { backStackEntry -> - backStackEntry.path("url")?.let { + val url = backStackEntry.path("url")?.let { URLDecoder.decode(it, "UTF-8") - }?.let { - RawMediaScene(url = it) + } + val type = MediaType.valueOf(backStackEntry.path("type") ?: MediaType.photo.name) + url?.let { + RawMediaScene(url = it, type = type) } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 77f957776..947079589 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -375,7 +375,7 @@ private object StatusMediaInfoDefaults { } @Composable -fun RawMediaScene(url: String) { +fun RawMediaScene(url: String, type: MediaType) { TwidereDialog( requireDarkTheme = true, extendViewIntoStatusBar = true, @@ -390,7 +390,7 @@ fun RawMediaScene(url: String) { navController.popBackStack() }, ) - MediaView(media = listOf(MediaData(url, MediaType.photo)), swiperState = swiperState) + MediaView(media = listOf(MediaData(url, type)), swiperState = swiperState) } } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index e2db8574f..e14bf7afa 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.scenes.compose import android.Manifest import android.annotation.SuppressLint +import android.content.Context import android.location.Location import android.net.Uri import androidx.activity.compose.BackHandler @@ -30,6 +31,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -76,6 +78,7 @@ import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.rememberBottomSheetScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -95,6 +98,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource @@ -121,13 +125,14 @@ import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.icon +import com.twidere.twiderex.extensions.mediaType import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.stringName import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonVisibility -import com.twidere.twiderex.model.enums.MediaInsertType +import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.navigation.RootRoute @@ -448,6 +453,7 @@ private fun ComposeImageList( images: List, viewModel: ComposeViewModel ) { + val context = LocalContext.current Spacer(modifier = Modifier.height(ComposeImageListDefaults.Spacing)) LazyRow( modifier = Modifier.padding(ComposeImageListDefaults.ContentPadding), @@ -455,7 +461,7 @@ private fun ComposeImageList( itemsIndexed( items = images, ) { index, item -> - ComposeImage(item, viewModel) + ComposeImage(item, viewModel, context) if (index != images.lastIndex) { Spacer(modifier = Modifier.width(ComposeImageListDefaults.ItemSpacing)) } @@ -1147,12 +1153,6 @@ private fun ComposeActions( } val scope = rememberCoroutineScope() val navController = LocalNavController.current - val filePickerLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.GetMultipleContents(), - onResult = { - viewModel.putImages(it) - }, - ) val permissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestMultiplePermissions(), onResult = { @@ -1166,13 +1166,8 @@ private fun ComposeActions( Row { AnimatedVisibility(visible = allowImage) { MediaInsertMenu( - onSelect = { - when (it) { - MediaInsertType.CAMERA -> TODO() - MediaInsertType.RECORD_VIDEO -> TODO() - MediaInsertType.LIBRARY -> { scope.launch { filePickerLauncher.launch("image/*") } } - MediaInsertType.GIF -> TODO() - } + onResult = { + viewModel.putImages(it) } ) } @@ -1331,8 +1326,10 @@ private object ComposeActionsDefaults { } @Composable -private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { +private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Context) { var expanded by remember { mutableStateOf(false) } + val type = item.mediaType(context) + val navController = LocalNavController.current Box { Box( modifier = Modifier @@ -1345,12 +1342,41 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { ) .clickable( onClick = { - expanded = true + navController.navigate(RootRoute.Media.Raw(if (type == MediaType.video) MediaType.video else MediaType.photo, item.toString())) } ) .clip(MaterialTheme.shapes.small), ) { NetworkImage(data = item) + when (type) { + MediaType.animated_gif -> + Image( + painter = painterResource(id = R.drawable.ic_media_tag_bg), + contentDescription = type.name, + modifier = Modifier.align(Alignment.BottomStart) + .width(ComposeImageDefaults.Tag.Width) + .height(ComposeImageDefaults.Tag.Height) + .padding(ComposeImageDefaults.Tag.Padding) + ) + MediaType.video -> Icon( + imageVector = Icons.Default.PlayArrow, + tint = Color.White.copy(alpha = LocalContentAlpha.current), + modifier = Modifier + .align(Alignment.Center) + .size(ComposeImageDefaults.VideoIconSize) + .background(MaterialTheme.colors.primary, CircleShape), + contentDescription = type.name + ) + else -> {} + } + Image( + painter = painterResource(id = R.drawable.ic_dots_circle_horiz), + contentDescription = type.name, + modifier = Modifier.align(Alignment.BottomEnd) + .size(ComposeImageDefaults.Menu.Size) + .padding(ComposeImageDefaults.Menu.Padding) + .clickable { expanded = !expanded } + ) } DropdownMenu( expanded = expanded, @@ -1373,4 +1399,15 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { private object ComposeImageDefaults { val ImageSize = 72.dp + + object Tag { + val Width = 24.dp + val Height = 18.dp + val Padding = PaddingValues(start = 6.dp, bottom = 6.dp) + } + object Menu { + val Size = 24.dp + val Padding = PaddingValues(end = 6.dp, bottom = 3.dp) + } + val VideoIconSize = 30.dp } diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt index acc56962f..b56e2fbf0 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt @@ -28,12 +28,29 @@ import java.io.File object FileProviderHelper { private const val providerName = "com.twidere.twiderex.fileprovider" private const val shareDir = "shares/" + private const val mediaDir = "medias/" - fun getUriFromMedia(mediaFileName: String, context: Context): Uri { - val shareDir = File(context.externalCacheDir, shareDir).apply { + fun getUriFromShares(mediaFileName: String, context: Context): Uri { + return getUri( + fileName = mediaFileName, + dir = shareDir, + context = context + ) + } + + fun getUriFromMedias(mediaFileName: String, context: Context): Uri { + return getUri( + fileName = mediaFileName, + dir = mediaDir, + context = context + ) + } + + private fun getUri(fileName: String, dir: String, context: Context): Uri { + val dirFile = File(context.externalCacheDir, dir).apply { if (!exists()) mkdirs() } - val file = File(shareDir, mediaFileName).apply { + val file = File(dirFile, fileName).apply { if (!exists()) createNewFile() } return FileProvider.getUriForFile(context, providerName, file) diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 1be35a12f..dc2cbbe2f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -62,7 +62,7 @@ class MediaViewModel @AssistedInject constructor( } fun shareMedia(currentMedia: UiMedia, target: String, context: Context) { - val uri = FileProviderHelper.getUriFromMedia(target, context) + val uri = FileProviderHelper.getUriFromShares(target, context) inAppNotification.show(R.string.common_alerts_media_sharing_title) currentMedia.mediaUrl?.let { DownloadMediaWorker.create( diff --git a/app/src/main/res/drawable/ic_dots_circle_horiz.xml b/app/src/main/res/drawable/ic_dots_circle_horiz.xml new file mode 100644 index 000000000..280a9a987 --- /dev/null +++ b/app/src/main/res/drawable/ic_dots_circle_horiz.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_media_tag_bg.xml b/app/src/main/res/drawable/ic_media_tag_bg.xml new file mode 100644 index 000000000..003a25d7a --- /dev/null +++ b/app/src/main/res/drawable/ic_media_tag_bg.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res-localized/drawable/ic_video.xml b/app/src/main/res/drawable/ic_video.xml similarity index 100% rename from app/src/main/res-localized/drawable/ic_video.xml rename to app/src/main/res/drawable/ic_video.xml diff --git a/app/src/main/res/xml/filer_provider_path.xml b/app/src/main/res/xml/filer_provider_path.xml index 80c229d05..fed0e2f90 100644 --- a/app/src/main/res/xml/filer_provider_path.xml +++ b/app/src/main/res/xml/filer_provider_path.xml @@ -1,4 +1,5 @@ + \ No newline at end of file From c78bb463c944e02ca2657ccea9e03c0fd9b8d157 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 6 Sep 2021 14:54:08 +0800 Subject: [PATCH 128/615] fix res and optin --- .../viewmodel/compose/ComposeViewModel.kt | 0 ....kt => NotificationChannelSpec.android.kt} | 0 .../notification/NotificationChannelSpec.kt | 58 +------------------ 3 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt rename common/src/androidMain/kotlin/com/twidere/twiderex/notification/{NotificationChannelSpec.kt => NotificationChannelSpec.android.kt} (100%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt deleted file mode 100644 index e69de29bb..000000000 diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt similarity index 100% rename from common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index 61e0eead5..3fb1d4696 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -1,58 +1,2 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.notification +package com.twidere.twiderex.notification -import android.net.Uri -import com.twidere.twiderex.model.MicroBlogKey - -enum class NotificationChannelSpec( - val id: String, - val showBadge: Boolean = false, - val grouped: Boolean = false -) { - /** - * For notifications indicate that some lengthy operations are performing in the background. - * Such as sending attachment process. - */ - BackgroundProgresses( - "background_progresses", - ), - - ContentInteractions( - "content_interactions", - showBadge = true, - grouped = true - ), - - ContentMessages( - "content_messages", - showBadge = true, - grouped = true - ) -} - -fun MicroBlogKey.notificationChannelId(id: String): String { - return "${id}_${Uri.encode(toString())}" -} - -fun MicroBlogKey.notificationChannelGroupId(): String { - return Uri.encode(toString()) -} From 9019cb53b7f155edac00e97d43c9c2a8c017c772 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 6 Sep 2021 14:54:43 +0800 Subject: [PATCH 129/615] fix vm optin --- .../kotlin/com/twidere/twiderex/Consts.kt | 5 ++ .../twiderex/jobs/common/DownloadMediaJob.kt | 6 +- .../twiderex/jobs/common/NotificationJob.kt | 32 +++++----- .../twiderex/jobs/compose/ComposeJob.kt | 10 ++-- .../jobs/compose/MastodonComposeJob.kt | 5 +- .../jobs/compose/TwitterComposeJob.kt | 5 +- .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 12 +++- .../twiderex/jobs/dm/DirectMessageSendJob.kt | 5 +- .../jobs/dm/TwitterDirectMessageSendJob.kt | 8 ++- .../com/twidere/twiderex/model/HomeMenus.kt | 11 ---- .../notification/InAppNotification.kt | 6 ++ .../notification/NotificationChannelSpec.kt | 58 ++++++++++++++++++- .../twiderex/viewmodel/MediaViewModel.kt | 2 + .../twiderex/viewmodel/StatusViewModel.kt | 3 + .../compose/ComposeSearchUserViewModel.kt | 3 +- .../viewmodel/compose/ComposeViewModel.kt | 3 + .../MastodonComposeSearchHashtagViewModel.kt | 3 + .../viewmodel/dm/DMConversationViewModel.kt | 2 + .../twiderex/viewmodel/dm/DMEventViewModel.kt | 3 + .../dm/DMNewConversationViewModel.kt | 3 + .../lists/ListsSearchUserViewModel.kt | 3 + .../viewmodel/lists/ListsTimelineViewModel.kt | 2 + .../viewmodel/lists/ListsUserViewModel.kt | 3 + .../viewmodel/lists/ListsViewModel.kt | 5 ++ .../mastodon/MastodonHashtagViewModel.kt | 2 + .../MastodonSearchHashtagViewModel.kt | 2 + .../viewmodel/search/SearchInputViewModel.kt | 3 + .../viewmodel/search/SearchTweetsViewModel.kt | 2 + .../viewmodel/search/SearchUserViewModel.kt | 2 + .../settings/AccountNotificationViewModel.kt | 2 + .../viewmodel/timeline/TimelineViewModel.kt | 4 ++ .../viewmodel/trend/TrendViewModel.kt | 2 + .../twitter/TwitterSignInViewModel.kt | 4 +- .../search/TwitterSearchMediaViewModel.kt | 2 + .../viewmodel/user/FollowersViewModel.kt | 2 + .../viewmodel/user/FollowingViewModel.kt | 2 + .../user/UserFavouriteTimelineViewModel.kt | 2 + .../user/UserMediaTimelineViewModel.kt | 2 + .../viewmodel/user/UserTimelineViewModel.kt | 2 + 39 files changed, 189 insertions(+), 44 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt index f7976e425..1990a8398 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt @@ -29,3 +29,8 @@ val twitterHosts = listOf( "http://mobile.twitter.com", "http://www.twitter.com", ) + +object DefaultConfig { + val ConsumerKey = "wmtrtTaVOjUnH5pWQp4LDI5Qs" + val ConsumerSecret = "E9Q9u2yK0COJae2tLcNEdY75OPA3bxqJiGZQztraHaQUtoI2cu" +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt index 9b8dfdf96..4eebacbcc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt @@ -21,16 +21,18 @@ package com.twidere.twiderex.jobs.common import com.twidere.services.microblog.DownloadMediaService -import com.twidere.twiderex.R import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.notification.StringNotificationEvent.Companion.show import com.twidere.twiderex.repository.AccountRepository class DownloadMediaJob( private val accountRepository: AccountRepository, private val inAppNotification: InAppNotification, private val fileResolver: FileResolver, + private val resLoader: ResLoader, ) { suspend fun execute( target: String, @@ -47,6 +49,6 @@ class DownloadMediaJob( fileResolver.openOutputStream(target)?.use { service.download(target = source).copyTo(it) } ?: throw Error("Download failed") - inAppNotification.show(com.twidere.common.R.string.common_controls_actions_save) + inAppNotification.show(resLoader.getString(com.twidere.twiderex.MR.strings.common_controls_actions_save)) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index fa23e5d1b..8eb720941 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -20,11 +20,13 @@ */ package com.twidere.twiderex.jobs.common +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec @@ -40,6 +42,7 @@ class NotificationJob( private val repository: NotificationRepository, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, + private val resLoader: ResLoader, ) { suspend fun execute() = coroutineScope { accountRepository.getAccounts() @@ -75,8 +78,8 @@ class NotificationJob( val notificationData = when (status.platformType) { PlatformType.Twitter -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_mentions, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_mentions, status.user.displayName ), htmlContent = status.htmlText, @@ -118,8 +121,8 @@ class NotificationJob( MastodonStatusType.Status -> null MastodonStatusType.NotificationFollow -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_follow, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_follow, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey), @@ -128,8 +131,8 @@ class NotificationJob( } MastodonStatusType.NotificationFollowRequest -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_follow_request, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_follow_request, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey) @@ -137,8 +140,8 @@ class NotificationJob( } MastodonStatusType.NotificationMention -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_mentions, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_mentions, actualStatus.user.displayName ), htmlContent = actualStatus.htmlText, @@ -148,8 +151,8 @@ class NotificationJob( } MastodonStatusType.NotificationReblog -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_reblog, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_reblog, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), @@ -158,8 +161,8 @@ class NotificationJob( } MastodonStatusType.NotificationFavourite -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_favourite, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_favourite, actualStatus.user.displayName ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), @@ -168,15 +171,14 @@ class NotificationJob( } MastodonStatusType.NotificationPoll -> { NotificationData( - title = applicationContext.getString( - com.twidere.common.R.string.common_notification_poll, + title = resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_poll, ), deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, ) } MastodonStatusType.NotificationStatus -> null - else -> null } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 18c179710..4a51f7ec1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -21,9 +21,9 @@ package com.twidere.twiderex.jobs.compose import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.R import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.RemoteNavigator +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData @@ -40,11 +40,12 @@ abstract class ComposeJob( private val notificationManager: AppNotificationManager, private val exifScrambler: ExifScrambler, private val remoteNavigator: RemoteNavigator, + private val resLoader: ResLoader, ) { suspend fun execute(composeData: ComposeData, accountKey: MicroBlogKey) { val builder = AppNotification .Builder(NotificationChannelSpec.BackgroundProgresses.id) - .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_sending_title)) + .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_tweet_sending_title)) .setOngoing(true) .setSilent(true) .setProgress(100, 0, false) @@ -52,6 +53,7 @@ abstract class ComposeJob( accountRepository.findByAccountKey(accountKey = it) } ?: throw Error("Can't find any account matches:$$accountKey") val notificationId = composeData.draftId.hashCode() + @Suppress("UNCHECKED_CAST") val service = accountDetails.service as T notificationManager.notify(notificationId, builder.build()) @@ -77,7 +79,7 @@ abstract class ComposeJob( builder.setOngoing(false) .setProgress(0, 0, false) .setSilent(false) - .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_sent_title)) + .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_tweet_sent_title)) notificationManager.notifyTransient(notificationId, builder.build()) if (composeData.isThreadMode) { // open compose scene in thread mode @@ -91,7 +93,7 @@ abstract class ComposeJob( builder.setOngoing(false) .setProgress(0, 0, false) .setSilent(false) - .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_tweet_fail_title)) + .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_tweet_fail_title)) .setContentText(composeData.content) .setDeepLink(RootDeepLinksRoute.Draft(composeData.draftId)) notificationManager.notify(notificationId, builder.build()) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index 015c2032c..92b6dc7e2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility @@ -44,13 +45,15 @@ class MastodonComposeJob( notificationManager: AppNotificationManager, exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator, + resLoader: ResLoader, private val fileResolver: FileResolver, private val cacheDatabase: CacheDatabase, ) : ComposeJob( accountRepository, notificationManager, exifScrambler, - remoteNavigator + remoteNavigator, + resLoader, ) { override suspend fun compose( service: MastodonService, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 8bc1b2b27..4f30f5057 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.ExifScrambler import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.RemoteNavigator +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData @@ -39,6 +40,7 @@ class TwitterComposeJob constructor( notificationManager: AppNotificationManager, exifScrambler: ExifScrambler, remoteNavigator: RemoteNavigator, + resLoader: ResLoader, private val statusRepository: StatusRepository, private val fileResolver: FileResolver, private val cacheDatabase: CacheDatabase, @@ -46,7 +48,8 @@ class TwitterComposeJob constructor( accountRepository, notificationManager, exifScrambler, - remoteNavigator + remoteNavigator, + resLoader, ) { override suspend fun compose( service: TwitterService, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt index c69782390..48fba1167 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.jobs.dm import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.R +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage import com.twidere.twiderex.navigation.RootDeepLinksRoute @@ -39,6 +39,7 @@ class DirectMessageFetchJob( private val repository: DirectMessageRepository, private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, + private val resLoader: ResLoader, ) { suspend fun execute() { accountRepository.activeAccount.firstOrNull()?.takeIf { @@ -62,8 +63,13 @@ class DirectMessageFetchJob( NotificationChannelSpec.ContentMessages.id ) ) - .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_notification_messages_title)) - .setContentText(applicationContext.getString(com.twidere.common.R.string.common_notification_messages_content, message.latestMessage.sender.displayName)) + .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_notification_messages_title)) + .setContentText( + resLoader.getString( + com.twidere.twiderex.MR.strings.common_notification_messages_content, + message.latestMessage.sender.displayName + ) + ) .setDeepLink(RootDeepLinksRoute.Conversation(message.conversation.conversationKey)) notificationManager.notify(message.latestMessage.messageKey.hashCode(), builder.build()) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index 07d357730..fac370c7b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -22,9 +22,9 @@ package com.twidere.twiderex.jobs.dm import android.graphics.BitmapFactory import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.R import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MediaType @@ -44,6 +44,7 @@ abstract class DirectMessageSendJob( private val accountRepository: AccountRepository, private val notificationManager: AppNotificationManager, protected val fileResolver: FileResolver, + private val resLoader: ResLoader, ) { suspend fun execute(sendData: DirectMessageSendData, accountKey: MicroBlogKey) { val accountDetails = accountKey.let { @@ -82,7 +83,7 @@ abstract class DirectMessageSendJob( NotificationChannelSpec.ContentMessages.id ) ) - .setContentTitle(applicationContext.getString(com.twidere.common.R.string.common_alerts_failed_to_send_message_message)) + .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_failed_to_send_message_message)) .setContentText(sendData.text) .setDeepLink(RootDeepLinksRoute.Conversation(sendData.conversationKey)) notificationManager.notify(notificationId, builder.build()) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt index e9e1286df..df0042576 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.dataprovider.mapper.autolink import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.ui.UiDMEvent @@ -38,8 +39,13 @@ class TwitterDirectMessageSendJob( notificationManager: AppNotificationManager, fileResolver: FileResolver, cacheDatabase: CacheDatabase, + resLoader: ResLoader, ) : DirectMessageSendJob( - cacheDatabase, accountRepository, notificationManager, fileResolver + cacheDatabase, + accountRepository, + notificationManager, + fileResolver, + resLoader, ) { override suspend fun sendMessage( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt index 695b02ae9..ca4d185c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt @@ -23,57 +23,46 @@ package com.twidere.twiderex.model import com.twidere.twiderex.model.enums.PlatformType enum class HomeMenus( - val item: HomeNavigationItem, val showDefault: Boolean, val supportedPlatformType: List, ) { HomeTimeline( - item = HomeTimelineItem(), showDefault = true, supportedPlatformType = PlatformType.values().toList(), ), MastodonNotification( - item = MastodonNotificationItem(), showDefault = true, supportedPlatformType = listOf(PlatformType.Mastodon), ), Mention( - item = MentionItem(), showDefault = true, supportedPlatformType = listOf(PlatformType.Twitter), ), Search( - item = SearchItem(), showDefault = true, supportedPlatformType = PlatformType.values().toList(), ), Me( - item = MeItem(), showDefault = true, supportedPlatformType = PlatformType.values().toList(), ), Message( - item = DMConversationListItem(), showDefault = false, supportedPlatformType = listOf(PlatformType.Twitter), ), LocalTimeline( - item = LocalTimelineItem(), showDefault = false, supportedPlatformType = listOf(PlatformType.Mastodon), ), FederatedTimeline( - item = FederatedTimelineItem(), showDefault = false, supportedPlatformType = listOf(PlatformType.Mastodon), ), Draft( - item = DraftNavigationItem(), showDefault = false, supportedPlatformType = PlatformType.values().toList(), ), Lists( - item = ListsNavigationItem(), showDefault = false, supportedPlatformType = PlatformType.values().toList(), ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index cef04eb5d..aa63accfd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -38,6 +38,12 @@ class StringNotificationEvent( override fun getMessage(): String { return message } + + companion object { + fun InAppNotification.show(message: String) { + show(StringNotificationEvent(message = message)) + } + } } class InAppNotification { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index 3fb1d4696..61e0eead5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -1,2 +1,58 @@ -package com.twidere.twiderex.notification +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.notification +import android.net.Uri +import com.twidere.twiderex.model.MicroBlogKey + +enum class NotificationChannelSpec( + val id: String, + val showBadge: Boolean = false, + val grouped: Boolean = false +) { + /** + * For notifications indicate that some lengthy operations are performing in the background. + * Such as sending attachment process. + */ + BackgroundProgresses( + "background_progresses", + ), + + ContentInteractions( + "content_interactions", + showBadge = true, + grouped = true + ), + + ContentMessages( + "content_messages", + showBadge = true, + grouped = true + ) +} + +fun MicroBlogKey.notificationChannelId(id: String): String { + return "${id}_${Uri.encode(toString())}" +} + +fun MicroBlogKey.notificationChannelGroupId(): String { + return Uri.encode(toString()) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index a229f8a3d..a90fa30d3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -27,6 +27,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -67,6 +68,7 @@ class MediaViewModel( } val loading = MutableStateFlow(false) + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { if (it != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index 7185b6f41..afe91491a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -38,6 +39,7 @@ class StatusViewModel( private val account by lazy { accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { if (it != null) { @@ -48,6 +50,7 @@ class StatusViewModel( }.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { if (it != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 3b3536d5c..b498c001c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce @@ -45,7 +46,7 @@ class ComposeSearchUserViewModel( val text = MutableStateFlow("") - @OptIn(FlowPreview::class) + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { str -> account.flatMapLatest { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 0d04c7aed..52f16bca7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -45,6 +45,7 @@ import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.MastodonEmojiCache import com.twidere.twiderex.utils.notifyError import com.twitter.twittertext.Extractor +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow @@ -163,6 +164,7 @@ open class ComposeViewModel( open val draftId: String = UUID.randomUUID().toString() + @OptIn(ExperimentalCoroutinesApi::class) val emojis by lazy { account.flatMapLatest { it?.let { account -> @@ -243,6 +245,7 @@ open class ComposeViewModel( .asStateIn(viewModelScope, false) val locationEnabled = MutableStateFlow(false) val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { if (statusKey != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 22fbdfa65..60b7682e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -28,6 +28,8 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow @@ -44,6 +46,7 @@ class MastodonComposeSearchHashtagViewModel( val text = MutableStateFlow("") + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { str -> account.flatMapLatest { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 3b9ca6d79..bf55a8913 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.services.microblog.LookupService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -39,6 +40,7 @@ class DMConversationViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 8372375b4..f04b38eeb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -33,6 +33,7 @@ import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull @@ -53,6 +54,7 @@ class DMEventViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val conversation by lazy { account.flatMapLatest { it?.let { account -> @@ -64,6 +66,7 @@ class DMEventViewModel( } } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index a52bcd687..c2b782772 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -31,6 +31,8 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow @@ -50,6 +52,7 @@ class DMNewConversationViewModel( val input = MutableStateFlow("") + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) val sourceFlow = input.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { str -> account.flatMapLatest { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index e850d3e5f..1ba158d9f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -28,6 +28,8 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow @@ -45,6 +47,7 @@ class ListsSearchUserViewModel( val text = MutableStateFlow("") + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { account.flatMapLatest { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index 1817db1dc..a2d126d80 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -40,6 +41,7 @@ class ListsTimelineViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 14eea0259..e4bdf4f4e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -32,6 +32,7 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.utils.notifyError import com.twidere.twiderex.viewmodel.user.UserListViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -52,6 +53,7 @@ class ListsUserViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) private val members by lazy { account.flatMapLatest { it?.let { account -> @@ -64,6 +66,7 @@ class ListsUserViewModel( }.cachedIn(viewModelScope) } + @OptIn(ExperimentalCoroutinesApi::class) private val subscribers by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 28a78ec7a..a3534ab97 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -31,6 +31,7 @@ import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.utils.notifyError +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull @@ -49,6 +50,7 @@ class ListsViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> @@ -60,6 +62,7 @@ class ListsViewModel( }.cachedIn(viewModelScope) } + @OptIn(ExperimentalCoroutinesApi::class) val ownerSource by lazy { account.flatMapLatest { it?.let { account -> @@ -70,6 +73,7 @@ class ListsViewModel( }.cachedIn(viewModelScope) } + @OptIn(ExperimentalCoroutinesApi::class) val subscribedSource by lazy { account.flatMapLatest { it?.let { account -> @@ -152,6 +156,7 @@ class ListsModifyViewModel( var editDesc = MutableStateFlow("") var editPrivate = MutableStateFlow(false) + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 907fcbdaf..2f60d096f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -39,6 +40,7 @@ class MastodonHashtagViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 45181796a..f86888eb0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -41,6 +42,7 @@ class MastodonSearchHashtagViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index a80fa2bae..dde1157c7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -24,6 +24,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -40,6 +41,7 @@ class SearchInputViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { @@ -48,6 +50,7 @@ class SearchInputViewModel( } } + @OptIn(ExperimentalCoroutinesApi::class) val savedSource by lazy { account.flatMapLatest { it?.let { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 375b1bff7..39929427f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map @@ -43,6 +44,7 @@ class SearchTweetsViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index f6f14845e..cc146c0e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -42,6 +43,7 @@ class SearchUserViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index ec2a7d942..13db48818 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.lastOrNull @@ -45,6 +46,7 @@ class AccountNotificationViewModel( }.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val isNotificationEnabled by lazy { preferences.flatMapLatest { it?.isNotificationEnabled ?: flowOf(false) } .asStateIn(viewModelScope, false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 0c95f55e9..1fafb1775 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -31,6 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.paging.toUi +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -43,6 +44,7 @@ import moe.tlaster.precompose.viewmodel.viewModelScope abstract class TimelineViewModel( private val dataStore: DataStore ) : ViewModel() { + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { pagingMediator.flatMapLatest { it?.pager()?.toUi() ?: emptyFlow() @@ -50,9 +52,11 @@ abstract class TimelineViewModel( } abstract val pagingMediator: Flow abstract val savedStateKey: Flow + @OptIn(ExperimentalCoroutinesApi::class) val loadingBetween: Flow> get() = pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + @OptIn(ExperimentalCoroutinesApi::class) val timelineScrollState by lazy { savedStateKey.flatMapLatest { val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index cd3479259..a1a23ea2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.services.microblog.TrendService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -38,6 +39,7 @@ class TrendViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { if (it != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index e36ec640d..9555e7b5f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.viewmodel.twitter import com.twidere.services.twitter.TwitterOAuthService import com.twidere.services.twitter.TwitterService -import com.twidere.twiderex.BuildConfig +import com.twidere.twiderex.DefaultConfig import com.twidere.twiderex.dataprovider.mapper.toAmUser import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.http.TwidereServiceFactory @@ -134,7 +134,7 @@ class TwitterSignInViewModel( } private fun isBuiltInKey(): Boolean { - return consumerKey == BuildConfig.CONSUMERKEY && consumerSecret == BuildConfig.CONSUMERSECRET + return consumerKey == DefaultConfig.ConsumerKey && consumerSecret == DefaultConfig.ConsumerSecret } fun cancel() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index a2978be93..0a9fe7317 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.services.microblog.SearchService import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -39,6 +40,7 @@ class TwitterSearchMediaViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { it?.let { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 76529629a..0e5badc5e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.viewModelScope @@ -39,6 +40,7 @@ class FollowersViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) override val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index d7a48b747..54005cd84 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.viewModelScope @@ -39,6 +40,7 @@ class FollowingViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) override val source by lazy { account.flatMapLatest { it?.let { account -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index ad2389508..3b37c468a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel @@ -40,6 +41,7 @@ class UserFavouriteTimelineViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { if (it != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index dfcf50d66..d9658d3ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -34,6 +34,7 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.user.UserMediaMediator import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -50,6 +51,7 @@ class UserMediaTimelineViewModel( repository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val source: Flow>> by lazy { pagingMediator.flatMapLatest { it?.let { pagingMediator -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 73c2fe259..d0699dad4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow @@ -48,6 +49,7 @@ class UserTimelineViewModel( _excludeReplies.value = value } + @OptIn(FlowPreview::class) val source by lazy { combine(account, _excludeReplies) { account, excludeReplies -> if (account != null) { From 4fbf24a01e7dfacd80d33d4e2af4d5577c5ad14f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 6 Sep 2021 14:56:51 +0800 Subject: [PATCH 130/615] add gif service and giphy api --- apiKey.properties | 3 +- app/build.gradle.kts | 1 + .../twiderex/http/TwidereServiceFactory.kt | 12 + .../com/twidere/services/gif/GifService.kt | 29 + .../services/gif/giphy/GiphyPagingResponse.kt | 542 ++++++++++++++++++ .../services/gif/giphy/GiphyResource.kt | 46 ++ .../services/gif/giphy/GiphyService.kt | 85 +++ .../com/twidere/services/gif/model/IGif.kt | 30 + 8 files changed, 747 insertions(+), 1 deletion(-) create mode 100644 services/src/main/java/com/twidere/services/gif/GifService.kt create mode 100644 services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt create mode 100644 services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt create mode 100644 services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt create mode 100644 services/src/main/java/com/twidere/services/gif/model/IGif.kt diff --git a/apiKey.properties b/apiKey.properties index 4edc1cef0..08ad1762e 100644 --- a/apiKey.properties +++ b/apiKey.properties @@ -1,2 +1,3 @@ ConsumerKey="wmtrtTaVOjUnH5pWQp4LDI5Qs" -ConsumerSecret="E9Q9u2yK0COJae2tLcNEdY75OPA3bxqJiGZQztraHaQUtoI2cu" \ No newline at end of file +ConsumerSecret="E9Q9u2yK0COJae2tLcNEdY75OPA3bxqJiGZQztraHaQUtoI2cu" +GiphyKey = "uxJ0w7FGkoluJ6ZYd3dUX68y7EjlSDAd" \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 977f05f53..3742a48d8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -66,6 +66,7 @@ android { apiKeyProp.load(apiKeyProperties.inputStream()) buildConfigField("String", "CONSUMERKEY", apiKeyProp.getProperty("ConsumerKey")) buildConfigField("String", "CONSUMERSECRET", apiKeyProp.getProperty("ConsumerSecret")) + buildConfigField("String", "GIPHYKEY", apiKeyProp.getProperty("GiphyKey")) } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt index 667624511..0c3b8e68d 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt @@ -20,11 +20,14 @@ */ package com.twidere.twiderex.http +import com.twidere.services.gif.GifService +import com.twidere.services.gif.giphy.GiphyService import com.twidere.services.http.HttpClientFactory import com.twidere.services.http.config.HttpConfigClientFactory import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.MicroBlogService import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.BuildConfig import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.Credentials import com.twidere.twiderex.model.cred.OAuth2Credentials @@ -78,5 +81,14 @@ class TwidereServiceFactory(private val configProvider: TwidereHttpConfigProvide HttpConfigClientFactory(it.configProvider) } ?: throw Error("Factory needs to be initiate") } + + fun createGifService(): GifService { + return instance?.let { + GiphyService( + apiKey = BuildConfig.GIPHYKEY, + httpClientFactory = createHttpClientFactory() + ) + } ?: throw Error("Factory needs to be initiate") + } } } diff --git a/services/src/main/java/com/twidere/services/gif/GifService.kt b/services/src/main/java/com/twidere/services/gif/GifService.kt new file mode 100644 index 000000000..cfc1bdb7d --- /dev/null +++ b/services/src/main/java/com/twidere/services/gif/GifService.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.gif + +import com.twidere.services.gif.model.GifPaging + +interface GifService { + suspend fun trending(nextPage: String, limit: Int = 25): GifPaging + + suspend fun search(query: String, lang: String = "en", nextPage: String, limit: Int = 25): GifPaging +} diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt new file mode 100644 index 000000000..19676accd --- /dev/null +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt @@ -0,0 +1,542 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.gif.giphy + +import com.twidere.services.gif.model.IGif +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class GiphyPagingResponse( + @SerialName("data") + val `data`: List? = null, + @SerialName("meta") + val meta: Meta? = null, + @SerialName("pagination") + val pagination: Pagination? = null +) { + + @Serializable + data class Meta( + @SerialName("msg") + val msg: String? = null, + @SerialName("response_id") + val responseId: String? = null, + @SerialName("status") + val status: Int? = null + ) + + @Serializable + data class Pagination( + @SerialName("count") + val count: Int? = null, + @SerialName("offset") + val offset: Int? = null, + @SerialName("total_count") + val totalCount: Int? = null + ) +} + +@Serializable +data class GifObject( + @SerialName("analytics") + val analytics: Analytics? = null, + @SerialName("analytics_response_payload") + val analyticsResponsePayload: String? = null, + @SerialName("bitly_gif_url") + val bitlyGifUrl: String? = null, + @SerialName("bitly_url") + val bitlyUrl: String? = null, + @SerialName("content_url") + val contentUrl: String? = null, + @SerialName("embed_url") + val embedUrl: String? = null, + @SerialName("id") + val id: String? = null, + @SerialName("images") + val images: Images? = null, + @SerialName("import_datetime") + val importDatetime: String? = null, + @SerialName("is_sticker") + val isSticker: Int? = null, + @SerialName("rating") + val rating: String? = null, + @SerialName("slug") + val slug: String? = null, + @SerialName("source") + val source: String? = null, + @SerialName("source_post_url") + val sourcePostUrl: String? = null, + @SerialName("source_tld") + val sourceTld: String? = null, + @SerialName("title") + val title: String? = null, + @SerialName("trending_datetime") + val trendingDatetime: String? = null, + @SerialName("type") + val type: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("user") + val user: User? = null, + @SerialName("username") + val username: String? = null +) : IGif { + @Serializable + data class Analytics( + @SerialName("onclick") + val onclick: Onclick? = null, + @SerialName("onload") + val onload: Onload? = null, + @SerialName("onsent") + val onsent: Onsent? = null + ) { + @Serializable + data class Onclick( + @SerialName("url") + val url: String? = null + ) + + @Serializable + data class Onload( + @SerialName("url") + val url: String? = null + ) + + @Serializable + data class Onsent( + @SerialName("url") + val url: String? = null + ) + } + + @Serializable + data class Images( + @SerialName("downsized") + val downsized: Downsized? = null, + @SerialName("downsized_large") + val downsizedLarge: DownsizedLarge? = null, + @SerialName("downsized_medium") + val downsizedMedium: DownsizedMedium? = null, + @SerialName("downsized_small") + val downsizedSmall: DownsizedSmall? = null, + @SerialName("downsized_still") + val downsizedStill: DownsizedStill? = null, + @SerialName("fixed_height") + val fixedHeight: FixedHeight? = null, + @SerialName("fixed_height_downsampled") + val fixedHeightDownsampled: FixedHeightDownsampled? = null, + @SerialName("fixed_height_small") + val fixedHeightSmall: FixedHeightSmall? = null, + @SerialName("fixed_height_small_still") + val fixedHeightSmallStill: FixedHeightSmallStill? = null, + @SerialName("fixed_height_still") + val fixedHeightStill: FixedHeightStill? = null, + @SerialName("fixed_width") + val fixedWidth: FixedWidth? = null, + @SerialName("fixed_width_downsampled") + val fixedWidthDownsampled: FixedWidthDownsampled? = null, + @SerialName("fixed_width_small") + val fixedWidthSmall: FixedWidthSmall? = null, + @SerialName("fixed_width_small_still") + val fixedWidthSmallStill: FixedWidthSmallStill? = null, + @SerialName("fixed_width_still") + val fixedWidthStill: FixedWidthStill? = null, + @SerialName("hd") + val hd: Hd? = null, + @SerialName("looping") + val looping: Looping? = null, + @SerialName("original") + val original: Original? = null, + @SerialName("original_mp4") + val originalMp4: OriginalMp4? = null, + @SerialName("original_still") + val originalStill: OriginalStill? = null, + @SerialName("preview") + val preview: Preview? = null, + @SerialName("preview_gif") + val previewGif: PreviewGif? = null, + @SerialName("preview_webp") + val previewWebp: PreviewWebp? = null, + @SerialName("480w_still") + val wStill: WStill? = null + ) { + @Serializable + data class Downsized( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class DownsizedLarge( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class DownsizedMedium( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class DownsizedSmall( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class DownsizedStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedHeight( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedHeightDownsampled( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedHeightSmall( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedHeightSmallStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedHeightStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedWidth( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedWidthDownsampled( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedWidthSmall( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedWidthSmallStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class FixedWidthStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class Hd( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class Looping( + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null + ) + + @Serializable + data class Original( + @SerialName("frames") + val frames: String? = null, + @SerialName("hash") + val hash: String? = null, + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("webp") + val webp: String? = null, + @SerialName("webp_size") + val webpSize: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class OriginalMp4( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class OriginalStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class Preview( + @SerialName("height") + val height: String? = null, + @SerialName("mp4") + val mp4: String? = null, + @SerialName("mp4_size") + val mp4Size: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class PreviewGif( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class PreviewWebp( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + + @Serializable + data class WStill( + @SerialName("height") + val height: String? = null, + @SerialName("size") + val size: String? = null, + @SerialName("url") + val url: String? = null, + @SerialName("width") + val width: String? = null + ) + } + + @Serializable + data class User( + @SerialName("avatar_url") + val avatarUrl: String? = null, + @SerialName("banner_image") + val bannerImage: String? = null, + @SerialName("banner_url") + val bannerUrl: String? = null, + @SerialName("description") + val description: String? = null, + @SerialName("display_name") + val displayName: String? = null, + @SerialName("instagram_url") + val instagramUrl: String? = null, + @SerialName("is_verified") + val isVerified: Boolean? = null, + @SerialName("profile_url") + val profileUrl: String? = null, + @SerialName("username") + val username: String? = null, + @SerialName("website_url") + val websiteUrl: String? = null + ) +} diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt new file mode 100644 index 000000000..641e45a58 --- /dev/null +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.gif.giphy + +import retrofit2.http.GET +import retrofit2.http.Query + +internal interface GiphyResource { + @GET("/v1/gifs/trending") + suspend fun getTrending( + @Query("api_key") apiKey: String, + @Query("offset") offset: Int, + @Query("limit") limit: Int, + @Query("rating") rating: String? = null, + @Query("random_id") random_id: String? = null, + ): GiphyPagingResponse + + @GET("/v1/gifs/search") + suspend fun search( + @Query("q") query: String, + @Query("api_key") apiKey: String, + @Query("offset") offset: Int, + @Query("limit") limit: Int, + @Query("lang") lang: String, + @Query("rating") rating: String? = null, + @Query("random_id") random_id: String? = null, + ): GiphyPagingResponse +} diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt new file mode 100644 index 000000000..3ebe4e13a --- /dev/null +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.gif.giphy + +import com.twidere.services.gif.GifService +import com.twidere.services.gif.model.GifPaging +import com.twidere.services.http.HttpClientFactory +import com.twidere.services.http.authorization.Authorization + +private const val GIPHY_BASE_URL = "https://api.giphy.com/" +class GiphyService( + private val apiKey: String, + private val httpClientFactory: HttpClientFactory, +) : GifService { + private val resource: GiphyResource + get() = httpClientFactory.createResources( + clazz = GiphyResource::class.java, + baseUrl = GIPHY_BASE_URL, + useCache = true, + authorization = EmptyAuthorization() + ) + + override suspend fun trending(nextPage: String, limit: Int) = resource.getTrending( + apiKey = apiKey, + limit = limit, + offset = nextPage.toInt() + ).let { + GifPaging( + data = it.data ?: emptyList(), + nextPage = generateNextPage(it.pagination) + ) + } + + private fun generateNextPage(pagination: GiphyPagingResponse.Pagination?): String? { + return pagination?.let { + if (it.count != null && it.offset != null && it.totalCount != null) { + val nextOffset = it.count + it.offset + if (nextOffset < it.totalCount) + nextOffset.toString() + else null + } else null + } + } + + override suspend fun search( + query: String, + lang: String, + nextPage: String, + limit: Int + ) = resource.search( + apiKey = apiKey, + limit = limit, + offset = nextPage.toInt(), + query = query, + lang = lang + ).let { + GifPaging( + data = it.data ?: emptyList(), + nextPage = generateNextPage(it.pagination) + ) + } + + class EmptyAuthorization : Authorization { + override val hasAuthorization: Boolean + get() = false + } +} diff --git a/services/src/main/java/com/twidere/services/gif/model/IGif.kt b/services/src/main/java/com/twidere/services/gif/model/IGif.kt new file mode 100644 index 000000000..689f12705 --- /dev/null +++ b/services/src/main/java/com/twidere/services/gif/model/IGif.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.gif.model + +import com.twidere.services.microblog.model.IPaging + +interface IGif + +class GifPaging( + data: List, + override val nextPage: String? = null +) : ArrayList(data), IPaging From 13e0037df9fa2c364df79047978250f486f1ee60 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 6 Sep 2021 15:39:07 +0800 Subject: [PATCH 131/615] fix android references in common --- .../com/twidere/twiderex/kmp/FileResolver.kt | 11 ++++++++++ .../twiderex/jobs/dm/DirectMessageSendJob.kt | 22 +++++++------------ .../com/twidere/twiderex/kmp/FileResolver.kt | 7 ++++++ .../notification/NotificationChannelSpec.kt | 6 ++--- .../twiderex/viewmodel/MediaViewModel.kt | 5 ++--- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 3 +-- .../com/twidere/twiderex/kmp/FileResolver.kt | 4 ++++ .../notification/AppNotificationManager.kt | 2 ++ 8 files changed, 38 insertions(+), 22 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 6bcfa99dc..f91d2e1d0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.kmp import android.content.ContentResolver +import android.graphics.BitmapFactory import android.net.Uri import java.io.InputStream import java.io.OutputStream @@ -41,4 +42,14 @@ actual class FileResolver(private val contentResolver: ContentResolver) { actual fun openOutputStream(file: String): OutputStream? { return contentResolver.openOutputStream(Uri.parse(file)) } + + actual fun getMediaSize(file: String): MediaSize { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(file, options) + return MediaSize( + width = options.outWidth.toLong(), + height = options.outHeight.toLong() + ) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index fac370c7b..347e6896c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -20,10 +20,10 @@ */ package com.twidere.twiderex.jobs.dm -import android.graphics.BitmapFactory import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.MediaSize import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey @@ -134,15 +134,17 @@ abstract class DirectMessageSendJob( recipientAccountKey = sendData.recipientUserKey, sendStatus = UiDMEvent.SendStatus.PENDING, media = images.mapIndexed { index, uri -> - val imageSize = getImageSize(URI.create(uri).path) + val imageSize = URI.create(uri).path?.let { + getImageSize(it) + } UiMedia( belongToKey = sendData.draftMessageKey, url = uri, mediaUrl = uri, previewUrl = uri, type = getMediaType(uri), - width = imageSize[0], - height = imageSize[1], + width = imageSize?.width ?: 0L, + height = imageSize?.height ?: 0L, altText = "", order = index, pageUrl = null, @@ -171,16 +173,8 @@ abstract class DirectMessageSendJob( } } - private fun getImageSize(path: String?): Array { - return path?.let { - val options = BitmapFactory.Options() - options.inJustDecodeBounds = true - BitmapFactory.decodeFile(it, options) - arrayOf( - options.outWidth.toLong(), - options.outHeight.toLong() - ) - } ?: arrayOf(0, 0) + private fun getImageSize(path: String): MediaSize { + return fileResolver.getMediaSize(path) } protected abstract suspend fun sendMessage( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 8e9fffc10..63c0df31b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -31,4 +31,11 @@ expect class FileResolver { fun openInputStream(file: String): InputStream? fun openOutputStream(file: String): OutputStream? + + fun getMediaSize(file: String): MediaSize } + +data class MediaSize( + val width: Long, + val height: Long, +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index 61e0eead5..8f1b4b894 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.notification -import android.net.Uri import com.twidere.twiderex.model.MicroBlogKey +import java.net.URLEncoder enum class NotificationChannelSpec( val id: String, @@ -50,9 +50,9 @@ enum class NotificationChannelSpec( } fun MicroBlogKey.notificationChannelId(id: String): String { - return "${id}_${Uri.encode(toString())}" + return "${id}_${URLEncoder.encode(toString(), "UTF-8")}" } fun MicroBlogKey.notificationChannelGroupId(): String { - return Uri.encode(toString()) + return URLEncoder.encode(toString(), "UTF-8") } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index a90fa30d3..2960336b9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel -import android.net.Uri import com.twidere.twiderex.action.MediaAction import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey @@ -46,13 +45,13 @@ class MediaViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } - fun saveFile(currentMedia: UiMedia, target: Uri) = viewModelScope.launch { + fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { val account = account.lastOrNull() ?: return@launch currentMedia.mediaUrl?.let { mediaAction.download( accountKey = account.accountKey, source = it, - target = target.toString() + target = target ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index f04b38eeb..450364953 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.dm -import android.net.Uri import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService @@ -82,7 +81,7 @@ class DMEventViewModel( // input val input = MutableStateFlow("") - val inputImage = MutableStateFlow(null) + val inputImage = MutableStateFlow(null) val firstEventKey = MutableStateFlow(null) val pendingActionMessage = MutableStateFlow(null) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index c1f8b3b9d..de0714204 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -39,4 +39,8 @@ actual class FileResolver { actual fun openOutputStream(file: String): OutputStream? { TODO("Not yet implemented") } + + actual fun getMediaSize(file: String): MediaSize { + TODO("Not yet implemented") + } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 8733b37e4..6310502d6 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.notification import kotlin.time.Duration +import kotlin.time.ExperimentalTime actual class AppNotificationManager { actual fun notify( @@ -29,6 +30,7 @@ actual class AppNotificationManager { ) { } + @OptIn(ExperimentalTime::class) actual fun notifyTransient( notificationId: Int, appNotification: AppNotification, From b42040b845a048389cbf99dc7665143eb3b8b985 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 6 Sep 2021 16:19:32 +0800 Subject: [PATCH 132/615] add gif paging source and repository --- .../com/twidere/twiderex/db/mapper/IGif.kt | 37 ++++++++++++ .../com/twidere/twiderex/model/ui/UiGif.kt | 26 +++++++++ .../paging/source/gif/GifPagingSource.kt | 46 +++++++++++++++ .../source/gif/GifSearchPagingSource.kt | 36 ++++++++++++ .../source/gif/GifTrendingPagingSource.kt | 27 +++++++++ .../twiderex/repository/GifRepository.kt | 58 +++++++++++++++++++ .../com/twidere/services/gif/GifService.kt | 4 +- .../services/gif/giphy/GiphyService.kt | 8 +-- 8 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt new file mode 100644 index 000000000..5ee3a2ac3 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt @@ -0,0 +1,37 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.mapper + +import com.twidere.services.gif.giphy.GifObject +import com.twidere.services.gif.model.IGif +import com.twidere.twiderex.model.ui.UiGif + +typealias GiphyGif = GifObject + +fun IGif.toUi(): UiGif { + return when (this) { + is GiphyGif -> UiGif( + url = this.images?.original?.url ?: "", + preview = this.images?.previewGif?.url ?: "" + ) + else -> throw NotImplementedError() + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt new file mode 100644 index 000000000..7581349a5 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt @@ -0,0 +1,26 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.ui + +data class UiGif( + val url: String, + val preview: String, +) diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt new file mode 100644 index 000000000..bc3d5288d --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source.gif + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.twidere.services.gif.GifService +import com.twidere.services.gif.model.GifPaging +import com.twidere.twiderex.db.mapper.toUi +import com.twidere.twiderex.model.ui.UiGif + +abstract class GifPagingSource(protected val service: GifService) : PagingSource() { + override fun getRefreshKey(state: PagingState): String? { + return null + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val result = loadFromService(params.key, params.loadSize) + val nextPage = result.nextPage + LoadResult.Page(data = result.map { it.toUi() }, prevKey = null, nextKey = nextPage) + } catch (e: Throwable) { + LoadResult.Error(e) + } + } + + abstract suspend fun loadFromService(key: String?, loadSize: Int): GifPaging +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt new file mode 100644 index 000000000..dc6e28d9b --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source.gif + +import com.twidere.services.gif.GifService + +class GifSearchPagingSource( + gifService: GifService, + private val query: String, + private val lang: String +) : GifPagingSource(gifService) { + override suspend fun loadFromService(key: String?, loadSize: Int) = service.search( + nextPage = key, + limit = loadSize, + query = query, + lang = lang + ) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt new file mode 100644 index 000000000..6569b2bfd --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.source.gif + +import com.twidere.services.gif.GifService + +class GifTrendingPagingSource(gifService: GifService) : GifPagingSource(gifService) { + override suspend fun loadFromService(key: String?, loadSize: Int) = service.trending(nextPage = key, limit = loadSize) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt b/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt new file mode 100644 index 000000000..5695694b6 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import com.twidere.services.gif.GifService +import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.paging.source.gif.GifSearchPagingSource +import com.twidere.twiderex.paging.source.gif.GifTrendingPagingSource + +class GifRepository { + fun gifTrending(service: GifService) = Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + GifTrendingPagingSource( + service + ) + }.flow + + fun gifSearch( + service: GifService, + query: String, + lang: String + ) = Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + GifSearchPagingSource( + service, + query = query, + lang = lang + ) + }.flow +} diff --git a/services/src/main/java/com/twidere/services/gif/GifService.kt b/services/src/main/java/com/twidere/services/gif/GifService.kt index cfc1bdb7d..b797f4cfd 100644 --- a/services/src/main/java/com/twidere/services/gif/GifService.kt +++ b/services/src/main/java/com/twidere/services/gif/GifService.kt @@ -23,7 +23,7 @@ package com.twidere.services.gif import com.twidere.services.gif.model.GifPaging interface GifService { - suspend fun trending(nextPage: String, limit: Int = 25): GifPaging + suspend fun trending(nextPage: String?, limit: Int = 25): GifPaging - suspend fun search(query: String, lang: String = "en", nextPage: String, limit: Int = 25): GifPaging + suspend fun search(query: String, lang: String = "en", nextPage: String?, limit: Int = 25): GifPaging } diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt index 3ebe4e13a..3bdcc6042 100644 --- a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt @@ -38,10 +38,10 @@ class GiphyService( authorization = EmptyAuthorization() ) - override suspend fun trending(nextPage: String, limit: Int) = resource.getTrending( + override suspend fun trending(nextPage: String?, limit: Int) = resource.getTrending( apiKey = apiKey, limit = limit, - offset = nextPage.toInt() + offset = nextPage?.toInt() ?: 0 ).let { GifPaging( data = it.data ?: emptyList(), @@ -63,12 +63,12 @@ class GiphyService( override suspend fun search( query: String, lang: String, - nextPage: String, + nextPage: String?, limit: Int ) = resource.search( apiKey = apiKey, limit = limit, - offset = nextPage.toInt(), + offset = nextPage?.toInt() ?: 0, query = query, lang = lang ).let { From 7ef8b16ac40aa0ea921e72b98342ddbe1b959f5f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 6 Sep 2021 17:59:23 +0800 Subject: [PATCH 133/615] add gif scene and viewmodel --- .../component/lazy/ui/LazyUiGifList.kt | 117 +++++++++++++ .../component/media/MediaInsertMenu.kt | 13 +- .../twidere/twiderex/di/RepositoryModule.kt | 4 + .../http/TwidereNetworkImageLoader.kt | 24 --- .../com/twidere/twiderex/navigation/Root.kt | 4 + .../com/twidere/twiderex/navigation/Route.kt | 5 + .../twiderex/scenes/compose/ComposeScene.kt | 27 ++- .../twidere/twiderex/scenes/gif/GifScene.kt | 163 ++++++++++++++++++ .../twiderex/viewmodel/gif/GifViewModel.kt | 65 +++++++ .../{ic_media_tag_bg.xml => ic_gif_tag.xml} | 0 10 files changed, 395 insertions(+), 27 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt create mode 100644 app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt rename app/src/main/res/drawable/{ic_media_tag_bg.xml => ic_gif_tag.xml} (100%) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt b/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt new file mode 100644 index 000000000..23a72bd3d --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt @@ -0,0 +1,117 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.lazy.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.paging.compose.LazyPagingItems +import com.twidere.twiderex.R +import com.twidere.twiderex.component.foundation.NetworkImage +import com.twidere.twiderex.component.lazy.itemsPagingGridIndexed +import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiGif + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun LazyUiGifList( + modifier: Modifier = Modifier, + items: LazyPagingItems, + selectedItem: UiGif?, + state: LazyListState = rememberLazyListState(), + onItemSelected: (UiGif) -> Unit = {}, + header: LazyListScope.() -> Unit = {}, +) { + LazyUiList(items = items) { + LazyColumn( + modifier = modifier, + state = state, + ) { + header.invoke(this) + itemsPagingGridIndexed( + data = items, + rowSize = 2, + spacing = LazyUiGifListDefaults.Spacing + ) { _, item -> + item?.let { + val selected = selectedItem?.url == it.url + Box( + modifier = Modifier.clip(MaterialTheme.shapes.medium) + .clickable { onItemSelected(item) } + .then( + if (selected) Modifier.border( + LazyUiGifListDefaults.Border, color = MaterialTheme.colors.primary, + shape = MaterialTheme.shapes.medium + ) else Modifier + ) + ) { + NetworkImage( + data = it.preview, + Modifier.fillMaxWidth() + .aspectRatio(1f) + ) + Image( + painter = painterResource(id = R.drawable.ic_gif_tag), + contentDescription = MediaType.animated_gif.name, + modifier = Modifier.align(Alignment.BottomEnd) + .padding(LazyUiGifListDefaults.Tag.Padding) + .width(LazyUiGifListDefaults.Tag.Width) + .height(LazyUiGifListDefaults.Tag.Height) + ) + } + } + } + loadState(items.loadState.append) { + items.retry() + } + } + } +} + +private object LazyUiGifListDefaults { + object Tag { + val Width = 25.dp + val Height = 16.dp + val Padding = PaddingValues(end = 8.dp, bottom = 8.dp) + } + val Spacing = 16.dp + val Border = 3.dp +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index df24cda42..29ef9bce7 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -36,6 +36,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -43,7 +44,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.MediaInsertType +import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.utils.FileProviderHelper +import kotlinx.coroutines.launch import java.util.UUID @OptIn(ExperimentalMaterialApi::class) @@ -60,6 +64,8 @@ fun MediaInsertMenu( onResult(it) }, ) + val navController = LocalNavController.current + val scope = rememberCoroutineScope() var cameraTempUri by remember { mutableStateOf(Uri.EMPTY) @@ -102,7 +108,12 @@ fun MediaInsertMenu( videoRecordLauncher.launch(videoTempUri) } MediaInsertType.LIBRARY -> filePickerLauncher.launch(arrayOf("image/*", "video/*")) - MediaInsertType.GIF -> TODO() + MediaInsertType.GIF -> scope.launch { + navController.navigateForResult(RootRoute.Gif.Home) + ?.let { result -> + onResult(listOf(result as Uri)) + } + } } showDropdown = false } diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index eac42a046..f52f04378 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -30,6 +30,7 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.CacheRepository import com.twidere.twiderex.repository.DirectMessageRepository import com.twidere.twiderex.repository.DraftRepository +import com.twidere.twiderex.repository.GifRepository import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.repository.MediaRepository @@ -122,4 +123,7 @@ object RepositoryModule { fun provideDirectMediaRepository( database: CacheDatabase ) = MediaRepository(database = database) + + @Provides + fun provideGifRepository() = GifRepository() } diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt index 96af842a4..b7b885732 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.http import android.content.Context -import android.graphics.Bitmap -import android.media.MediaMetadataRetriever import android.net.Uri import coil.ImageLoader import coil.bitmap.BitmapPool @@ -37,7 +35,6 @@ import com.twidere.twiderex.model.cred.OAuthCredentials import com.twidere.twiderex.model.enums.PlatformType import okhttp3.Headers import okhttp3.Request -import java.lang.Exception import java.net.URL class TwidereNetworkImageLoader( @@ -91,30 +88,9 @@ class TwidereNetworkImageLoader( ) ).build() } else { - if (data is Uri && context.contentResolver.getType(data)?.startsWith("video") == true) { - data = getThumbVideo(context = context, videoUri = data) ?: data - } request.newBuilder(request.context) .data(data) .build() } } - - private fun getThumbVideo(context: Context, videoUri: Uri): Bitmap? { - var bitmap: Bitmap? = null - var mediaMetadataRetriever: MediaMetadataRetriever? = null - try { - mediaMetadataRetriever = MediaMetadataRetriever() - mediaMetadataRetriever.setDataSource(context, videoUri) - bitmap = mediaMetadataRetriever.getFrameAtTime( - 1000, - MediaMetadataRetriever.OPTION_CLOSEST_SYNC - ) - } catch (e: Exception) { - e.printStackTrace() - } finally { - mediaMetadataRetriever?.release() - } - return bitmap - } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt b/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt index 31073ac3f..64b2e916f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/navigation/Root.kt @@ -112,4 +112,8 @@ interface Root { fun Conversation(conversationKey: MicroBlogKey): String val NewConversation: String } + + interface Gif { + val Home: String + } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt index 444886c87..d14267eac 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -48,6 +48,7 @@ import com.twidere.twiderex.scenes.compose.DraftComposeScene import com.twidere.twiderex.scenes.dm.DMConversationListScene import com.twidere.twiderex.scenes.dm.DMConversationScene import com.twidere.twiderex.scenes.dm.DMNewConversationScene +import com.twidere.twiderex.scenes.gif.GifScene import com.twidere.twiderex.scenes.home.HomeTimelineScene import com.twidere.twiderex.scenes.home.MeScene import com.twidere.twiderex.scenes.home.MentionScene @@ -642,4 +643,8 @@ fun RouteBuilder.route(constraints: Constraints) { DMConversationScene(conversationKey = MicroBlogKey.valueOf(it)) } } + + authorizedScene(RootRouteDefinition.Gif.Home) { + GifScene() + } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index e14bf7afa..3b6ecd32f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -23,7 +23,9 @@ package com.twidere.twiderex.scenes.compose import android.Manifest import android.annotation.SuppressLint import android.content.Context +import android.graphics.Bitmap import android.location.Location +import android.media.MediaMetadataRetriever import android.net.Uri import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult @@ -153,6 +155,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import java.lang.Exception import kotlin.math.max @Composable @@ -1347,11 +1350,11 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = item) + NetworkImage(data = getThumb(context, item) ?: item) when (type) { MediaType.animated_gif -> Image( - painter = painterResource(id = R.drawable.ic_media_tag_bg), + painter = painterResource(id = R.drawable.ic_gif_tag), contentDescription = type.name, modifier = Modifier.align(Alignment.BottomStart) .width(ComposeImageDefaults.Tag.Width) @@ -1397,6 +1400,26 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex } } +private fun getThumb(context: Context, uri: Uri): Bitmap? { + return if (context.contentResolver.getType(uri)?.startsWith("video") == true) { + var bitmap: Bitmap? = null + var mediaMetadataRetriever: MediaMetadataRetriever? = null + try { + mediaMetadataRetriever = MediaMetadataRetriever() + mediaMetadataRetriever.setDataSource(context, uri) + bitmap = mediaMetadataRetriever.getFrameAtTime( + 1000, + MediaMetadataRetriever.OPTION_CLOSEST_SYNC + ) + } catch (e: Exception) { + e.printStackTrace() + } finally { + mediaMetadataRetriever?.release() + } + bitmap + } else null +} + private object ComposeImageDefaults { val ImageSize = 72.dp diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt new file mode 100644 index 000000000..27c2ebede --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt @@ -0,0 +1,163 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.scenes.gif + +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.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Divider +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.LocalContentColor +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Done +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +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.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.paging.compose.LazyPagingItems +import androidx.paging.compose.collectAsLazyPagingItems +import com.twidere.twiderex.R +import com.twidere.twiderex.component.foundation.AppBar +import com.twidere.twiderex.component.foundation.AppBarNavigationButton +import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.foundation.TextInput +import com.twidere.twiderex.component.lazy.ui.LazyUiGifList +import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.model.ui.UiGif +import com.twidere.twiderex.ui.TwidereScene +import com.twidere.twiderex.viewmodel.gif.GifViewModel + +@Composable +fun GifScene() { + val viewModel = assistedViewModel { + it.create() + } + val enable by viewModel.enable.collectAsState(initial = false) + TwidereScene { + InAppNotificationScaffold( + topBar = { + AppBar( + navigationIcon = { + AppBarNavigationButton() + }, + title = { + Text(text = "GIPHY") + }, + actions = { + IconButton( + onClick = { TODO("Download gif and return uri to caller") }, + enabled = enable + ) { + Icon( + imageVector = Icons.Default.Done, + contentDescription = stringResource(id = R.string.common_controls_actions_yes), + tint = if (enable) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + ) + } + } + ) + }, + ) { + GifContent(viewModel = viewModel) + } + } +} + +@Composable +private fun GifContent(viewModel: GifViewModel) { + val searchInput by viewModel.input.collectAsState() + val searchState by viewModel.searchFlow.collectAsState(initial = null) + val searchSource = searchState?.collectAsLazyPagingItems() + val trendingSource = viewModel.trendSource.collectAsLazyPagingItems() + val selectedItem by viewModel.selectedItem.collectAsState(initial = null) + Column { + SearchInput( + input = searchInput, + onValueChanged = { + viewModel.input.value = it + } + ) + Divider() + GifList( + data = if (searchInput.isNotEmpty() && searchSource != null) searchSource else trendingSource, + selectedItem = selectedItem, + onItemSelected = { viewModel.selectedItem.value = it } + ) + } +} + +@Composable +private fun SearchInput(modifier: Modifier = Modifier, input: String, onValueChanged: (value: String) -> Unit) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.padding( + SearchInputDefaults.ContentPadding + ) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_search), + contentDescription = stringResource( + id = R.string.scene_search_title + ) + ) + Spacer(modifier = Modifier.width(SearchInputDefaults.ContentSpacing)) + TextInput( + value = input, + onValueChange = onValueChanged, modifier = Modifier.weight(1f), + placeholder = { + Text(text = "Search GIF") + }, + maxLines = 1, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) + ) + } +} + +private object SearchInputDefaults { + val ContentPadding = PaddingValues(16.dp) + val ContentSpacing = 16.dp +} + +@Composable +private fun GifList(data: LazyPagingItems, selectedItem: UiGif?, onItemSelected: (UiGif) -> Unit = {}) { + LazyUiGifList( + items = data, + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + selectedItem = selectedItem, + onItemSelected = onItemSelected + ) +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt new file mode 100644 index 000000000..e881ba529 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt @@ -0,0 +1,65 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.gif + +import androidx.compose.ui.text.intl.Locale +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn +import com.twidere.twiderex.http.TwidereServiceFactory +import com.twidere.twiderex.model.ui.UiGif +import com.twidere.twiderex.repository.GifRepository +import dagger.assisted.AssistedInject +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.map + +class GifViewModel @AssistedInject constructor( + private val gifRepository: GifRepository, +) : ViewModel() { + + @dagger.assisted.AssistedFactory + interface AssistedFactory { + fun create(): GifViewModel + } + + val input = MutableStateFlow("") + + val selectedItem = MutableStateFlow(null) + + val enable = selectedItem.map { it != null } + + private val service = TwidereServiceFactory.createGifService() + + @OptIn(FlowPreview::class) + val searchFlow = input.debounce(666L).map { + it.takeIf { it.isNotEmpty() }?.let { query -> + gifRepository.gifSearch( + service = service, + query = query, + lang = Locale.current.language + ) + } + } + + val trendSource = gifRepository.gifTrending(service = service).cachedIn(viewModelScope) +} diff --git a/app/src/main/res/drawable/ic_media_tag_bg.xml b/app/src/main/res/drawable/ic_gif_tag.xml similarity index 100% rename from app/src/main/res/drawable/ic_media_tag_bg.xml rename to app/src/main/res/drawable/ic_gif_tag.xml From 5ec833711f614b98deb3c62b4af496e191a70084 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 6 Sep 2021 18:33:07 +0800 Subject: [PATCH 134/615] return uri to compose scene after gif selected --- .../com/twidere/twiderex/db/mapper/IGif.kt | 5 ++- .../twidere/twiderex/di/RepositoryModule.kt | 5 ++- .../com/twidere/twiderex/model/ui/UiGif.kt | 2 ++ .../twiderex/repository/GifRepository.kt | 12 ++++++- .../twidere/twiderex/scenes/gif/GifScene.kt | 31 +++++++++++++++++-- .../twiderex/viewmodel/gif/GifViewModel.kt | 26 ++++++++++++++++ .../com/twidere/services/gif/GifService.kt | 3 +- .../services/gif/giphy/GiphyService.kt | 18 +++++++++++ 8 files changed, 96 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt index 5ee3a2ac3..4ee5c4595 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt @@ -23,14 +23,17 @@ package com.twidere.twiderex.db.mapper import com.twidere.services.gif.giphy.GifObject import com.twidere.services.gif.model.IGif import com.twidere.twiderex.model.ui.UiGif +import java.util.UUID typealias GiphyGif = GifObject fun IGif.toUi(): UiGif { return when (this) { is GiphyGif -> UiGif( + id = this.id ?: UUID.randomUUID().toString(), url = this.images?.original?.url ?: "", - preview = this.images?.previewGif?.url ?: "" + preview = this.images?.previewGif?.url ?: "", + type = this.type ?: "gif" ) else -> throw NotImplementedError() } diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt index f52f04378..c49cec2d7 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt @@ -26,6 +26,7 @@ import coil.util.CoilUtils import com.twidere.services.nitter.NitterService import com.twidere.twiderex.db.AppDatabase import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.CacheRepository import com.twidere.twiderex.repository.DirectMessageRepository @@ -125,5 +126,7 @@ object RepositoryModule { ) = MediaRepository(database = database) @Provides - fun provideGifRepository() = GifRepository() + fun provideGifRepository( + fileResolver: FileResolver + ) = GifRepository(fileResolver = fileResolver) } diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt index 7581349a5..ebc15074f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt @@ -21,6 +21,8 @@ package com.twidere.twiderex.model.ui data class UiGif( + val id: String, val url: String, val preview: String, + val type: String ) diff --git a/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt b/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt index 5695694b6..1115e2dc3 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/repository/GifRepository.kt @@ -23,11 +23,15 @@ package com.twidere.twiderex.repository import androidx.paging.Pager import androidx.paging.PagingConfig import com.twidere.services.gif.GifService +import com.twidere.services.microblog.DownloadMediaService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.paging.source.gif.GifSearchPagingSource import com.twidere.twiderex.paging.source.gif.GifTrendingPagingSource -class GifRepository { +class GifRepository( + private val fileResolver: FileResolver +) { fun gifTrending(service: GifService) = Pager( config = PagingConfig( pageSize = defaultLoadCount, @@ -55,4 +59,10 @@ class GifRepository { lang = lang ) }.flow + + suspend fun download(target: String, source: String, service: DownloadMediaService) { + fileResolver.openOutputStream(target)?.use { + service.download(target = source).copyTo(it) + } ?: throw Error("Download failed") + } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt index 27c2ebede..97c1d5eb5 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.scenes.gif +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -42,20 +43,24 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiGifList import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.model.ui.UiGif +import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.gif.GifViewModel @@ -65,6 +70,9 @@ fun GifScene() { it.create() } val enable by viewModel.enable.collectAsState(initial = false) + val context = LocalContext.current + val navController = LocalNavController.current + val commitLoading by viewModel.commitLoading.collectAsState(initial = false) TwidereScene { InAppNotificationScaffold( topBar = { @@ -77,7 +85,14 @@ fun GifScene() { }, actions = { IconButton( - onClick = { TODO("Download gif and return uri to caller") }, + onClick = { + viewModel.commit( + context = context, + onSuccess = { + navController.goBackWith(it) + } + ) + }, enabled = enable ) { Icon( @@ -90,11 +105,23 @@ fun GifScene() { ) }, ) { - GifContent(viewModel = viewModel) + Box { + GifContent(viewModel = viewModel) + if (commitLoading) { + LoadingView() + } + } } } } +@Composable +fun LoadingView() { + Dialog(onDismissRequest = { }) { + LoadingProgress() + } +} + @Composable private fun GifContent(viewModel: GifViewModel) { val searchInput by viewModel.input.collectAsState() diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt index e881ba529..1cf435384 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.viewmodel.gif +import android.content.Context +import android.net.Uri import androidx.compose.ui.text.intl.Locale import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -27,11 +29,14 @@ import androidx.paging.cachedIn import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.model.ui.UiGif import com.twidere.twiderex.repository.GifRepository +import com.twidere.twiderex.utils.FileProviderHelper import dagger.assisted.AssistedInject import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import okhttp3.internal.notify class GifViewModel @AssistedInject constructor( private val gifRepository: GifRepository, @@ -62,4 +67,25 @@ class GifViewModel @AssistedInject constructor( } val trendSource = gifRepository.gifTrending(service = service).cachedIn(viewModelScope) + + private val _commitLoading = MutableStateFlow(false) + + val commitLoading get() = _commitLoading + + fun commit(context: Context, onSuccess: (uri: Uri) -> Unit) { + selectedItem.value?.let { + viewModelScope.launch { + _commitLoading.value = true + try { + val target = FileProviderHelper.getUriFromMedias("${it.id}.${it.type}", context) + gifRepository.download(target = target.toString(), source = it.url, service = service) + onSuccess(target) + } catch (e: Throwable) { + e.notify() + } finally { + _commitLoading.value = false + } + } + } + } } diff --git a/services/src/main/java/com/twidere/services/gif/GifService.kt b/services/src/main/java/com/twidere/services/gif/GifService.kt index b797f4cfd..e828f6621 100644 --- a/services/src/main/java/com/twidere/services/gif/GifService.kt +++ b/services/src/main/java/com/twidere/services/gif/GifService.kt @@ -21,8 +21,9 @@ package com.twidere.services.gif import com.twidere.services.gif.model.GifPaging +import com.twidere.services.microblog.DownloadMediaService -interface GifService { +interface GifService : DownloadMediaService { suspend fun trending(nextPage: String?, limit: Int = 25): GifPaging suspend fun search(query: String, lang: String = "en", nextPage: String?, limit: Int = 25): GifPaging diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt index 3bdcc6042..0713c371a 100644 --- a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt @@ -24,6 +24,9 @@ import com.twidere.services.gif.GifService import com.twidere.services.gif.model.GifPaging import com.twidere.services.http.HttpClientFactory import com.twidere.services.http.authorization.Authorization +import com.twidere.services.utils.await +import okhttp3.Request +import java.io.InputStream private const val GIPHY_BASE_URL = "https://api.giphy.com/" class GiphyService( @@ -78,6 +81,21 @@ class GiphyService( ) } + override suspend fun download(target: String): InputStream { + return httpClientFactory.createHttpClientBuilder() + .build() + .newCall( + Request + .Builder() + .url(target) + .get() + .build() + ) + .await() + .body + ?.byteStream() ?: throw IllegalArgumentException() + } + class EmptyAuthorization : Authorization { override val hasAuthorization: Boolean get() = false From ab766d409e25e8c1912ea8f63df00b7f5d04569e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 7 Sep 2021 15:14:36 +0800 Subject: [PATCH 135/615] add media insert mode for compose --- .../component/media/MediaInsertMenu.kt | 126 +++++++++++------- .../twiderex/model/ui/UiMediaInsert.kt | 29 ++++ .../twiderex/scenes/compose/ComposeScene.kt | 44 +++--- .../viewmodel/compose/ComposeViewModel.kt | 70 +++++++++- 4 files changed, 193 insertions(+), 76 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index 29ef9bce7..27479515a 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.component.media +import android.content.Context import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -30,6 +31,8 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.ListItem +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -44,24 +47,35 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.MediaInsertType +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.utils.FileProviderHelper import kotlinx.coroutines.launch import java.util.UUID +private const val VideoSuffix = ".mp4" + @OptIn(ExperimentalMaterialApi::class) @Composable fun MediaInsertMenu( modifier: Modifier = Modifier, + supportMultipleSelect: Boolean = true, + librariesSupported: Array = arrayOf("image/*", "video/*"), disableList: List = emptyList(), - onResult: (List) -> Unit + onResult: (List) -> Unit ) { val context = LocalContext.current - val filePickerLauncher = rememberLauncherForActivityResult( + val filePickerLauncher = if (supportMultipleSelect) rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenMultipleDocuments(), onResult = { - onResult(it) + onResult(it.filterNotNull().toUi(context)) + }, + ) else rememberLauncherForActivityResult( + contract = ActivityResultContracts.OpenDocument(), + onResult = { + onResult(listOfNotNull(it).toUi(context)) }, ) val navController = LocalNavController.current @@ -74,7 +88,7 @@ fun MediaInsertMenu( val cameraLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.TakePicture(), onResult = { - if (it) onResult(listOf(cameraTempUri)) + if (it) onResult(listOf(cameraTempUri).toUi(context)) }, ) @@ -85,7 +99,7 @@ fun MediaInsertMenu( val videoRecordLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CaptureVideo(), onResult = { - if (it) onResult(listOf(videoTempUri)) + if (it) onResult(listOf(UiMediaInsert(videoTempUri, MediaType.video))) }, ) @@ -95,60 +109,72 @@ fun MediaInsertMenu( Box(modifier) { DropdownMenu(expanded = showDropdown, onDismissRequest = { showDropdown = false }) { MediaInsertType.values().forEach { - if (!disableList.contains(it)) { - DropdownMenuItem( - onClick = { - when (it) { - MediaInsertType.CAMERA -> { - cameraTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) - cameraLauncher.launch(cameraTempUri) - } - MediaInsertType.RECORD_VIDEO -> { - videoTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) - videoRecordLauncher.launch(videoTempUri) - } - MediaInsertType.LIBRARY -> filePickerLauncher.launch(arrayOf("image/*", "video/*")) - MediaInsertType.GIF -> scope.launch { - navController.navigateForResult(RootRoute.Gif.Home) - ?.let { result -> - onResult(listOf(result as Uri)) - } - } + val enabled = !disableList.contains(it) + DropdownMenuItem( + onClick = { + when (it) { + MediaInsertType.CAMERA -> { + cameraTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) + cameraLauncher.launch(cameraTempUri) } - showDropdown = false - } - ) { - ListItem( - text = { - Text(text = it.stringName()) - }, - icon = { - Icon( - painter = it.icon(), - contentDescription = it.stringName(), - tint = MaterialTheme.colors.primary - ) + MediaInsertType.RECORD_VIDEO -> { + videoTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = "${UUID.randomUUID()}$VideoSuffix", context) + videoRecordLauncher.launch(videoTempUri) } - ) - } + MediaInsertType.LIBRARY -> filePickerLauncher.launch(librariesSupported) + MediaInsertType.GIF -> scope.launch { + navController.navigateForResult(RootRoute.Gif.Home) + ?.let { result -> + onResult(listOf(result as Uri).toUi(context)) + } + } + } + showDropdown = false + }, + enabled = enabled + ) { + ListItem( + text = { + Text(text = it.stringName()) + }, + icon = { + Icon( + painter = it.icon(), + contentDescription = it.stringName(), + tint = if (enabled) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + ) + } + ) } } } - IconButton( - onClick = { - showDropdown = !showDropdown - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_photo), - contentDescription = stringResource( - id = R.string.accessibility_scene_compose_image - ) - ) + } + IconButton( + onClick = { + showDropdown = !showDropdown } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_photo), + contentDescription = stringResource( + id = R.string.accessibility_scene_compose_image + ) + ) } } +private fun List.toUi(context: Context) = map { + val mimeType = context.contentResolver.getType(it) ?: "image/*" + UiMediaInsert( + uri = it, + type = when { + mimeType.startsWith("video") -> MediaType.video + mimeType == "image/gif" -> MediaType.animated_gif + else -> MediaType.photo + } + ) +} + @Composable private fun MediaInsertType.stringName() = when (this) { MediaInsertType.CAMERA -> "Take Photo" diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt new file mode 100644 index 000000000..18066f439 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.model.ui + +import android.net.Uri +import com.twidere.twiderex.model.enums.MediaType + +data class UiMediaInsert( + val uri: Uri, + val type: MediaType +) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index 3b6ecd32f..d36f2652e 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -127,7 +127,6 @@ import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.icon -import com.twidere.twiderex.extensions.mediaType import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.stringName import com.twidere.twiderex.extensions.withElevation @@ -137,6 +136,7 @@ import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory +import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -155,7 +155,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import java.lang.Exception import kotlin.math.max @Composable @@ -409,7 +408,7 @@ private fun ComposeBody( viewModel = viewModel, ) CompositionLocalProvider(LocalContentAlpha.provides(ContentAlpha.medium)) { - MastodonExtraActions(images, viewModel) + MastodonExtraActions(images.map { it.uri }, viewModel) } } else { Spacer(modifier = Modifier.weight(1F)) @@ -453,7 +452,7 @@ private fun ComposeBody( @Composable private fun ComposeImageList( - images: List, + images: List, viewModel: ComposeViewModel ) { val context = LocalContext.current @@ -1165,13 +1164,17 @@ private fun ComposeActions( }, ) val draftCount = viewModel.draftCount.observeAsState(0) + val insertMode by viewModel.mediaInsertMode.observeAsState(initial = ComposeViewModel.MediaInsertMode.All) Box { Row { AnimatedVisibility(visible = allowImage) { MediaInsertMenu( onResult = { viewModel.putImages(it) - } + }, + supportMultipleSelect = insertMode.multiSelect, + disableList = insertMode.disabledInsertType, + librariesSupported = insertMode.librarySupportedType.toTypedArray() ) } if (account.type == PlatformType.Mastodon) { @@ -1207,10 +1210,6 @@ private fun ComposeActions( ) } } - // TODO: -// IconButton(onClick = {}) { -// Icon(painter = painterResource(id = R.drawable.ic_gif)) -// } if (account.type == PlatformType.Mastodon || account.type == PlatformType.Twitter) { IconButton( onClick = { @@ -1329,9 +1328,9 @@ private object ComposeActionsDefaults { } @Composable -private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Context) { +private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel, context: Context) { var expanded by remember { mutableStateOf(false) } - val type = item.mediaType(context) + val type = item.type val navController = LocalNavController.current Box { Box( @@ -1345,18 +1344,24 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex ) .clickable( onClick = { - navController.navigate(RootRoute.Media.Raw(if (type == MediaType.video) MediaType.video else MediaType.photo, item.toString())) + navController.navigate( + RootRoute.Media.Raw( + if (type == MediaType.video) MediaType.video else MediaType.photo, + item.uri.toString() + ) + ) } ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = getThumb(context, item) ?: item) + NetworkImage(data = getThumb(context, item) ?: item.uri) when (type) { MediaType.animated_gif -> Image( painter = painterResource(id = R.drawable.ic_gif_tag), contentDescription = type.name, - modifier = Modifier.align(Alignment.BottomStart) + modifier = Modifier + .align(Alignment.BottomStart) .width(ComposeImageDefaults.Tag.Width) .height(ComposeImageDefaults.Tag.Height) .padding(ComposeImageDefaults.Tag.Padding) @@ -1375,7 +1380,8 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex Image( painter = painterResource(id = R.drawable.ic_dots_circle_horiz), contentDescription = type.name, - modifier = Modifier.align(Alignment.BottomEnd) + modifier = Modifier + .align(Alignment.BottomEnd) .size(ComposeImageDefaults.Menu.Size) .padding(ComposeImageDefaults.Menu.Padding) .clickable { expanded = !expanded } @@ -1388,7 +1394,7 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex DropdownMenuItem( onClick = { expanded = false - viewModel.removeImage(item) + viewModel.removeImage(item.uri) } ) { Text( @@ -1400,13 +1406,13 @@ private fun ComposeImage(item: Uri, viewModel: ComposeViewModel, context: Contex } } -private fun getThumb(context: Context, uri: Uri): Bitmap? { - return if (context.contentResolver.getType(uri)?.startsWith("video") == true) { +private fun getThumb(context: Context, item: UiMediaInsert): Bitmap? { + return if (item.type == MediaType.video) { var bitmap: Bitmap? = null var mediaMetadataRetriever: MediaMetadataRetriever? = null try { mediaMetadataRetriever = MediaMetadataRetriever() - mediaMetadataRetriever.setDataSource(context, uri) + mediaMetadataRetriever.setDataSource(context, item.uri) bitmap = mediaMetadataRetriever.getFrameAtTime( 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 19f593f9c..df6f3c9ca 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.viewmodel.compose import android.Manifest.permission +import android.content.Context import android.location.Criteria import android.location.Location import android.location.LocationListener @@ -45,9 +46,12 @@ import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.MastodonVisibility +import com.twidere.twiderex.model.enums.MediaInsertType +import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiEmoji +import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.DraftRepository @@ -59,6 +63,7 @@ import com.twidere.twiderex.worker.draft.SaveDraftWorker import com.twitter.twittertext.Extractor import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -101,6 +106,7 @@ class DraftComposeViewModel @AssistedInject constructor( repository: StatusRepository, userRepository: UserRepository, workManager: WorkManager, + @ApplicationContext context: Context, inAppNotification: InAppNotification, @Assisted account: AccountDetails, @Assisted private val draft: DbDraft, @@ -121,7 +127,20 @@ class DraftComposeViewModel @AssistedInject constructor( init { setText(TextFieldValue(draft.content)) - putImages(draft.media.map { Uri.parse(it) }) + putImages( + draft.media.map { + val uri = Uri.parse(it) + val mimeType = context.contentResolver.getType(uri) ?: "image/*" + UiMediaInsert( + Uri.parse(it), + type = when { + mimeType.startsWith("video") -> MediaType.video + mimeType == "image/gif" -> MediaType.animated_gif + else -> MediaType.photo + } + ) + } + ) excludedReplyUserIds.value = draft.excludedReplyUserIds ?: emptyList() } @@ -282,7 +301,7 @@ open class ComposeViewModel @AssistedInject constructor( val isContentWarningEnabled = MutableStateFlow(false) val contentWarningTextFieldValue = MutableStateFlow(TextFieldValue()) val textFieldValue = MutableStateFlow(TextFieldValue()) - val images = MutableStateFlow>(emptyList()) + val images = MutableStateFlow>(emptyList()) val canSend = textFieldValue.combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } val canSaveDraft = textFieldValue.combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } @@ -326,6 +345,8 @@ open class ComposeViewModel @AssistedInject constructor( } } + val mediaInsertMode = MutableStateFlow(MediaInsertMode.All) + fun setText(value: TextFieldValue) { textFieldValue.value = value } @@ -384,7 +405,7 @@ open class ComposeViewModel @AssistedInject constructor( private fun buildComposeData(text: String) = ComposeData( content = text, draftId = draftId, - images = images.value.map { it.toString() }, + images = images.value.map { it.uri.toString() }, composeType = composeType, statusKey = statusKey, lat = location.value?.latitude, @@ -399,12 +420,22 @@ open class ComposeViewModel @AssistedInject constructor( isThreadMode = enableThreadMode.value, ) - fun putImages(value: List) { + fun putImages(value: List) { + val allowType = images.value.firstOrNull()?.type ?: value.firstOrNull()?.type ?: return images.value.let { - value + it - }.take(imageLimit).let { + value + it.filter { media -> media.type == allowType } + }.take(if (allowType == MediaType.photo) imageLimit else 1).let { images.value = it } + when (allowType) { + MediaType.video, MediaType.animated_gif -> mediaInsertMode.value = MediaInsertMode.Disabled + else -> { + mediaInsertMode.value = if (images.value.size == imageLimit) + MediaInsertMode.Disabled + else + MediaInsertMode.ImageOnly + } + } } private val imageLimit: Int @@ -449,10 +480,17 @@ open class ComposeViewModel @AssistedInject constructor( fun removeImage(item: Uri) { images.value.let { - it - item + it.toMutableList().apply { + removeAll { media -> media.uri == item } + } }.let { images.value = it } + if (images.value.isEmpty()) { + mediaInsertMode.value = MediaInsertMode.All + } else if (images.value.first().type == MediaType.photo) { + mediaInsertMode.value = MediaInsertMode.ImageOnly + } } fun excludeReplyUser(user: UiUser) { @@ -481,4 +519,22 @@ open class ComposeViewModel @AssistedInject constructor( fun insertEmoji(emoji: UiEmoji) { insertText("${if (textFieldValue.value.selection.start != 0) " " else ""}:${emoji.shortcode}: ") } + + data class MediaInsertMode( + val disabledInsertType: List, + val multiSelect: Boolean, + val librarySupportedType: List + ) { + companion object { + val All = MediaInsertMode(emptyList(), false, listOf("image/*", "video/*")) + val ImageOnly = MediaInsertMode( + listOf( + MediaInsertType.GIF, + MediaInsertType.RECORD_VIDEO + ), + true, listOf("image/*") + ) + val Disabled = MediaInsertMode(MediaInsertType.values().toList(), true, listOf("image/*")) + } + } } From 9e65bf734f0916da570fa7612bd7ee30c4975ea8 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 7 Sep 2021 17:20:31 +0800 Subject: [PATCH 136/615] update media insert in dm and fixed mastdon gif upload issue --- .../com/twidere/twiderex/db/mapper/IGif.kt | 1 + .../com/twidere/twiderex/model/ui/UiGif.kt | 1 + .../twiderex/model/ui/UiMediaInsert.kt | 27 ++++++- .../twiderex/scenes/compose/ComposeScene.kt | 25 +------ .../twiderex/scenes/dm/DMConversationScene.kt | 72 +++++++++---------- .../twidere/twiderex/scenes/gif/GifScene.kt | 4 ++ .../twiderex/viewmodel/dm/DMEventViewModel.kt | 6 +- .../twiderex/viewmodel/gif/GifViewModel.kt | 16 ++++- 8 files changed, 82 insertions(+), 70 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt index 4ee5c4595..b869870af 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/db/mapper/IGif.kt @@ -32,6 +32,7 @@ fun IGif.toUi(): UiGif { is GiphyGif -> UiGif( id = this.id ?: UUID.randomUUID().toString(), url = this.images?.original?.url ?: "", + mp4 = this.images?.original?.mp4 ?: "", preview = this.images?.previewGif?.url ?: "", type = this.type ?: "gif" ) diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt index ebc15074f..831841b98 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiGif.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.model.ui data class UiGif( val id: String, val url: String, + val mp4: String, val preview: String, val type: String ) diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt index 18066f439..6d8b1385c 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt @@ -20,10 +20,35 @@ */ package com.twidere.twiderex.model.ui +import android.content.Context +import android.graphics.Bitmap +import android.media.MediaMetadataRetriever import android.net.Uri import com.twidere.twiderex.model.enums.MediaType data class UiMediaInsert( val uri: Uri, val type: MediaType -) +) { + companion object { + fun UiMediaInsert.getVideoThumb(context: Context): Bitmap? { + return if (type == MediaType.video) { + var bitmap: Bitmap? = null + var mediaMetadataRetriever: MediaMetadataRetriever? = null + try { + mediaMetadataRetriever = MediaMetadataRetriever() + mediaMetadataRetriever.setDataSource(context, uri) + bitmap = mediaMetadataRetriever.getFrameAtTime( + 1000, + MediaMetadataRetriever.OPTION_CLOSEST_SYNC + ) + } catch (e: Exception) { + e.printStackTrace() + } finally { + mediaMetadataRetriever?.release() + } + bitmap + } else null + } + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d36f2652e..4f30a4c99 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -23,9 +23,7 @@ package com.twidere.twiderex.scenes.compose import android.Manifest import android.annotation.SuppressLint import android.content.Context -import android.graphics.Bitmap import android.location.Location -import android.media.MediaMetadataRetriever import android.net.Uri import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult @@ -137,6 +135,7 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.model.ui.UiMediaInsert +import com.twidere.twiderex.model.ui.UiMediaInsert.Companion.getVideoThumb import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -1354,7 +1353,7 @@ private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel, conte ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = getThumb(context, item) ?: item.uri) + NetworkImage(data = item.getVideoThumb(context) ?: item.uri) when (type) { MediaType.animated_gif -> Image( @@ -1406,26 +1405,6 @@ private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel, conte } } -private fun getThumb(context: Context, item: UiMediaInsert): Bitmap? { - return if (item.type == MediaType.video) { - var bitmap: Bitmap? = null - var mediaMetadataRetriever: MediaMetadataRetriever? = null - try { - mediaMetadataRetriever = MediaMetadataRetriever() - mediaMetadataRetriever.setDataSource(context, item.uri) - bitmap = mediaMetadataRetriever.getFrameAtTime( - 1000, - MediaMetadataRetriever.OPTION_CLOSEST_SYNC - ) - } catch (e: Exception) { - e.printStackTrace() - } finally { - mediaMetadataRetriever?.release() - } - bitmap - } else null -} - private object ComposeImageDefaults { val ImageSize = 72.dp diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index 0c1e39573..5be66f749 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -23,10 +23,6 @@ package com.twidere.twiderex.scenes.dm import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.net.Uri -import androidx.activity.compose.ManagedActivityResultLauncher -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background @@ -76,14 +72,16 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiDMEventList +import com.twidere.twiderex.component.media.MediaInsertMenu import com.twidere.twiderex.di.assisted.assistedViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiMediaInsert +import com.twidere.twiderex.model.ui.UiMediaInsert.Companion.getVideoThumb import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMEventViewModel -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @OptIn(ExperimentalAnimatedInsets ::class) @@ -124,12 +122,6 @@ fun DMConversationScene(conversationKey: MicroBlogKey) { @Composable fun NormalContent(viewModel: DMEventViewModel) { val clipboardManager = LocalContext.current.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager? - val filePickerLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.OpenDocument(), - onResult = { - viewModel.inputImage.value = it - }, - ) val copyText = stringResource(id = R.string.scene_messages_action_copy_text) val source = viewModel.source.collectAsLazyPagingItems() val input by viewModel.input.observeAsState(initial = "") @@ -171,13 +163,14 @@ fun NormalContent(viewModel: DMEventViewModel) { ) } Divider(modifier = Modifier.fillMaxWidth()) - InputPhotoPreview(inputImage) { + InputMediaPreview(inputImage) { viewModel.inputImage.value = null } InputComponent( modifier = Modifier.fillMaxWidth(), - scope = scope, - filePickerLauncher = filePickerLauncher, + onMediaInsert = { + viewModel.inputImage.value = it.firstOrNull() + }, enableSelectPhoto = inputImage == null, enableSend = input.isNotEmpty() || inputImage != null, input = input, @@ -185,15 +178,22 @@ fun NormalContent(viewModel: DMEventViewModel) { onSend = { viewModel.sendMessage() } ) } - LaunchedEffect( - key1 = firstEventKey, - block = { - if (firstEventKey == null || source.itemCount <= 0) return@LaunchedEffect - scope.launch { - listState.scrollToItem(0) + firstEventKey?.let { + LaunchedEffect( + key1 = firstEventKey, + block = { + if (source.itemCount <= 0) return@LaunchedEffect + scope.launch { + try { + listState.scrollToItem(0) + } catch (e: Throwable) { + // when viewModel is restored from cache while List hasn't init yet + // might cause crash + } + } } - } - ) + ) + } } @OptIn(ExperimentalMaterialApi::class) @@ -235,8 +235,9 @@ private object MessageActionComponentDefaults { } @Composable -fun InputPhotoPreview(inputImage: Uri?, onRemove: () -> Unit) { +fun InputMediaPreview(inputImage: UiMediaInsert?, onRemove: () -> Unit) { if (inputImage == null) return + val context = LocalContext.current Box(modifier = Modifier.padding(InputPhotoPreviewDefaults.ContentPadding)) { Box( modifier = Modifier @@ -249,7 +250,7 @@ fun InputPhotoPreview(inputImage: Uri?, onRemove: () -> Unit) { ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = inputImage) + NetworkImage(data = inputImage.getVideoThumb(context) ?: inputImage.uri) } Box( modifier = Modifier @@ -280,8 +281,7 @@ private object InputPhotoPreviewDefaults { @Composable fun InputComponent( modifier: Modifier = Modifier, - filePickerLauncher: ManagedActivityResultLauncher, Uri>, - scope: CoroutineScope, + onMediaInsert: (List) -> Unit, input: String, onValueChanged: (input: String) -> Unit, enableSelectPhoto: Boolean, @@ -293,20 +293,12 @@ fun InputComponent( verticalAlignment = Alignment.CenterVertically ) { AnimatedVisibility(visible = enableSelectPhoto) { - IconButton( - onClick = { - scope.launch { - filePickerLauncher.launch(arrayOf("image/*")) - } - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_camera), - contentDescription = stringResource( - id = R.string.accessibility_scene_compose_image - ) - ) - } + MediaInsertMenu( + onResult = { + onMediaInsert(it) + }, + supportMultipleSelect = false, + ) } Spacer(modifier = Modifier.width(InputComponentDefaults.ContentSpacing)) TextInput( diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt index 97c1d5eb5..3caad29c9 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt @@ -59,7 +59,9 @@ import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiGifList import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiGif +import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.gif.GifViewModel @@ -71,6 +73,7 @@ fun GifScene() { } val enable by viewModel.enable.collectAsState(initial = false) val context = LocalContext.current + val account = LocalActiveAccount.current val navController = LocalNavController.current val commitLoading by viewModel.commitLoading.collectAsState(initial = false) TwidereScene { @@ -88,6 +91,7 @@ fun GifScene() { onClick = { viewModel.commit( context = context, + platform = account?.type ?: PlatformType.Twitter, onSuccess = { navController.goBackWith(it) } diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 4da2aa555..11c445263 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.dm -import android.net.Uri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn @@ -33,6 +32,7 @@ import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.repository.DirectMessageRepository import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -71,7 +71,7 @@ class DMEventViewModel @AssistedInject constructor( // input val input = MutableStateFlow("") - val inputImage = MutableStateFlow(null) + val inputImage = MutableStateFlow(null) val firstEventKey = MutableStateFlow(null) val pendingActionMessage = MutableStateFlow(null) @@ -83,7 +83,7 @@ class DMEventViewModel @AssistedInject constructor( account.type, data = DirectMessageSendData( text = input.value, - images = inputImage.value?.toString()?.let { uri -> listOf(uri) } + images = inputImage.value?.uri?.toString()?.let { uri -> listOf(uri) } ?: emptyList(), recipientUserKey = it.recipientKey, draftMessageKey = when (account.type) { diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt index 1cf435384..0fc7d1c93 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn import com.twidere.twiderex.http.TwidereServiceFactory +import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiGif import com.twidere.twiderex.repository.GifRepository import com.twidere.twiderex.utils.FileProviderHelper @@ -72,13 +73,22 @@ class GifViewModel @AssistedInject constructor( val commitLoading get() = _commitLoading - fun commit(context: Context, onSuccess: (uri: Uri) -> Unit) { + fun commit(context: Context, onSuccess: (uri: Uri) -> Unit, platform: PlatformType) { selectedItem.value?.let { + // mastodon support image/video only + val url = when (platform) { + PlatformType.Mastodon -> it.mp4 + else -> it.url + } + val suffix = when (platform) { + PlatformType.Mastodon -> "mp4" + else -> it.type + } viewModelScope.launch { _commitLoading.value = true try { - val target = FileProviderHelper.getUriFromMedias("${it.id}.${it.type}", context) - gifRepository.download(target = target.toString(), source = it.url, service = service) + val target = FileProviderHelper.getUriFromMedias("${it.id}.$suffix", context) + gifRepository.download(target = target.toString(), source = url, service = service) onSuccess(target) } catch (e: Throwable) { e.notify() From d0a41e533119785fb6b536f226b33fca4c2b8899 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 7 Sep 2021 17:53:31 +0800 Subject: [PATCH 137/615] migrate to koin --- .../twidere/twiderex/action/DraftAction.kt | 18 +- .../twidere/twiderex/action/MediaAction.kt | 30 ++- .../di/modules/ActionsModule.android.kt | 36 +++ .../twiderex/di/modules/KmpModule.android.kt | 36 +++ .../di/modules/PlatformModule.android.kt | 88 +++++++ .../PreferencesModule.android.kt | 2 +- .../twiderex/utils/FileProviderHelper.kt | 0 .../twiderex/worker/DownloadMediaWorker.kt | 5 +- .../kotlin/com/twidere/twiderex/di/Setup.kt | 14 ++ .../twidere/twiderex/di/ext/ViewModelExt.kt | 5 +- .../twiderex/di/modules/ActionModule.kt | 25 ++ .../twiderex/di/modules/DataBaseModule.kt | 31 +++ .../twidere/twiderex/di/modules/JobsModule.kt | 85 +++++++ .../twidere/twiderex/di/modules/KmpModule.kt | 25 ++ .../di/{ => modules}/PlatformModule.kt | 2 +- .../di/{ => modules}/PreferencesModule.kt | 2 +- .../twiderex/di/modules/RepositoryModule.kt | 55 +++++ .../twiderex/di/modules/ViewModelModule.kt | 233 ++++++++++++++++++ .../twiderex/extensions/KoinExtensions.kt | 41 +++ .../twiderex/repository/StatusRepository.kt | 28 ++- .../lists/ListsAddMemberViewModel.kt | 95 +++++++ .../viewmodel/lists/ListsUserViewModel.kt | 66 ----- .../precompose/viewmodel/ViewModelProvider.kt | 13 +- .../di/modules/ActionsModule.desktop.kt} | 8 +- .../twiderex/di/modules/KmpModule.desktop.kt | 26 ++ .../{ => modules}/PlatformModule.desktop.kt | 2 +- .../PreferencesModule.desktop.kt | 2 +- 27 files changed, 879 insertions(+), 94 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt rename common/src/androidMain/kotlin/com/twidere/twiderex/di/{ => modules}/PreferencesModule.android.kt (96%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt (100%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt rename common/src/commonMain/kotlin/com/twidere/twiderex/di/{ => modules}/PlatformModule.kt (95%) rename common/src/commonMain/kotlin/com/twidere/twiderex/di/{ => modules}/PreferencesModule.kt (98%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt rename common/src/{androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt => desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt} (83%) create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt rename common/src/desktopMain/kotlin/com/twidere/twiderex/di/{ => modules}/PlatformModule.desktop.kt (95%) rename common/src/desktopMain/kotlin/com/twidere/twiderex/di/{ => modules}/PreferencesModule.desktop.kt (96%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt index 52a1c01e1..8757a7228 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -20,12 +20,28 @@ */ package com.twidere.twiderex.action +import androidx.core.app.NotificationManagerCompat +import androidx.work.WorkManager import com.twidere.twiderex.model.job.ComposeData +import com.twidere.twiderex.worker.draft.RemoveDraftWorker +import com.twidere.twiderex.worker.draft.SaveDraftWorker -actual class DraftAction { +actual class DraftAction( + private val workManager: WorkManager, + private val notificationManagerCompat: NotificationManagerCompat, +) { actual fun delete(id: String) { + workManager.beginWith(RemoveDraftWorker.create(id)).enqueue() + notificationManagerCompat.cancel(id.hashCode()) } actual fun save(composeData: ComposeData) { + workManager + .beginWith( + SaveDraftWorker.create( + composeData + ) + ) + .enqueue() } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index f44285658..7c151461c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -20,16 +20,44 @@ */ package com.twidere.twiderex.action +import android.content.Context +import androidx.work.WorkManager import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.utils.FileProviderHelper +import com.twidere.twiderex.worker.DownloadMediaWorker +import com.twidere.twiderex.worker.ShareMediaWorker -actual class MediaAction { +actual class MediaAction( + private val workManager: WorkManager, + private val context: Context, +) { actual fun download( source: String, target: String, accountKey: MicroBlogKey ) { + workManager.enqueue( + DownloadMediaWorker.create( + accountKey = accountKey, + source = source, + target = target + ) + ) } actual fun share(source: String, accountKey: MicroBlogKey) { + val uri = FileProviderHelper.getUriFromMedia(source, context) + DownloadMediaWorker.create( + accountKey = accountKey, + source = source, + target = uri.toString() + ).let { + workManager.beginWith(it) + .then( + ShareMediaWorker.create( + target = uri + ) + ).enqueue() + } } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt new file mode 100644 index 000000000..4af71a355 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di + +import com.twidere.twiderex.action.ComposeAction +import com.twidere.twiderex.action.DirectMessageAction +import com.twidere.twiderex.action.DraftAction +import com.twidere.twiderex.action.MediaAction +import com.twidere.twiderex.action.StatusActions +import org.koin.dsl.module + +actual val actionModule = module { + single { ComposeAction(get()) } + single { DirectMessageAction(get()) } + single { DraftAction(get(), get()) } + single { MediaAction(get(), get()) } + single { StatusActions(get()) } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt new file mode 100644 index 000000000..ca8539bda --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt @@ -0,0 +1,36 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import com.twidere.twiderex.kmp.ExifScrambler +import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.LocationProvider +import com.twidere.twiderex.kmp.RemoteNavigator +import com.twidere.twiderex.kmp.ResLoader +import org.koin.dsl.module + +actual val kmpModule = module { + single { ExifScrambler(get()) } + single { FileResolver(get()) } + single { LocationProvider(get()) } + single { RemoteNavigator(get()) } + single { ResLoader(get()) } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt new file mode 100644 index 000000000..bdced0b04 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt @@ -0,0 +1,88 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import android.accounts.AccountManager +import android.content.Context +import android.location.LocationManager +import android.net.ConnectivityManager +import androidx.core.app.NotificationManagerCompat +import androidx.work.WorkManager +import com.twidere.twiderex.kmp.ResLoader +import com.twidere.twiderex.model.AccountPreferencesFactory +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.worker.DownloadMediaWorker +import com.twidere.twiderex.worker.NotificationWorker +import com.twidere.twiderex.worker.ShareMediaWorker +import com.twidere.twiderex.worker.compose.MastodonComposeWorker +import com.twidere.twiderex.worker.compose.TwitterComposeWorker +import com.twidere.twiderex.worker.database.DeleteDbStatusWorker +import com.twidere.twiderex.worker.dm.DirectMessageDeleteWorker +import com.twidere.twiderex.worker.dm.DirectMessageFetchWorker +import com.twidere.twiderex.worker.dm.TwitterDirectMessageSendWorker +import com.twidere.twiderex.worker.draft.RemoveDraftWorker +import com.twidere.twiderex.worker.draft.SaveDraftWorker +import com.twidere.twiderex.worker.status.DeleteStatusWorker +import com.twidere.twiderex.worker.status.LikeWorker +import com.twidere.twiderex.worker.status.MastodonVoteWorker +import com.twidere.twiderex.worker.status.RetweetWorker +import com.twidere.twiderex.worker.status.UnLikeWorker +import com.twidere.twiderex.worker.status.UnRetweetWorker +import com.twidere.twiderex.worker.status.UpdateStatusWorker +import org.koin.androidx.workmanager.dsl.worker +import org.koin.core.module.Module +import org.koin.dsl.module + +internal actual val platformModule = module { + single { + ResLoader(get()) + } + single { AccountRepository(get(), get()) } + single { AccountPreferencesFactory(get()) } + single { AccountManager.get(get()) } + single { get().getSystemService(Context.LOCATION_SERVICE) as LocationManager } + single { get().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } + single { get().contentResolver } + single { NotificationManagerCompat.from(get()) } + single { WorkManager.getInstance(get()) } + workManager() +} + +private fun Module.workManager() { + worker { ShareMediaWorker(get(), get(), get()) } + worker { NotificationWorker(get(), get(), get(), get()) } + worker { DownloadMediaWorker(get(), get(), get()) } + worker { DeleteStatusWorker(get(), get(), get()) } + worker { LikeWorker(get(), get(), get()) } + worker { MastodonVoteWorker(get(), get(), get()) } + worker { RetweetWorker(get(), get(), get()) } + worker { UnLikeWorker(get(), get(), get()) } + worker { UnRetweetWorker(get(), get(), get()) } + worker { UpdateStatusWorker(get(), get(), get(), get()) } + worker { RemoveDraftWorker(get(), get(), get()) } + worker { SaveDraftWorker(get(), get(), get()) } + worker { DirectMessageDeleteWorker(get(), get(), get()) } + worker { DirectMessageFetchWorker(get(), get(), get()) } + worker { TwitterDirectMessageSendWorker(get(), get(), get()) } + worker { DeleteDbStatusWorker(get(), get(), get()) } + worker { MastodonComposeWorker(get(), get(), get()) } + worker { TwitterComposeWorker(get(), get(), get()) } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.android.kt similarity index 96% rename from common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.android.kt index 4ea159d0a..7687a02a4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PreferencesModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.android.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import android.content.Context import org.koin.core.scope.Scope diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/utils/FileProviderHelper.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt index 4afa2b438..17e1819b4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.worker import android.content.Context -import android.net.Uri import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder @@ -39,13 +38,13 @@ class DownloadMediaWorker( fun create( accountKey: MicroBlogKey, source: String, - target: Uri, + target: String, ) = OneTimeWorkRequestBuilder() .setInputData( Data.Builder() .putString("accountKey", accountKey.toString()) .putString("source", source) - .putString("target", target.toString()) + .putString("target", target) .build() ) .build() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index 769aab250..b57e48727 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -20,9 +20,23 @@ */ package com.twidere.twiderex.di +import com.twidere.twiderex.di.modules.actionModule +import com.twidere.twiderex.di.modules.dataBaseModule +import com.twidere.twiderex.di.modules.jobsModule +import com.twidere.twiderex.di.modules.kmpModule +import com.twidere.twiderex.di.modules.platformModule +import com.twidere.twiderex.di.modules.preferencesModule +import com.twidere.twiderex.di.modules.repositoryModule +import com.twidere.twiderex.di.modules.viewModelModule import org.koin.core.KoinApplication fun KoinApplication.setupModules() { modules(preferencesModule) modules(platformModule) + modules(dataBaseModule) + modules(viewModelModule) + modules(repositoryModule) + modules(actionModule) + modules(jobsModule) + modules(kmpModule) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt index 4f0888f6a..ec42863ed 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.remember import moe.tlaster.precompose.ui.LocalViewModelStoreOwner import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner +import moe.tlaster.precompose.viewmodel.getViewModel import org.koin.core.parameter.ParametersDefinition import org.koin.core.qualifier.Qualifier import org.koin.mp.KoinPlatformTools @@ -72,5 +73,7 @@ fun ViewModelStoreOwner.getViewModel( clazz: KClass, parameters: ParametersDefinition? = null, ): T { - return KoinPlatformTools.defaultContext().get().get(clazz, qualifier, parameters) + return this.viewModelStore.getViewModel(qualifier.toString(), clazz) { + KoinPlatformTools.defaultContext().get().get(clazz, qualifier, parameters) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt new file mode 100644 index 000000000..e89f3343b --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import org.koin.core.module.Module + +expect val actionModule: Module diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt new file mode 100644 index 000000000..3f6accb9b --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import com.twidere.twiderex.dataprovider.DataProvider +import org.koin.dsl.module + +val dataBaseModule = module { + single { DataProvider.create() } + single { get().appDatabase } + single { get().cacheDatabase } + single { get().fileCacheHandler } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt new file mode 100644 index 000000000..2c0bb41b0 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import com.twidere.twiderex.jobs.common.DownloadMediaJob +import com.twidere.twiderex.jobs.common.NotificationJob +import com.twidere.twiderex.jobs.common.ShareMediaJob +import com.twidere.twiderex.jobs.compose.MastodonComposeJob +import com.twidere.twiderex.jobs.compose.TwitterComposeJob +import com.twidere.twiderex.jobs.database.DeleteDbStatusJob +import com.twidere.twiderex.jobs.dm.DirectMessageDeleteJob +import com.twidere.twiderex.jobs.dm.DirectMessageFetchJob +import com.twidere.twiderex.jobs.dm.TwitterDirectMessageSendJob +import com.twidere.twiderex.jobs.draft.RemoveDraftJob +import com.twidere.twiderex.jobs.draft.SaveDraftJob +import com.twidere.twiderex.jobs.status.DeleteStatusJob +import com.twidere.twiderex.jobs.status.LikeStatusJob +import com.twidere.twiderex.jobs.status.MastodonVoteJob +import com.twidere.twiderex.jobs.status.RetweetStatusJob +import com.twidere.twiderex.jobs.status.UnRetweetStatusJob +import com.twidere.twiderex.jobs.status.UnlikeStatusJob +import org.koin.core.module.Module +import org.koin.dsl.module + +val jobsModule = module { + common() + compose() + database() + dm() + draft() + status() +} + +private fun Module.status() { + single { DeleteStatusJob(get(), get(), get()) } + single { LikeStatusJob(get(), get(), get()) } + single { MastodonVoteJob(get(), get(), get()) } + single { RetweetStatusJob(get(), get(), get()) } + single { UnlikeStatusJob(get(), get(), get()) } + single { UnRetweetStatusJob(get(), get(), get()) } +} + +private fun Module.draft() { + single { RemoveDraftJob(get()) } + single { SaveDraftJob(get(), get()) } +} + +private fun Module.dm() { + single { DirectMessageDeleteJob(get(), get()) } + single { DirectMessageFetchJob(get(), get(), get(), get()) } + single { TwitterDirectMessageSendJob(get(), get(), get(), get(), get()) } +} + +private fun Module.database() { + single { DeleteDbStatusJob(get()) } +} + +private fun Module.compose() { + single { MastodonComposeJob(get(), get(), get(), get(), get(), get(), get()) } + single { TwitterComposeJob(get(), get(), get(), get(), get(), get(), get(), get()) } +} + +private fun Module.common() { + single { DownloadMediaJob(get(), get(), get(), get()) } + single { NotificationJob(get(), get(), get(), get()) } + single { ShareMediaJob(get(), get()) } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt new file mode 100644 index 000000000..1cbdaeb24 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import org.koin.core.module.Module + +expect val kmpModule: Module diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt similarity index 95% rename from common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt index be49f1aa2..bba6d1f70 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PlatformModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import org.koin.core.module.Module diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt similarity index 98% rename from common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt index e2d963199..f3882567c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/PreferencesModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.Serializer diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt new file mode 100644 index 000000000..5420cb639 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt @@ -0,0 +1,55 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import com.twidere.twiderex.preferences.PreferencesHolder +import com.twidere.twiderex.repository.CacheRepository +import com.twidere.twiderex.repository.DirectMessageRepository +import com.twidere.twiderex.repository.DraftRepository +import com.twidere.twiderex.repository.ListsRepository +import com.twidere.twiderex.repository.ListsUsersRepository +import com.twidere.twiderex.repository.MediaRepository +import com.twidere.twiderex.repository.NotificationRepository +import com.twidere.twiderex.repository.ReactionRepository +import com.twidere.twiderex.repository.SearchRepository +import com.twidere.twiderex.repository.StatusRepository +import com.twidere.twiderex.repository.TimelineRepository +import com.twidere.twiderex.repository.TrendRepository +import com.twidere.twiderex.repository.UserListRepository +import com.twidere.twiderex.repository.UserRepository +import org.koin.dsl.module + +val repositoryModule = module { + single { CacheRepository(get(), get(), get()) } + single { DirectMessageRepository(get()) } + single { DraftRepository(get()) } + single { ListsRepository(get()) } + single { ListsUsersRepository() } + single { MediaRepository(get()) } + single { NotificationRepository(get()) } + single { ReactionRepository(get()) } + single { SearchRepository(get(), get()) } + single { StatusRepository(get(), get().miscPreferences) } + single { TimelineRepository(get()) } + single { TrendRepository(get()) } + single { UserListRepository() } + single { UserRepository(get(), get()) } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt new file mode 100644 index 000000000..d79cc93fb --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -0,0 +1,233 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import com.twidere.twiderex.extensions.viewModel +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.viewmodel.ActiveAccountViewModel +import com.twidere.twiderex.viewmodel.DraftViewModel +import com.twidere.twiderex.viewmodel.MediaViewModel +import com.twidere.twiderex.viewmodel.PureMediaViewModel +import com.twidere.twiderex.viewmodel.StatusViewModel +import com.twidere.twiderex.viewmodel.compose.ComposeSearchUserViewModel +import com.twidere.twiderex.viewmodel.compose.ComposeViewModel +import com.twidere.twiderex.viewmodel.compose.DraftComposeViewModel +import com.twidere.twiderex.viewmodel.compose.DraftItemViewModel +import com.twidere.twiderex.viewmodel.compose.MastodonComposeSearchHashtagViewModel +import com.twidere.twiderex.viewmodel.dm.DMConversationViewModel +import com.twidere.twiderex.viewmodel.dm.DMEventViewModel +import com.twidere.twiderex.viewmodel.dm.DMNewConversationViewModel +import com.twidere.twiderex.viewmodel.lists.ListsAddMemberViewModel +import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel +import com.twidere.twiderex.viewmodel.lists.ListsModifyViewModel +import com.twidere.twiderex.viewmodel.lists.ListsSearchUserViewModel +import com.twidere.twiderex.viewmodel.lists.ListsTimelineViewModel +import com.twidere.twiderex.viewmodel.lists.ListsUserViewModel +import com.twidere.twiderex.viewmodel.lists.ListsViewModel +import com.twidere.twiderex.viewmodel.mastodon.MastodonHashtagViewModel +import com.twidere.twiderex.viewmodel.mastodon.MastodonSearchHashtagViewModel +import com.twidere.twiderex.viewmodel.mastodon.MastodonSignInViewModel +import com.twidere.twiderex.viewmodel.search.SearchInputViewModel +import com.twidere.twiderex.viewmodel.search.SearchSaveViewModel +import com.twidere.twiderex.viewmodel.search.SearchTweetsViewModel +import com.twidere.twiderex.viewmodel.search.SearchUserViewModel +import com.twidere.twiderex.viewmodel.settings.AccountNotificationViewModel +import com.twidere.twiderex.viewmodel.settings.AppearanceViewModel +import com.twidere.twiderex.viewmodel.settings.DisplayViewModel +import com.twidere.twiderex.viewmodel.settings.LayoutViewModel +import com.twidere.twiderex.viewmodel.settings.MiscViewModel +import com.twidere.twiderex.viewmodel.settings.NotificationViewModel +import com.twidere.twiderex.viewmodel.settings.StorageViewModel +import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel +import com.twidere.twiderex.viewmodel.timeline.MentionsTimelineViewModel +import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel +import com.twidere.twiderex.viewmodel.timeline.mastodon.FederatedTimelineViewModel +import com.twidere.twiderex.viewmodel.timeline.mastodon.LocalTimelineViewModel +import com.twidere.twiderex.viewmodel.trend.TrendViewModel +import com.twidere.twiderex.viewmodel.twitter.TwitterSignInViewModel +import com.twidere.twiderex.viewmodel.twitter.search.TwitterSearchMediaViewModel +import com.twidere.twiderex.viewmodel.twitter.user.TwitterUserViewModel +import com.twidere.twiderex.viewmodel.user.FollowersViewModel +import com.twidere.twiderex.viewmodel.user.FollowingViewModel +import com.twidere.twiderex.viewmodel.user.UserFavouriteTimelineViewModel +import com.twidere.twiderex.viewmodel.user.UserMediaTimelineViewModel +import com.twidere.twiderex.viewmodel.user.UserTimelineViewModel +import com.twidere.twiderex.viewmodel.user.UserViewModel +import org.koin.core.module.Module +import org.koin.dsl.module + +val viewModelModule = module { + viewModel { (statusKey: MicroBlogKey) -> StatusViewModel(get(), get(), statusKey) } + viewModel { (belongKey: MicroBlogKey) -> PureMediaViewModel(get(), belongKey) } + viewModel { (statusKey: MicroBlogKey) -> MediaViewModel(get(), get(), get(), statusKey) } + viewModel { DraftViewModel(get(), get()) } + viewModel { ActiveAccountViewModel(get()) } + + user() + twitter() + trend() + timeline() + settings() + search() + mastodon() + lists() + dm() + compose() +} + +private fun Module.compose() { + viewModel { MastodonComposeSearchHashtagViewModel(get()) } + viewModel { (draftId: String) -> DraftItemViewModel(get(), draftId) } + viewModel { (draft: UiDraft) -> + DraftComposeViewModel( + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + draft + ) + } + viewModel { (statusKey: MicroBlogKey?, composeType: ComposeType) -> + ComposeViewModel( + get(), + get(), + get(), + get(), + get(), + get(), + get(), + get(), + statusKey, + composeType, + ) + } + viewModel { ComposeSearchUserViewModel(get()) } +} + +private fun Module.dm() { + viewModel { DMConversationViewModel(get(), get()) } + viewModel { (conversationKey: MicroBlogKey) -> + DMEventViewModel( + get(), + get(), + get(), + conversationKey + ) + } + viewModel { DMNewConversationViewModel(get(), get()) } +} + +private fun Module.lists() { + viewModel { (following: Boolean) -> ListsSearchUserViewModel(get(), following) } + viewModel { (listKey: MicroBlogKey) -> ListsTimelineViewModel(get(), get(), listKey) } + viewModel { (listId: String) -> ListsAddMemberViewModel(get(), get(), get(), listId) } + viewModel { (listId: String, viewMembers: Boolean) -> + ListsUserViewModel( + get(), + get(), + listId, + viewMembers + ) + } + viewModel { ListsViewModel(get(), get()) } + viewModel { (onResult: (success: Boolean, list: UiList?) -> Unit) -> + ListsCreateViewModel( + get(), + get(), + get(), + onResult + ) + } + viewModel { (listKey: MicroBlogKey) -> ListsModifyViewModel(get(), get(), get(), listKey) } +} + +private fun Module.mastodon() { + viewModel { (keyword: String) -> MastodonHashtagViewModel(get(), get(), keyword) } + viewModel { (keyword: String) -> MastodonSearchHashtagViewModel(get(), keyword) } + viewModel { MastodonSignInViewModel(get(), get()) } +} + +private fun Module.search() { + viewModel { SearchInputViewModel(get(), get()) } + viewModel { (content: String) -> SearchSaveViewModel(get(), get(), content) } + viewModel { (keyword: String) -> SearchTweetsViewModel(get(), get(), keyword) } + viewModel { (keyword: String) -> SearchUserViewModel(get(), keyword) } +} + +private fun Module.settings() { + viewModel { AccountNotificationViewModel(get()) } + viewModel { AppearanceViewModel(get()) } + viewModel { DisplayViewModel(get()) } + viewModel { LayoutViewModel(get()) } + viewModel { MiscViewModel(get(), get()) } + viewModel { NotificationViewModel(get()) } + viewModel { StorageViewModel(get()) } +} + +private fun Module.timeline() { + viewModel { NotificationTimelineViewModel(get(), get(), get(), get()) } + viewModel { MentionsTimelineViewModel(get(), get(), get(), get()) } + viewModel { HomeTimelineViewModel(get(), get(), get()) } + viewModel { LocalTimelineViewModel(get(), get(), get()) } + viewModel { FederatedTimelineViewModel(get(), get(), get()) } +} + +private fun Module.trend() { + viewModel { TrendViewModel(get(), get()) } +} + +private fun Module.twitter() { + viewModel { ( + consumerKey: String, + consumerSecret: String, + oauthVerifierProvider: suspend (url: String) -> String?, + pinCodeProvider: suspend (url: String) -> String?, + onResult: (success: Boolean) -> Unit, + ) -> + TwitterSignInViewModel( + get(), + get(), + consumerKey, + consumerSecret, + oauthVerifierProvider, + pinCodeProvider, + onResult, + ) + } + viewModel { (screenName: String) -> TwitterUserViewModel(get(), get(), get(), screenName) } + viewModel { (keyword: String) -> TwitterSearchMediaViewModel(get(), get(), keyword) } +} + +private fun Module.user() { + viewModel { (userKey: MicroBlogKey) -> UserViewModel(get(), get(), get(), userKey) } + viewModel { (userKey: MicroBlogKey) -> UserTimelineViewModel(get(), get(), userKey) } + viewModel { (userKey: MicroBlogKey) -> UserMediaTimelineViewModel(get(), get(), userKey) } + viewModel { (userKey: MicroBlogKey) -> UserFavouriteTimelineViewModel(get(), get(), userKey) } + viewModel { (userKey: MicroBlogKey) -> FollowingViewModel(get(), get(), userKey) } + viewModel { (userKey: MicroBlogKey) -> FollowersViewModel(get(), get(), userKey) } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt new file mode 100644 index 000000000..b06e0d3d2 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.extensions + +import moe.tlaster.precompose.viewmodel.ViewModel +import org.koin.core.definition.Definition +import org.koin.core.instance.InstanceFactory +import org.koin.core.instance.newInstance +import org.koin.core.module.Module +import org.koin.core.qualifier.Qualifier + +inline fun Module.viewModel( + qualifier: Qualifier? = null, + noinline definition: Definition +): Pair> { + return factory(qualifier, definition) +} + +inline fun Module.viewModel( + qualifier: Qualifier? = null +): Pair> { + return factory(qualifier) { newInstance(it) } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 9c0aa6383..19093936a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.repository +import androidx.datastore.core.DataStore import androidx.paging.ExperimentalPagingApi import androidx.paging.PagingData import com.twidere.services.mastodon.MastodonService @@ -29,6 +30,7 @@ import com.twidere.services.nitter.NitterService import com.twidere.services.twitter.TwitterService import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus @@ -36,11 +38,15 @@ import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.paging.toUi import com.twidere.twiderex.paging.mediator.status.MastodonStatusContextMediator import com.twidere.twiderex.paging.mediator.status.TwitterConversationMediator +import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flattenMerge +import kotlinx.coroutines.flow.flow class StatusRepository( private val database: CacheDatabase, - private val nitterService: NitterService?, + private val preferences: DataStore, ) { fun loadStatus( statusKey: MicroBlogKey, @@ -53,7 +59,11 @@ class StatusRepository( return database.statusDao().findWithStatusKey(statusKey, accountKey) } - suspend fun updateStatus(statusKey: MicroBlogKey, accountKey: MicroBlogKey, action: (UiStatus) -> UiStatus) { + suspend fun updateStatus( + statusKey: MicroBlogKey, + accountKey: MicroBlogKey, + action: (UiStatus) -> UiStatus + ) { database.statusDao().findWithStatusKey(statusKey, accountKey = accountKey)?.let { database.statusDao().insertAll(listOf(action.invoke(it)), accountKey) } @@ -85,12 +95,18 @@ class StatusRepository( platformType: PlatformType, service: MicroBlogService, accountKey: MicroBlogKey - ): Flow> { + ): Flow> = flow { // TODO: remove usage of `when` val remoteMediator = when (platformType) { PlatformType.Twitter -> TwitterConversationMediator( service = service as TwitterService, - nitterService = nitterService, + nitterService = preferences.data.first().nitterInstance.takeIf { it.isNotEmpty() } + ?.let { + NitterService( + it.trimEnd('/'), + TwidereServiceFactory.createHttpClientFactory() + ) + }, statusKey = statusKey, accountKey = accountKey, database = database, @@ -104,6 +120,6 @@ class StatusRepository( database = database, ) } - return remoteMediator.pager().toUi() - } + emit(remoteMediator.pager().toUi()) + }.flattenMerge() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt new file mode 100644 index 000000000..6c8a3e073 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -0,0 +1,95 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.lists + +import androidx.compose.runtime.mutableStateMapOf +import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.ListsUsersRepository +import com.twidere.twiderex.utils.notifyError +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope + +class ListsAddMemberViewModel( + private val listsUsersRepository: ListsUsersRepository, + private val inAppNotification: InAppNotification, + private val accountRepository: AccountRepository, + private val listId: String, +) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null) + } + + val loading = MutableStateFlow(false) + val pendingMap = mutableStateMapOf() + + fun addToOrRemove(user: UiUser) { + if (pendingMap[user.userKey] == null) { + loading.value = true + loadingRequest { + account.firstOrNull()?.let { account -> + listsUsersRepository.addMember( + listId = listId, + user = user, + service = account.service as ListsService, + ) + pendingMap[user.userKey] = user + } + } + } else { + loadingRequest { + account.firstOrNull()?.let { account -> + listsUsersRepository.removeMember( + service = account.service as ListsService, + listId = listId, + user = user + ) + pendingMap.remove(user.userKey) + } + } + } + } + + fun isInPendingList(user: UiUser): Boolean { + return pendingMap[user.userKey] != null + } + + private fun loadingRequest(request: suspend () -> Unit) { + loading.value = true + viewModelScope.launch { + runCatching { + request() + }.onFailure { + inAppNotification.notifyError(it) + loading.value = false + }.onSuccess { + loading.value = false + } + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index e4bdf4f4e..5440af03d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -20,27 +20,20 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.compose.runtime.mutableStateMapOf import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.services.microblog.ListsService import com.twidere.twiderex.extensions.asStateIn -import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository -import com.twidere.twiderex.utils.notifyError import com.twidere.twiderex.viewmodel.user.UserListViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch -import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class ListsUserViewModel( @@ -100,62 +93,3 @@ class ListsUserViewModel( } } } - -class ListsAddMemberViewModel( - private val listsUsersRepository: ListsUsersRepository, - private val inAppNotification: InAppNotification, - private val accountRepository: AccountRepository, - private val listId: String, -) : ViewModel() { - private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) - } - - val loading = MutableStateFlow(false) - val pendingMap = mutableStateMapOf() - - fun addToOrRemove(user: UiUser) { - if (pendingMap[user.userKey] == null) { - loading.value = true - loadingRequest { - account.firstOrNull()?.let { account -> - listsUsersRepository.addMember( - listId = listId, - user = user, - service = account.service as ListsService, - ) - pendingMap[user.userKey] = user - } - } - } else { - loadingRequest { - account.firstOrNull()?.let { account -> - listsUsersRepository.removeMember( - service = account.service as ListsService, - listId = listId, - user = user - ) - pendingMap.remove(user.userKey) - } - } - } - } - - fun isInPendingList(user: UiUser): Boolean { - return pendingMap[user.userKey] != null - } - - private fun loadingRequest(request: suspend () -> Unit) { - loading.value = true - viewModelScope.launch { - runCatching { - request() - }.onFailure { - inAppNotification.notifyError(it) - loading.value = false - }.onSuccess { - loading.value = false - } - } - } -} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt index fb8503b80..4ed54cc21 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt @@ -20,21 +20,24 @@ */ package moe.tlaster.precompose.viewmodel +import kotlin.reflect.KClass + inline fun ViewModelStore.getViewModel( noinline creator: () -> T, ): T { val key = T::class.qualifiedName.toString() - return getViewModel(key, creator) + return getViewModel(key, T::class, creator) } -inline fun ViewModelStore.getViewModel( +fun ViewModelStore.getViewModel( key: String, - noinline creator: () -> T, + clazz: KClass, + creator: () -> T, ): T { val existing = get(key) - if (existing != null && existing is T) { + if (existing != null && clazz.isInstance(existing)) { @Suppress("UNCHECKED_CAST") - return existing + return existing as T } else { @Suppress("ControlFlowWithEmptyBody") if (existing != null) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt similarity index 83% rename from common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt index 0933a7a6a..fef162556 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/PlatformModule.android.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt @@ -18,13 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules -import com.twidere.twiderex.kmp.ResLoader import org.koin.dsl.module -internal actual val platformModule = module { - single { - ResLoader(get()) - } +actual val actionModule = module { } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt new file mode 100644 index 000000000..d8fb7b063 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt @@ -0,0 +1,26 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.di.modules + +import org.koin.dsl.module + +actual val kmpModule = module { +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt similarity index 95% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt index 6cb005f64..82a2740de 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PlatformModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import com.twidere.twiderex.kmp.ResLoader import org.koin.dsl.module diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.desktop.kt similarity index 96% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.desktop.kt index 4e5ced08a..99bf91345 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/PreferencesModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.desktop.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import org.koin.core.scope.Scope import java.io.File From 6d34e1fc6f4d4a68522ca082d77c693ba908dae7 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 7 Sep 2021 18:15:47 +0800 Subject: [PATCH 138/615] fix common build --- .../moe/tlaster/precompose/viewmodel/compose/ViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt index b78d24071..7276e3126 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt @@ -37,6 +37,6 @@ inline fun viewModel( if (key == null) { it.getViewModel(creator) } else { - it.getViewModel(key, creator) + it.getViewModel(key, VM::class, creator) } } From 3690d1f6dcc0e06cb01a247acf4086d090419e4b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 7 Sep 2021 18:28:59 +0800 Subject: [PATCH 139/615] fix common build --- .../kotlin/com/twidere/twiderex/repository/StatusRepository.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index 19093936a..a2cf2e3e2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -39,6 +39,7 @@ import com.twidere.twiderex.paging.mediator.paging.toUi import com.twidere.twiderex.paging.mediator.status.MastodonStatusContextMediator import com.twidere.twiderex.paging.mediator.status.TwitterConversationMediator import com.twidere.twiderex.preferences.model.MiscPreferences +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flattenMerge @@ -89,7 +90,7 @@ class StatusRepository( ) } - @OptIn(ExperimentalPagingApi::class) + @OptIn(ExperimentalPagingApi::class, FlowPreview::class) fun conversation( statusKey: MicroBlogKey, platformType: PlatformType, From 283ec0959babe550bb22f800d7e8e85fe6758a72 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 8 Sep 2021 11:58:00 +0800 Subject: [PATCH 140/615] add strings for media insertions --- .../twiderex/component/media/MediaInsertMenu.kt | 8 ++++---- .../com/twidere/twiderex/scenes/gif/GifScene.kt | 4 ++-- app/src/main/res/values/strings.xml | 15 +++++++++++++++ localization | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index 27479515a..ab9a23158 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -177,10 +177,10 @@ private fun List.toUi(context: Context) = map { @Composable private fun MediaInsertType.stringName() = when (this) { - MediaInsertType.CAMERA -> "Take Photo" - MediaInsertType.RECORD_VIDEO -> "Record Video" - MediaInsertType.LIBRARY -> "Browse Library" - MediaInsertType.GIF -> "Add GIF" + MediaInsertType.CAMERA -> stringResource(id = R.string.accessibility_scene_compose_media_insert_camera) + MediaInsertType.RECORD_VIDEO -> stringResource(id = R.string.accessibility_scene_compose_media_insert_record_video) + MediaInsertType.LIBRARY -> stringResource(id = R.string.accessibility_scene_compose_media_insert_library) + MediaInsertType.GIF -> stringResource(id = R.string.accessibility_scene_compose_media_insert_gif) } @Composable diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt index 3caad29c9..ab5e8369d 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt @@ -84,7 +84,7 @@ fun GifScene() { AppBarNavigationButton() }, title = { - Text(text = "GIPHY") + Text(text = stringResource(id = R.string.accessibility_scene_gif_title)) }, actions = { IconButton( @@ -168,7 +168,7 @@ private fun SearchInput(modifier: Modifier = Modifier, input: String, onValueCha value = input, onValueChange = onValueChanged, modifier = Modifier.weight(1f), placeholder = { - Text(text = "Search GIF") + Text(text = stringResource(id = R.string.accessibility_scene_gif_search)) }, maxLines = 1, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ced0f9c87..2f1f5acd0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -115,7 +115,10 @@ Take photo OK Sign in + %s boosted %s retweeted + You retweeted + You boosted Media %s person %s vote @@ -124,11 +127,16 @@ %s people Show this thread Share link + Bookmark Delete tweet Quote Retweet + Pin on Profile Copy text + Unpin from Profile + Share Vote + Translate Copy link %s quote %s quotes @@ -169,11 +177,17 @@ Add Add image Add mention + Browse Library + Add GIF + Take Photo + Record Video Open draft Enable location Disable location Thread mode Send + Search GIF + GIPHY Load Website Media @@ -271,6 +285,7 @@ Always Media previews Automatic + Mute by default Auto playback Off Display diff --git a/localization b/localization index 9490971a1..77c585dce 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 9490971a1e0429701b15181dd3a1aa890a29a912 +Subproject commit 77c585dce05c2c95d57e2299945aaa18061d0e1c From 859a505476beff52e4f1f0ce38273fa44ef9ebbc Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 8 Sep 2021 15:54:15 +0800 Subject: [PATCH 141/615] add preview to UiMediaInsert and add MediaInsertProvider --- .../component/media/MediaInsertMenu.kt | 41 +++++------ .../com/twidere/twiderex/di/TwidereModule.kt | 6 ++ .../twiderex/model/ui/UiMediaInsert.kt | 28 +------- .../twiderex/scenes/compose/ComposeScene.kt | 10 +-- .../twiderex/scenes/dm/DMConversationScene.kt | 4 +- .../utils/media/MediaInsertProvider.kt | 68 +++++++++++++++++++ .../viewmodel/compose/ComposeViewModel.kt | 27 +++----- 7 files changed, 112 insertions(+), 72 deletions(-) create mode 100644 app/src/main/kotlin/com/twidere/twiderex/utils/media/MediaInsertProvider.kt diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index ab9a23158..9f4e30236 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.component.media -import android.content.Context import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -47,15 +46,16 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.MediaInsertType -import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.utils.FileProviderHelper +import com.twidere.twiderex.utils.media.MediaInsertProvider import kotlinx.coroutines.launch import java.util.UUID private const val VideoSuffix = ".mp4" +private const val ImageSuffix = ".jpg" @OptIn(ExperimentalMaterialApi::class) @Composable @@ -67,19 +67,26 @@ fun MediaInsertMenu( onResult: (List) -> Unit ) { val context = LocalContext.current + val mediaInsertProvider = remember { + MediaInsertProvider(context) + } + val scope = rememberCoroutineScope() val filePickerLauncher = if (supportMultipleSelect) rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenMultipleDocuments(), onResult = { - onResult(it.filterNotNull().toUi(context)) + scope.launch { + onResult(it.filterNotNull().toUi(mediaInsertProvider)) + } }, ) else rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument(), onResult = { - onResult(listOfNotNull(it).toUi(context)) + scope.launch { + onResult(listOfNotNull(it).toUi(mediaInsertProvider)) + } }, ) val navController = LocalNavController.current - val scope = rememberCoroutineScope() var cameraTempUri by remember { mutableStateOf(Uri.EMPTY) @@ -88,7 +95,9 @@ fun MediaInsertMenu( val cameraLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.TakePicture(), onResult = { - if (it) onResult(listOf(cameraTempUri).toUi(context)) + scope.launch { + if (it) onResult(listOf(cameraTempUri).toUi(mediaInsertProvider)) + } }, ) @@ -99,7 +108,9 @@ fun MediaInsertMenu( val videoRecordLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CaptureVideo(), onResult = { - if (it) onResult(listOf(UiMediaInsert(videoTempUri, MediaType.video))) + scope.launch { + if (it) onResult(listOf(mediaInsertProvider.provideUiMediaInsert(videoTempUri))) + } }, ) @@ -114,7 +125,7 @@ fun MediaInsertMenu( onClick = { when (it) { MediaInsertType.CAMERA -> { - cameraTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = UUID.randomUUID().toString(), context) + cameraTempUri = FileProviderHelper.getUriFromMedias(mediaFileName = "${System.currentTimeMillis()}$ImageSuffix", context) cameraLauncher.launch(cameraTempUri) } MediaInsertType.RECORD_VIDEO -> { @@ -125,7 +136,7 @@ fun MediaInsertMenu( MediaInsertType.GIF -> scope.launch { navController.navigateForResult(RootRoute.Gif.Home) ?.let { result -> - onResult(listOf(result as Uri).toUi(context)) + onResult(listOf(result as Uri).toUi(mediaInsertProvider)) } } } @@ -163,16 +174,8 @@ fun MediaInsertMenu( } } -private fun List.toUi(context: Context) = map { - val mimeType = context.contentResolver.getType(it) ?: "image/*" - UiMediaInsert( - uri = it, - type = when { - mimeType.startsWith("video") -> MediaType.video - mimeType == "image/gif" -> MediaType.animated_gif - else -> MediaType.photo - } - ) +private suspend fun List.toUi(mediaInsertProvider: MediaInsertProvider) = map { + mediaInsertProvider.provideUiMediaInsert(it) } @Composable diff --git a/app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt index fd78bd461..4b6631122 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt @@ -42,6 +42,7 @@ import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.proto.MiscPreferences import com.twidere.twiderex.utils.PlatformResolver +import com.twidere.twiderex.utils.media.MediaInsertProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -107,4 +108,9 @@ object TwidereModule { fun provideRemoteNavigator(@ApplicationContext context: Context): RemoteNavigator = AndroidRemoteNavigator( context = context ) + + @Provides + fun provideMediaInsertProvider(@ApplicationContext context: Context): MediaInsertProvider { + return MediaInsertProvider(context = context) + } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt index 6d8b1385c..d739ef15f 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt @@ -20,35 +20,11 @@ */ package com.twidere.twiderex.model.ui -import android.content.Context -import android.graphics.Bitmap -import android.media.MediaMetadataRetriever import android.net.Uri import com.twidere.twiderex.model.enums.MediaType data class UiMediaInsert( val uri: Uri, + val preview: Any, val type: MediaType -) { - companion object { - fun UiMediaInsert.getVideoThumb(context: Context): Bitmap? { - return if (type == MediaType.video) { - var bitmap: Bitmap? = null - var mediaMetadataRetriever: MediaMetadataRetriever? = null - try { - mediaMetadataRetriever = MediaMetadataRetriever() - mediaMetadataRetriever.setDataSource(context, uri) - bitmap = mediaMetadataRetriever.getFrameAtTime( - 1000, - MediaMetadataRetriever.OPTION_CLOSEST_SYNC - ) - } catch (e: Exception) { - e.printStackTrace() - } finally { - mediaMetadataRetriever?.release() - } - bitmap - } else null - } - } -} +) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index 4f30a4c99..f9de28990 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.scenes.compose import android.Manifest import android.annotation.SuppressLint -import android.content.Context import android.location.Location import android.net.Uri import androidx.activity.compose.BackHandler @@ -98,7 +97,6 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource @@ -135,7 +133,6 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.model.ui.UiMediaInsert -import com.twidere.twiderex.model.ui.UiMediaInsert.Companion.getVideoThumb import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -454,7 +451,6 @@ private fun ComposeImageList( images: List, viewModel: ComposeViewModel ) { - val context = LocalContext.current Spacer(modifier = Modifier.height(ComposeImageListDefaults.Spacing)) LazyRow( modifier = Modifier.padding(ComposeImageListDefaults.ContentPadding), @@ -462,7 +458,7 @@ private fun ComposeImageList( itemsIndexed( items = images, ) { index, item -> - ComposeImage(item, viewModel, context) + ComposeImage(item, viewModel) if (index != images.lastIndex) { Spacer(modifier = Modifier.width(ComposeImageListDefaults.ItemSpacing)) } @@ -1327,7 +1323,7 @@ private object ComposeActionsDefaults { } @Composable -private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel, context: Context) { +private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel) { var expanded by remember { mutableStateOf(false) } val type = item.type val navController = LocalNavController.current @@ -1353,7 +1349,7 @@ private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel, conte ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = item.getVideoThumb(context) ?: item.uri) + NetworkImage(data = item.preview) when (type) { MediaType.animated_gif -> Image( diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index 5be66f749..d48a2afdd 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -78,7 +78,6 @@ import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMediaInsert -import com.twidere.twiderex.model.ui.UiMediaInsert.Companion.getVideoThumb import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMEventViewModel @@ -237,7 +236,6 @@ private object MessageActionComponentDefaults { @Composable fun InputMediaPreview(inputImage: UiMediaInsert?, onRemove: () -> Unit) { if (inputImage == null) return - val context = LocalContext.current Box(modifier = Modifier.padding(InputPhotoPreviewDefaults.ContentPadding)) { Box( modifier = Modifier @@ -250,7 +248,7 @@ fun InputMediaPreview(inputImage: UiMediaInsert?, onRemove: () -> Unit) { ) .clip(MaterialTheme.shapes.small), ) { - NetworkImage(data = inputImage.getVideoThumb(context) ?: inputImage.uri) + NetworkImage(data = inputImage.preview) } Box( modifier = Modifier diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/media/MediaInsertProvider.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/media/MediaInsertProvider.kt new file mode 100644 index 000000000..fe5e58b49 --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/media/MediaInsertProvider.kt @@ -0,0 +1,68 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils.media + +import android.content.Context +import android.graphics.Bitmap +import android.media.MediaMetadataRetriever +import android.net.Uri +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiMediaInsert +import kotlinx.coroutines.coroutineScope + +// Todo change to expect/actual after merge to desktop +class MediaInsertProvider(private val context: Context) { + + suspend fun provideUiMediaInsert(uri: Uri): UiMediaInsert { + val type = (context.contentResolver.getType(uri) ?: "image/*").let { + when { + it.startsWith("video") -> MediaType.video + it == "image/gif" -> MediaType.animated_gif + else -> MediaType.photo + } + } + return UiMediaInsert( + uri = uri, + preview = if (type == MediaType.video) getVideoThumbnail(uri) ?: uri else uri, + type = type, + ) + } + + private suspend fun getVideoThumbnail(uri: Uri): Bitmap? { + return coroutineScope { + var bitmap: Bitmap? = null + var mediaMetadataRetriever: MediaMetadataRetriever? = null + try { + mediaMetadataRetriever = MediaMetadataRetriever() + mediaMetadataRetriever.setDataSource(context, uri) + bitmap = mediaMetadataRetriever.getFrameAtTime( + 1000, + MediaMetadataRetriever.OPTION_CLOSEST_SYNC + ) + } catch (e: Exception) { + e.printStackTrace() + } finally { + mediaMetadataRetriever?.release() + } + bitmap + } + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index df6f3c9ca..a230f9164 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.compose import android.Manifest.permission -import android.content.Context import android.location.Criteria import android.location.Location import android.location.LocationListener @@ -58,12 +57,12 @@ import com.twidere.twiderex.repository.DraftRepository import com.twidere.twiderex.repository.StatusRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.MastodonEmojiCache +import com.twidere.twiderex.utils.media.MediaInsertProvider import com.twidere.twiderex.utils.notify import com.twidere.twiderex.worker.draft.SaveDraftWorker import com.twitter.twittertext.Extractor import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -71,6 +70,7 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import java.util.UUID enum class ComposeType { @@ -106,7 +106,7 @@ class DraftComposeViewModel @AssistedInject constructor( repository: StatusRepository, userRepository: UserRepository, workManager: WorkManager, - @ApplicationContext context: Context, + mediaInsertProvider: MediaInsertProvider, inAppNotification: InAppNotification, @Assisted account: AccountDetails, @Assisted private val draft: DbDraft, @@ -127,20 +127,13 @@ class DraftComposeViewModel @AssistedInject constructor( init { setText(TextFieldValue(draft.content)) - putImages( - draft.media.map { - val uri = Uri.parse(it) - val mimeType = context.contentResolver.getType(uri) ?: "image/*" - UiMediaInsert( - Uri.parse(it), - type = when { - mimeType.startsWith("video") -> MediaType.video - mimeType == "image/gif" -> MediaType.animated_gif - else -> MediaType.photo - } - ) - } - ) + viewModelScope.launch { + putImages( + draft.media.map { + mediaInsertProvider.provideUiMediaInsert(Uri.parse(it)) + } + ) + } excludedReplyUserIds.value = draft.excludedReplyUserIds ?: emptyList() } From e9e68599ad60e076d9ad170cc3c859a40706dba2 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 8 Sep 2021 16:58:58 +0800 Subject: [PATCH 142/615] migrate some foundation components from android to common --- .../kotlin/com/twidere/twiderex/component/foundation/AppBar.kt | 0 .../com/twidere/twiderex/component/foundation/CheckboxItem.kt | 0 .../com/twidere/twiderex/component/foundation/ColoredSwitch.kt | 0 .../com/twidere/twiderex/component/foundation/GridLayout.kt | 0 .../twidere/twiderex/component/foundation/HorizontalDivider.kt | 0 .../com/twidere/twiderex/component/foundation/LoadingProgress.kt | 0 .../twidere/twiderex/component/foundation/NestedScrollScaffold.kt | 0 .../kotlin/com/twidere/twiderex/component/foundation/Pager.kt | 0 .../com/twidere/twiderex/component/foundation/SignInButton.kt | 0 .../twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt | 0 .../kotlin/com/twidere/twiderex/component/foundation/TextInput.kt | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/Pager.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt (100%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/Pager.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt From 7b0c8f6548c3441820fa24c72d68203b8924949f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 8 Sep 2021 18:15:35 +0800 Subject: [PATCH 143/615] migrate vm usage --- android/build.gradle.kts | 5 +- android/src/main/AndroidManifest.xml | 8 +- .../kotlin/com/twidere/twiderex/TwidereApp.kt | 22 +- .../com/twidere/twiderex/TwidereXActivity.kt | 38 +-- .../twiderex/component/UserComponent.kt | 52 ++-- .../com/twidere/twiderex/di/AndroidModule.kt | 67 ----- .../twiderex/di/InitializerEntryPoint.kt | 50 ---- .../com/twidere/twiderex/di/JobModule.kt | 260 ------------------ .../com/twidere/twiderex/di/KoinModule.kt | 50 ---- .../twidere/twiderex/di/PreferenceModule.kt | 59 ---- .../twidere/twiderex/di/RepositoryModule.kt | 135 --------- .../com/twidere/twiderex/di/TwidereModule.kt | 133 --------- .../twiderex/di/assisted/AssistedViewModel.kt | 60 ---- .../twidere/twiderex/scenes/DraftListScene.kt | 7 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 2 + .../com/twidere/twiderex/scenes/MediaScene.kt | 12 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 7 +- .../twidere/twiderex/scenes/SignInScene.kt | 6 +- .../twidere/twiderex/scenes/StatusScene.kt | 13 +- .../twiderex/scenes/compose/ComposeScene.kt | 38 ++- .../compose/ComposeSearchHashtagScene.kt | 10 +- .../scenes/compose/ComposeSearchUserScene.kt | 7 +- .../scenes/dm/DMConversationListScene.kt | 9 +- .../twiderex/scenes/dm/DMConversationScene.kt | 13 +- .../scenes/dm/DMNewConversationScene.kt | 57 ++-- .../scenes/home/AllNotificationItem.kt | 11 +- .../scenes/home/DMConversationListItem.kt | 1 + .../scenes/home/DraftNavigationItem.kt | 1 + .../twiderex/scenes/home/HomeMenu.android.kt | 40 +++ .../twiderex/scenes/home/HomeTimelineItem.kt | 11 +- .../scenes/home/ListsNavigationItem.kt | 1 + .../twidere/twiderex/scenes/home/MeItem.kt | 1 + .../twiderex/scenes/home/MentionItem.kt | 20 +- .../twiderex/scenes/home/NotificationItem.kt | 10 +- .../twiderex/scenes/home/SearchItem.kt | 16 +- .../home/mastodon/FederatedTimelineItem.kt | 11 +- .../scenes/home/mastodon/LocalTimelineItem.kt | 11 +- .../home/mastodon/MastodonNotificationItem.kt | 2 +- .../scenes/lists/ListsAddMembersScene.kt | 16 +- .../scenes/lists/ListsMembersScene.kt | 14 +- .../twiderex/scenes/lists/ListsScene.kt | 8 +- .../scenes/lists/ListsSubscribersScene.kt | 12 +- .../scenes/lists/ListsTimelineScene.kt | 39 +-- .../platform/MastodonListsCreateDialog.kt | 28 +- .../lists/platform/MastodonListsEditDialog.kt | 12 +- .../lists/platform/TwitterListsCreateScene.kt | 32 ++- .../lists/platform/TwitterListsEditScene.kt | 16 +- .../scenes/mastodon/MastodonHashtagScene.kt | 15 +- .../scenes/mastodon/MastodonSignInScene.kt | 9 +- .../scenes/search/SearchInputScene.kt | 11 +- .../twiderex/scenes/search/SearchScene.kt | 15 +- .../search/tabs/MastodonSearchHashtagItem.kt | 13 +- .../scenes/search/tabs/SearchTweetsItem.kt | 13 +- .../scenes/search/tabs/SearchUserItem.kt | 13 +- .../search/tabs/TwitterSearchMediaItem.kt | 13 +- .../settings/AccountNotificationScene.kt | 38 +-- .../scenes/settings/AppearanceScene.kt | 7 +- .../twiderex/scenes/settings/DisplayScene.kt | 7 +- .../twiderex/scenes/settings/LayoutScene.kt | 12 +- .../twiderex/scenes/settings/MiscScene.kt | 6 +- .../scenes/settings/NotificationScene.kt | 7 +- .../twiderex/scenes/settings/StorageScene.kt | 7 +- .../scenes/twitter/TwitterSigninScene.kt | 44 +-- .../scenes/twitter/user/TwitterUserScene.kt | 15 +- .../twiderex/scenes/user/FollowersScene.kt | 13 +- .../twiderex/scenes/user/FollowingScene.kt | 13 +- .../twidere/twiderex/scenes/user/UserScene.kt | 17 +- .../twidere/twiderex/utils/HttpErrorCodes.kt | 25 -- .../twiderex/utils/PlatformResolver.kt | 3 +- common/build.gradle.kts | 3 +- .../twidere/twiderex/TwidereApplication.kt | 3 +- .../di/modules/ActionsModule.android.kt | 2 +- .../twiderex/di/modules/KmpModule.android.kt | 2 + .../initializer/DirectMessageInitializer.kt | 10 +- .../NotificationChannelInitializer.kt | 23 +- .../initializer/NotificationInitializer.kt | 10 +- .../initializer}/TwidereServiceInitializer.kt | 14 +- .../component/lazy/LazyListController.kt | 2 +- .../kotlin/com/twidere/twiderex/di/Setup.kt | 5 + .../twiderex/model}/HomeNavigationItem.kt | 2 +- .../notification/NotificationChannelSpec.kt | 15 +- .../twitter/TwitterSignInViewModel.kt | 10 +- 82 files changed, 434 insertions(+), 1416 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt create mode 100644 android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt (88%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt (90%) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt (89%) rename {android/src/main/kotlin/com/twidere/twiderex/http => common/src/androidMain/kotlin/com/twidere/twiderex/initializer}/TwidereServiceInitializer.kt (78%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt (95%) rename {android/src/main/kotlin/com/twidere/twiderex/scenes/home => common/src/commonMain/kotlin/com/twidere/twiderex/model}/HomeNavigationItem.kt (97%) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index b0e96505b..6888dd91f 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -7,7 +7,6 @@ buildscript { } dependencies { - classpath("com.google.dagger:hilt-android-gradle-plugin:${Versions.hilt}") if (enableGoogleVariant) { // START Non-FOSS component @@ -33,7 +32,6 @@ if (enableGoogleVariant) { apply(plugin = "com.google.firebase.crashlytics") // END Non-FOSS component } -apply(plugin = "dagger.hilt.android.plugin") android { compileSdk = AndroidSdk.compile @@ -163,13 +161,12 @@ dependencies { kotlinCoroutines() implementation(projects.services) implementation(projects.common) - ksp(projects.assistedProcessor) + // ksp(projects.assistedProcessor) implementation(projects.routeProcessor) ksp(projects.routeProcessor) compose() paging() datastore() - hilt() accompanist() widget() misc() diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index ea12dd30e..7c92be06a 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -32,18 +32,18 @@ android:value="androidx.startup" tools:node="remove" /> diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt index 9fefbffda..6f18fa0d4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt @@ -20,27 +20,13 @@ */ package com.twidere.twiderex -import android.app.Application import android.content.Context -import androidx.hilt.work.HiltWorkerFactory import androidx.startup.AppInitializer -import androidx.work.Configuration -import com.twidere.twiderex.http.TwidereServiceInitializer -import com.twidere.twiderex.notification.NotificationInitializer -import com.twidere.twiderex.worker.dm.DirectMessageInitializer -import dagger.hilt.android.HiltAndroidApp -import javax.inject.Inject - -@HiltAndroidApp -class TwidereApp : Application(), Configuration.Provider { - @Inject - lateinit var workerFactory: HiltWorkerFactory - - override fun getWorkManagerConfiguration() = - Configuration.Builder() - .setWorkerFactory(workerFactory) - .build() +import com.twidere.twiderex.initializer.DirectMessageInitializer +import com.twidere.twiderex.initializer.NotificationInitializer +import com.twidere.twiderex.initializer.TwidereServiceInitializer +class TwidereApp : TwidereApplication() { override fun onCreate() { super.onCreate() // Note:Installs with missing splits are now blocked on devices which have Play Protect active or run on Android 10. diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 782bd65f2..d6ecff626 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -63,7 +63,6 @@ import com.google.accompanist.insets.ProvideWindowInsets import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.action.StatusActions import com.twidere.twiderex.component.foundation.LocalInAppNotification -import com.twidere.twiderex.di.assisted.ProvideAssistedFactory import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.Router import com.twidere.twiderex.notification.InAppNotification @@ -79,15 +78,10 @@ import com.twidere.twiderex.ui.LocalWindowInsetsController import com.twidere.twiderex.utils.CustomTabSignInChannel import com.twidere.twiderex.utils.LocalPlatformResolver import com.twidere.twiderex.utils.PlatformResolver -import com.twidere.twiderex.viewmodel.ActiveAccountViewModel -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.MutableStateFlow import moe.tlaster.precompose.navigation.NavController import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.getViewModel -import javax.inject.Inject -@AndroidEntryPoint class TwidereXActivity : ComponentActivity() { private val navController by lazy { @@ -107,22 +101,15 @@ class TwidereXActivity : ComponentActivity() { } } - @Inject - lateinit var viewModelHolder: TwidereXActivityAssistedViewModelHolder - - @Inject - lateinit var statusActions: StatusActions + val statusActions: StatusActions by inject() val preferencesHolder: PreferencesHolder by inject() - @Inject - lateinit var inAppNotification: InAppNotification + val inAppNotification: InAppNotification by inject() - @Inject - lateinit var connectivityManager: ConnectivityManager + val connectivityManager: ConnectivityManager by inject() - @Inject - lateinit var platformResolver: PlatformResolver + val platformResolver: PlatformResolver by inject() @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -181,7 +168,8 @@ class TwidereXActivity : ComponentActivity() { private fun App() { val windowInsetsControllerCompat = remember { WindowInsetsControllerCompat(window, window.decorView) } - val accountViewModel = com.twidere.twiderex.di.ext.getViewModel() + val accountViewModel = + com.twidere.twiderex.di.ext.getViewModel() val account by accountViewModel.account.observeAsState(null) val isActiveNetworkMetered by isActiveNetworkMetered.observeAsState(initial = false) CompositionLocalProvider( @@ -199,16 +187,12 @@ class TwidereXActivity : ComponentActivity() { ProvidePreferences( preferencesHolder, ) { - ProvideAssistedFactory( - viewModelHolder.factory, + ProvideWindowInsets( + windowInsetsAnimationsEnabled = true ) { - ProvideWindowInsets( - windowInsetsAnimationsEnabled = true - ) { - Router( - navController = navController - ) - } + Router( + navController = navController + ) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 4e628a39e..6e04aa848 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -97,7 +97,7 @@ import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.component.status.withAvatarClip -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.MicroBlogKey @@ -106,7 +106,6 @@ import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.navigation.twidereXSchema -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.user.UserFavouriteTimelineViewModel import com.twidere.twiderex.viewmodel.user.UserMediaTimelineViewModel @@ -117,19 +116,17 @@ import moe.tlaster.nestedscrollview.VerticalNestedScrollView import moe.tlaster.nestedscrollview.rememberNestedScrollViewState import moe.tlaster.placeholder.Placeholder import moe.tlaster.precompose.navigation.NavController +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalPagerApi::class) @Composable fun UserComponent( userKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account, - userKey, - ) { - it.create(account, userKey) + val viewModel: UserViewModel = getViewModel { + parametersOf(userKey) } + val isMe by viewModel.isMe.observeAsState(initial = false) val tabs = listOf( UserTabComponent( painterResource(id = R.drawable.ic_float_left), @@ -144,7 +141,7 @@ fun UserComponent( UserMediaTimeline(userKey = userKey) }, ).let { - if (viewModel.isMe || userKey.host == MicroBlogKey.TwitterHost) { + if (isMe || userKey.host == MicroBlogKey.TwitterHost) { it + UserTabComponent( painterResource(id = R.drawable.ic_heart), stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_favourite) @@ -233,16 +230,10 @@ fun UserStatusTimeline( viewModel: UserViewModel, ) { val user by viewModel.user.observeAsState(initial = null) - val account = LocalActiveAccount.current ?: return var excludeReplies by rememberSaveable { mutableStateOf(false) } - val timelineViewModel = - assistedViewModel( - account, - userKey, - excludeReplies, - ) { - it.create(account, userKey = userKey, excludeReplies) - } + val timelineViewModel: UserTimelineViewModel = getViewModel { + parametersOf(userKey, excludeReplies) + } val timelineSource = timelineViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -358,14 +349,9 @@ private object UserStatusTimelineFilterDefaults { fun UserMediaTimeline( userKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val mediaViewModel = - assistedViewModel( - account, - userKey, - ) { - it.create(account, userKey = userKey) - } + val mediaViewModel: UserMediaTimelineViewModel = getViewModel { + parametersOf(userKey) + } val mediaSource = mediaViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -377,14 +363,9 @@ fun UserMediaTimeline( fun UserFavouriteTimeline( userKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val timelineViewModel = - assistedViewModel( - account, - userKey, - ) { - it.create(account, userKey = userKey) - } + val timelineViewModel: UserFavouriteTimelineViewModel = getViewModel { + parametersOf(userKey) + } val timelineSource = timelineViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -402,6 +383,7 @@ fun UserInfo( ) { val user by viewModel.user.observeAsState(initial = null) val navController = LocalNavController.current + val isMe by viewModel.isMe.observeAsState(initial = false) Box( modifier = Modifier .background(MaterialTheme.colors.surface.withElevation()) @@ -448,7 +430,7 @@ fun UserInfo( user?.let { user -> UserInfoName(user) } - if (!viewModel.isMe) { + if (isMe) { Spacer(modifier = Modifier.height(UserInfoDefaults.RelationshipSpacing)) UserRelationship(viewModel) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt deleted file mode 100644 index 279524ade..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/AndroidModule.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.accounts.AccountManager -import android.content.ContentResolver -import android.content.Context -import android.content.SharedPreferences -import android.location.LocationManager -import android.net.ConnectivityManager -import androidx.core.app.NotificationManagerCompat -import androidx.work.WorkManager -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -object AndroidModule { - @Provides - fun provideWorkManager(@ApplicationContext context: Context): WorkManager = - WorkManager.getInstance(context) - - @Provides - fun provideAccountManager(@ApplicationContext context: Context): AccountManager = - AccountManager.get(context) - - @Provides - fun provideSharedPreference(@ApplicationContext context: Context): SharedPreferences = - context.getSharedPreferences("twiderex", Context.MODE_PRIVATE) - - @Provides - fun provideLocationManager(@ApplicationContext context: Context): LocationManager = - context.getSystemService(Context.LOCATION_SERVICE) as LocationManager - - @Provides - fun provideConnectivityManager(@ApplicationContext context: Context): ConnectivityManager = - context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - - @Provides - fun provideContentResolver(@ApplicationContext context: Context): ContentResolver = - context.contentResolver - - @Provides - fun provideNotificationManagerCompat(@ApplicationContext context: Context): NotificationManagerCompat = - NotificationManagerCompat.from(context) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt b/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt deleted file mode 100644 index 4fdcd4e96..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/InitializerEntryPoint.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.content.Context -import com.twidere.twiderex.http.TwidereServiceInitializer -import com.twidere.twiderex.initializer.DirectMessageInitializer -import com.twidere.twiderex.initializer.NotificationChannelInitializer -import com.twidere.twiderex.initializer.NotificationInitializer -import dagger.hilt.EntryPoint -import dagger.hilt.InstallIn -import dagger.hilt.android.EntryPointAccessors -import dagger.hilt.components.SingletonComponent - -@EntryPoint -@InstallIn(SingletonComponent::class) -interface InitializerEntryPoint { - companion object { - fun resolve(context: Context): InitializerEntryPoint { - val appContext = context.applicationContext ?: throw IllegalStateException() - return EntryPointAccessors.fromApplication( - appContext, - InitializerEntryPoint::class.java - ) - } - } - - fun inject(initializer: NotificationChannelInitializer) - fun inject(initializer: NotificationInitializer) - fun inject(initializer: DirectMessageInitializer) - fun inject(initializer: TwidereServiceInitializer) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt deleted file mode 100644 index 86883f273..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/JobModule.kt +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.content.Context -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.jobs.common.DownloadMediaJob -import com.twidere.twiderex.jobs.common.NotificationJob -import com.twidere.twiderex.jobs.common.ShareMediaJob -import com.twidere.twiderex.jobs.compose.MastodonComposeJob -import com.twidere.twiderex.jobs.compose.TwitterComposeJob -import com.twidere.twiderex.jobs.database.DeleteDbStatusJob -import com.twidere.twiderex.jobs.dm.DirectMessageDeleteJob -import com.twidere.twiderex.jobs.dm.DirectMessageFetchJob -import com.twidere.twiderex.jobs.dm.TwitterDirectMessageSendJob -import com.twidere.twiderex.jobs.draft.RemoveDraftJob -import com.twidere.twiderex.jobs.draft.SaveDraftJob -import com.twidere.twiderex.jobs.status.DeleteStatusJob -import com.twidere.twiderex.jobs.status.LikeStatusJob -import com.twidere.twiderex.jobs.status.MastodonVoteJob -import com.twidere.twiderex.jobs.status.RetweetStatusJob -import com.twidere.twiderex.jobs.status.UnRetweetStatusJob -import com.twidere.twiderex.jobs.status.UnlikeStatusJob -import com.twidere.twiderex.kmp.ExifScrambler -import com.twidere.twiderex.kmp.FileResolver -import com.twidere.twiderex.kmp.RemoteNavigator -import com.twidere.twiderex.notification.AppNotificationManager -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.repository.DirectMessageRepository -import com.twidere.twiderex.repository.DraftRepository -import com.twidere.twiderex.repository.NotificationRepository -import com.twidere.twiderex.repository.StatusRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -object JobModule { - - @Provides - fun provideShareMediaJob( - fileResolver: FileResolver, - remoteNavigator: RemoteNavigator - ): ShareMediaJob = ShareMediaJob( - fileResolver = fileResolver, - remoteNavigator = remoteNavigator - ) - - @Provides - fun provideDownloadMediaJob( - accountRepository: AccountRepository, - inAppNotification: InAppNotification, - fileResolver: FileResolver, - ): DownloadMediaJob = DownloadMediaJob( - accountRepository = accountRepository, - inAppNotification = inAppNotification, - fileResolver = fileResolver - ) - - @Provides - fun provideDeleteDbStatusJob( - statusRepository: StatusRepository - ): DeleteDbStatusJob = DeleteDbStatusJob( - statusRepository = statusRepository - ) - - @Provides - fun provideDeleteStatusJob( - accountRepository: AccountRepository, - inAppNotification: InAppNotification, - statusRepository: StatusRepository - ): DeleteStatusJob = DeleteStatusJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideNotificationJob( - @ApplicationContext context: Context, - accountRepository: AccountRepository, - repository: NotificationRepository, - notificationManager: AppNotificationManager - ): NotificationJob = NotificationJob( - applicationContext = context, - repository = repository, - accountRepository = accountRepository, - notificationManager = notificationManager - ) - - @Provides - fun provideLikeStatusJob( - accountRepository: AccountRepository, - statusRepository: StatusRepository, - inAppNotification: InAppNotification - ): LikeStatusJob = LikeStatusJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideRetweetStatusJob( - accountRepository: AccountRepository, - statusRepository: StatusRepository, - inAppNotification: InAppNotification - ): RetweetStatusJob = RetweetStatusJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideUnlikeStatusJob( - accountRepository: AccountRepository, - statusRepository: StatusRepository, - inAppNotification: InAppNotification - ): UnlikeStatusJob = UnlikeStatusJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideUnRetweetStatusJob( - accountRepository: AccountRepository, - statusRepository: StatusRepository, - inAppNotification: InAppNotification - ): UnRetweetStatusJob = UnRetweetStatusJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideMastodonVoteJob( - accountRepository: AccountRepository, - statusRepository: StatusRepository, - inAppNotification: InAppNotification - ): MastodonVoteJob = MastodonVoteJob( - accountRepository = accountRepository, - statusRepository = statusRepository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideRemoveDraftJob( - repository: DraftRepository - ): RemoveDraftJob = RemoveDraftJob( - repository = repository, - ) - - @Provides - fun provideSaveDraftJob( - repository: DraftRepository, - inAppNotification: InAppNotification - ): SaveDraftJob = SaveDraftJob( - repository = repository, - inAppNotification = inAppNotification - ) - - @Provides - fun provideDirectMessageDeleteJob( - repository: DirectMessageRepository, - accountRepository: AccountRepository - ): DirectMessageDeleteJob = DirectMessageDeleteJob( - repository = repository, - accountRepository = accountRepository - ) - - @Provides - fun provideDirectMessageFetchJob( - @ApplicationContext context: Context, - repository: DirectMessageRepository, - accountRepository: AccountRepository, - notificationManager: AppNotificationManager, - ): DirectMessageFetchJob = DirectMessageFetchJob( - applicationContext = context, - repository = repository, - accountRepository = accountRepository, - notificationManager = notificationManager - ) - - @Provides - fun provideTwitterDirectMessageSendJob( - @ApplicationContext context: Context, - accountRepository: AccountRepository, - notificationManager: AppNotificationManager, - fileResolver: FileResolver, - cacheDatabase: CacheDatabase, - ): TwitterDirectMessageSendJob = TwitterDirectMessageSendJob( - context = context, - accountRepository = accountRepository, - notificationManager = notificationManager, - fileResolver = fileResolver, - cacheDatabase = cacheDatabase - ) - - @Provides - fun provideTwitterComposeJob( - @ApplicationContext context: Context, - accountRepository: AccountRepository, - notificationManager: AppNotificationManager, - fileResolver: FileResolver, - cacheDatabase: CacheDatabase, - exifScrambler: ExifScrambler, - statusRepository: StatusRepository, - remoteNavigator: RemoteNavigator - ): TwitterComposeJob = TwitterComposeJob( - context = context, - accountRepository = accountRepository, - notificationManager = notificationManager, - fileResolver = fileResolver, - cacheDatabase = cacheDatabase, - exifScrambler = exifScrambler, - statusRepository = statusRepository, - remoteNavigator = remoteNavigator - ) - - @Provides - fun provideMastodonComposeJob( - @ApplicationContext context: Context, - accountRepository: AccountRepository, - notificationManager: AppNotificationManager, - fileResolver: FileResolver, - cacheDatabase: CacheDatabase, - exifScrambler: ExifScrambler, - remoteNavigator: RemoteNavigator - ): MastodonComposeJob = MastodonComposeJob( - context = context, - accountRepository = accountRepository, - notificationManager = notificationManager, - fileResolver = fileResolver, - cacheDatabase = cacheDatabase, - exifScrambler = exifScrambler, - remoteNavigator = remoteNavigator - ) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt deleted file mode 100644 index 34ff7fed0..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/KoinModule.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import org.koin.android.ext.koin.androidContext -import org.koin.android.ext.koin.androidLogger -import org.koin.core.Koin -import org.koin.core.context.startKoin -import org.koin.mp.KoinPlatformTools -import javax.inject.Singleton - -@Deprecated("Use koin directly") -@Module -@InstallIn(SingletonComponent::class) -object KoinModule { - @Singleton - @Provides - fun getKoin(@ApplicationContext context: Context): Koin { - startKoin { - androidLogger() - androidContext(context) - setupModules() - } - return KoinPlatformTools.defaultContext().get() - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt deleted file mode 100644 index e508c5cbf..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/PreferenceModule.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import androidx.datastore.core.DataStore -import com.twidere.twiderex.preferences.PreferencesHolder -import com.twidere.twiderex.preferences.model.AppearancePreferences -import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.preferences.model.MiscPreferences -import com.twidere.twiderex.preferences.model.NotificationPreferences -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import org.koin.core.Koin -import javax.inject.Singleton - -@Deprecated("Use koin directly") -@Module -@InstallIn(SingletonComponent::class) -object PreferenceModule { - @Singleton - @Provides - fun provideAppearances(koin: Koin): DataStore = - koin.get().appearancePreferences - - @Singleton - @Provides - fun provideDisplay(koin: Koin): DataStore = - koin.get().displayPreferences - - @Singleton - @Provides - fun provideMisc(koin: Koin): DataStore = - koin.get().miscPreferences - - @Singleton - @Provides - fun provideNotification(koin: Koin): DataStore = - koin.get().notificationPreferences -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt deleted file mode 100644 index b996f9a02..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/RepositoryModule.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.accounts.AccountManager -import android.content.Context -import com.twidere.services.nitter.NitterService -import com.twidere.twiderex.cache.FileCacheHandler -import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.model.AccountPreferencesFactory -import com.twidere.twiderex.repository.AccountRepository -import com.twidere.twiderex.repository.CacheRepository -import com.twidere.twiderex.repository.DirectMessageRepository -import com.twidere.twiderex.repository.DraftRepository -import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.repository.ListsUsersRepository -import com.twidere.twiderex.repository.MediaRepository -import com.twidere.twiderex.repository.NotificationRepository -import com.twidere.twiderex.repository.ReactionRepository -import com.twidere.twiderex.repository.SearchRepository -import com.twidere.twiderex.repository.StatusRepository -import com.twidere.twiderex.repository.TimelineRepository -import com.twidere.twiderex.repository.TrendRepository -import com.twidere.twiderex.repository.UserRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object RepositoryModule { - @Provides - fun provideAccountPreferencesFactory(@ApplicationContext context: Context): AccountPreferencesFactory = - AccountPreferencesFactory(context = context) - - @Singleton - @Provides - fun provideAccountRepository( - accountPreferencesFactory: AccountPreferencesFactory, - accountManager: AccountManager, - ): AccountRepository = AccountRepository(accountManager, accountPreferencesFactory) - - @Singleton - @Provides - fun provideDraftRepository(database: AppDatabase): DraftRepository = - DraftRepository(database = database) - - @Provides - fun provideSearchRepository(database: AppDatabase): SearchRepository = - SearchRepository(database = database) - - @Provides - fun provideCacheRepository( - database: CacheDatabase, - appDatabase: AppDatabase, - fileCacheHandler: FileCacheHandler - ): CacheRepository = CacheRepository( - fileCache = fileCacheHandler, - cacheDatabase = database, - appDatabase = appDatabase - ) - - @Provides - fun provideStatusRepository( - database: CacheDatabase, - nitterService: NitterService?, - ): StatusRepository = StatusRepository( - database = database, - nitterService = nitterService, - ) - - @Provides - fun provideReactionRepository(database: CacheDatabase): ReactionRepository = - ReactionRepository(database = database) - - @Provides - fun provideTimelineRepository(database: CacheDatabase): TimelineRepository = - TimelineRepository(database = database) - - @Provides - fun provideUserRepository( - database: CacheDatabase, - accountRepository: AccountRepository - ): UserRepository = UserRepository(database = database, accountRepository = accountRepository) - - @Provides - fun provideListsRepository( - database: CacheDatabase - ) = ListsRepository(database = database) - - @Provides - fun provideListUsersRepository() = ListsUsersRepository() - - @Provides - fun provideNotificationRepository( - database: CacheDatabase, - ): NotificationRepository = NotificationRepository(database = database) - - @Provides - fun provideTrendRepository( - database: CacheDatabase - ) = TrendRepository(database = database) - - @Provides - fun provideDirectMessageRepository( - database: CacheDatabase - ) = DirectMessageRepository(database = database) - - @Provides - fun provideDirectMediaRepository( - database: CacheDatabase - ) = MediaRepository(database) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt b/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt deleted file mode 100644 index dd79ea908..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/TwidereModule.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di - -import android.content.ContentResolver -import android.content.Context -import androidx.core.app.NotificationManagerCompat -import androidx.datastore.core.DataStore -import androidx.work.WorkManager -import com.twidere.services.nitter.NitterService -import com.twidere.twiderex.action.ComposeAction -import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.cache.FileCacheHandler -import com.twidere.twiderex.dataprovider.DataProvider -import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.http.TwidereHttpConfigProvider -import com.twidere.twiderex.http.TwidereServiceFactory -import com.twidere.twiderex.kmp.AndroidExifScrambler -import com.twidere.twiderex.kmp.AndroidFileResolver -import com.twidere.twiderex.kmp.AndroidNotificationManager -import com.twidere.twiderex.kmp.AndroidRemoteNavigator -import com.twidere.twiderex.kmp.ExifScrambler -import com.twidere.twiderex.kmp.FileResolver -import com.twidere.twiderex.kmp.RemoteNavigator -import com.twidere.twiderex.model.AccountPreferences -import com.twidere.twiderex.notification.AppNotificationManager -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.preferences.model.MiscPreferences -import com.twidere.twiderex.utils.PlatformResolver -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object TwidereModule { - @Singleton - @Provides - fun provideDataProvider(): DataProvider = DataProvider.Factory.create() - - @Singleton - @Provides - fun provideCacheDatabase(dataProvider: DataProvider): CacheDatabase = dataProvider.cacheDatabase - - @Singleton - @Provides - fun provideDraftDatabase(dataProvider: DataProvider): AppDatabase = dataProvider.appDatabase - - @Singleton - @Provides - fun provideFileCacheHandler(dataProvider: DataProvider): FileCacheHandler = dataProvider.fileCacheHandler - - @Singleton - @Provides - fun provideComposeQueue( - workManager: WorkManager, - ): ComposeAction = ComposeAction(workManager = workManager) - - @Singleton - @Provides - fun provideInAppNotification(): InAppNotification = InAppNotification() - - @Singleton - @Provides - fun providePlatformResolver(database: CacheDatabase): PlatformResolver = - PlatformResolver(database = database) - - @Provides - fun provideNitterService(preferences: DataStore): NitterService? { - return runBlocking { - preferences.data.first().nitterInstance.takeIf { it.isNotEmpty() } - ?.let { NitterService(it.trimEnd('/'), TwidereServiceFactory.createHttpClientFactory()) } - } - } - - @Singleton - @Provides - fun provideDirectMessageQueue( - workManager: WorkManager, - ): DirectMessageAction = DirectMessageAction(workManager = workManager) - - @Provides - fun provideAccountPreferencesFactory(@ApplicationContext context: Context): AccountPreferences.Factory = - AccountPreferences.Factory(context) - - @Provides - fun provideAppNotificationManager(@ApplicationContext context: Context, notificationManagerCompat: NotificationManagerCompat): AppNotificationManager = AndroidNotificationManager( - context = context, - notificationManagerCompat = notificationManagerCompat - ) - - @Provides - fun provideExifScrambler(@ApplicationContext context: Context): ExifScrambler = AndroidExifScrambler( - context = context - ) - - @Provides - fun provideFileResolver(contentResolver: ContentResolver): FileResolver = AndroidFileResolver( - contentResolver = contentResolver - ) - - @Provides - fun provideRemoteNavigator(@ApplicationContext context: Context): RemoteNavigator = AndroidRemoteNavigator( - context = context - ) - - @Provides - fun provideTwidereHttpConfigProvider(miscPreferences: DataStore): TwidereHttpConfigProvider = TwidereHttpConfigProvider(miscPreferences) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt b/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt deleted file mode 100644 index f9463ecb1..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/di/assisted/AssistedViewModel.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.di.assisted - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.staticCompositionLocalOf -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.compose.viewModel - -@Composable -inline fun assistedViewModel( - vararg dependsOn: Any, - noinline creator: ((AF) -> VM)? = null, -): VM { - val factories = LocalAssistedFactories.current - val factory = factories.firstOrNull { AF::class.java.isInstance(it) } as? AF - return viewModel( - key = if (dependsOn.any()) { - dependsOn.joinToString { it.hashCode().toString() } + VM::class.java.canonicalName - } else { - null - }, - creator = { - factory?.let { creator?.invoke(it) } as VM - } - ) -} - -@Composable -fun ProvideAssistedFactory( - factory: List, - content: @Composable () -> Unit, -) { - CompositionLocalProvider( - LocalAssistedFactories provides factory - ) { - content.invoke() - } -} - -val LocalAssistedFactories = staticCompositionLocalOf> { emptyList() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index 256da7abd..437e57daa 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -41,12 +41,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController @@ -78,9 +77,7 @@ fun DraftListScene() { fun DraftListSceneContent( lazyListController: LazyListController? = null, ) { - val viewModel = assistedViewModel { - it.create() - } + val viewModel: DraftViewModel = getViewModel() val source by viewModel.source.observeAsState(initial = emptyList()) val navController = LocalNavController.current val listState = rememberLazyListState() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index 3501a9422..b130b5c79 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.scenes import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.updateTransition import androidx.compose.animation.expandVertically @@ -94,6 +95,7 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences +import com.twidere.twiderex.scenes.home.item import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalNavController diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 7fb43133d..a49a47cea 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -91,7 +91,7 @@ import com.twidere.twiderex.component.status.StatusText import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.hideControls import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.setOnSystemBarsVisibilityChangeListener @@ -101,7 +101,6 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.ui.LocalWindow @@ -110,12 +109,12 @@ import com.twidere.twiderex.viewmodel.MediaViewModel import moe.tlaster.swiper.Swiper import moe.tlaster.swiper.SwiperState import moe.tlaster.swiper.rememberSwiperState +import org.koin.core.parameter.parametersOf @Composable fun StatusMediaScene(statusKey: MicroBlogKey, selectedIndex: Int) { - val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel { - it.create(account, statusKey) + val viewModel = getViewModel { + parametersOf(statusKey) } val status by viewModel.status.observeAsState(null) val loading by viewModel.loading.observeAsState(initial = false) @@ -296,7 +295,6 @@ private fun StatusMediaInfo( viewModel: MediaViewModel, currentMedia: UiMedia ) { - val context = LocalContext.current Column( modifier = Modifier .padding(StatusMediaInfoDefaults.ContentPadding), @@ -327,7 +325,7 @@ private fun StatusMediaInfo( contract = ActivityResultContracts.CreateDocument() ) { it?.let { - viewModel.saveFile(currentMedia, it) + viewModel.saveFile(currentMedia, it.toString()) } } ShareButton(status = status) { callback -> diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index a94e1facc..b06aa705e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -61,7 +61,7 @@ import com.google.accompanist.pager.rememberPagerState import com.google.android.exoplayer2.ui.PlayerControlView import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.InAppNotificationScaffold -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.hideControls import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.setOnSystemBarsVisibilityChangeListener @@ -76,12 +76,13 @@ import com.twidere.twiderex.ui.TwidereDialog import com.twidere.twiderex.viewmodel.PureMediaViewModel import moe.tlaster.swiper.SwiperState import moe.tlaster.swiper.rememberSwiperState +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalPagerApi::class) @Composable fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { - val viewModel = assistedViewModel { - it.create(belongToKey) + val viewModel = getViewModel { + parametersOf(belongToKey) } val source by viewModel.source.observeAsState(null) TwidereDialog( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index eb14e35c9..7dcabbcc1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.BuildConfig +import com.twidere.twiderex.DefaultConfig import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold @@ -140,8 +140,8 @@ private fun TwitterSignIn() { scope.launch { navController.navigateForResult( RootRoute.SignIn.Twitter( - BuildConfig.CONSUMERKEY, - BuildConfig.CONSUMERSECRET, + DefaultConfig.ConsumerKey, + DefaultConfig.ConsumerSecret, ) )?.let { it as Boolean diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt index 225dd50f3..bc9a76ec0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt @@ -50,7 +50,6 @@ import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemsIndexed -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.ErrorPlaceholder @@ -59,25 +58,21 @@ import com.twidere.twiderex.component.status.DetailedStatusComponent import com.twidere.twiderex.component.status.StatusDivider import com.twidere.twiderex.component.status.StatusThreadStyle import com.twidere.twiderex.component.status.TimelineStatusComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.utils.generateNotificationEvent import com.twidere.twiderex.viewmodel.StatusViewModel +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalMaterialApi::class) @Composable fun StatusScene( statusKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - statusKey, - account, - ) { - it.create(account = account, statusKey = statusKey) + val viewModel = getViewModel { + parametersOf(statusKey) } val source = viewModel.source.collectAsLazyPagingItems() val status by viewModel.status.observeAsState(initial = null) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index 9da8afad5..fffe67836 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.scenes.compose import android.Manifest import android.annotation.SuppressLint import android.location.Location -import android.net.Uri import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -118,7 +117,7 @@ import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserAvatarDefaults import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.icon import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.stringName @@ -146,6 +145,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import org.koin.core.parameter.parametersOf import kotlin.math.max @Composable @@ -153,19 +153,17 @@ fun DraftComposeScene( draftId: String, ) { val account = LocalActiveAccount.current ?: return - val draftItemViewModel = - assistedViewModel { - it.create(draftId = draftId) - } + val draftItemViewModel: DraftItemViewModel = getViewModel { + parametersOf(draftId) + } val data by draftItemViewModel.draft.observeAsState(null) data?.let { draft -> - val viewModel = - assistedViewModel { - it.create( - account = account, - draft = draft, - ) - } + val viewModel: DraftComposeViewModel = getViewModel { + parametersOf( + account, + draft + ) + } ComposeBody(viewModel = viewModel, account = account) } } @@ -176,8 +174,8 @@ fun ComposeScene( composeType: ComposeType = ComposeType.New, ) { val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel { - it.create(account, statusKey, composeType) + val viewModel: ComposeViewModel = getViewModel { + parametersOf(statusKey, composeType) } ComposeBody(viewModel = viewModel, account = account) } @@ -445,7 +443,7 @@ private fun ComposeBody( @Composable private fun ComposeImageList( - images: List, + images: List, viewModel: ComposeViewModel ) { Spacer(modifier = Modifier.height(ComposeImageListDefaults.Spacing)) @@ -575,7 +573,7 @@ object EmojiListDefaults { @Composable private fun MastodonExtraActions( - images: List, + images: List, viewModel: ComposeViewModel ) { if (images.any()) { @@ -619,7 +617,7 @@ private fun MastodonExtraActions( } @Composable -private fun LocationDisplay(it: Location) { +private fun LocationDisplay(it: com.twidere.twiderex.model.kmp.Location) { CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.medium ) { @@ -1144,7 +1142,7 @@ private fun ComposeActions( val filePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetMultipleContents(), onResult = { - viewModel.putImages(it) + viewModel.putImages(it.map { it.toString() }) }, ) val permissionLauncher = rememberLauncherForActivityResult( @@ -1332,7 +1330,7 @@ private object ComposeActionsDefaults { } @Composable -private fun ComposeImage(item: Uri, viewModel: ComposeViewModel) { +private fun ComposeImage(item: String, viewModel: ComposeViewModel) { var expanded by remember { mutableStateOf(false) } Box { Box( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index 2fb022df6..2fa9dce31 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -41,15 +41,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.extensions.viewModel -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.compose.MastodonComposeSearchHashtagViewModel @@ -57,12 +55,8 @@ import com.twidere.twiderex.viewmodel.compose.MastodonComposeSearchHashtagViewMo @OptIn(ExperimentalMaterialApi::class) @Composable fun ComposeSearchHashtagScene() { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val viewModel = viewModel(account) { - MastodonComposeSearchHashtagViewModel(account = account) - } - + val viewModel: MastodonComposeSearchHashtagViewModel = getViewModel() val text by viewModel.text.observeAsState(initial = "") val source = viewModel.source.collectAsLazyPagingItems() TwidereScene { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index 732dfe796..07fe7b00b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -37,15 +37,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.lazy.ui.LazyUiUserList +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.extensions.viewModel import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -56,9 +55,7 @@ import com.twidere.twiderex.viewmodel.compose.ComposeSearchUserViewModel fun ComposeSearchUserScene() { val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val viewModel = viewModel(account) { - ComposeSearchUserViewModel(account = account) - } + val viewModel: ComposeSearchUserViewModel = getViewModel() val text by viewModel.text.observeAsState(initial = "") val source = viewModel.source.collectAsLazyPagingItems() TwidereScene { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index 52ba14d85..c87facabf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.lazy.ui.LazyUiDMConversationList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -91,12 +91,7 @@ fun DMConversationListSceneContent( val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current if (!account.supportDirectMessage) return - val viewModel = - assistedViewModel( - account, - ) { - it.create(account) - } + val viewModel: DMConversationViewModel = getViewModel() val source = viewModel.source.collectAsLazyPagingItems() val listState = rememberLazyListState() LaunchedEffect(lazyListController) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index eabd41ebf..825826195 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -76,7 +76,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiDMEventList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent @@ -85,15 +85,14 @@ import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMEventViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalAnimatedInsets ::class) @Composable fun DMConversationScene(conversationKey: MicroBlogKey) { val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account, conversationKey - ) { - it.create(account, conversationKey) + val viewModel: DMEventViewModel = getViewModel { + parametersOf(conversationKey) } val conversation by viewModel.conversation.observeAsState(null) TwidereScene { @@ -127,7 +126,7 @@ fun NormalContent(viewModel: DMEventViewModel) { val filePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument(), onResult = { - viewModel.inputImage.value = it + viewModel.inputImage.value = it.toString() }, ) val copyText = stringResource(id = com.twidere.common.R.string.scene_messages_action_copy_text) @@ -235,7 +234,7 @@ private object MessageActionComponentDefaults { } @Composable -fun InputPhotoPreview(inputImage: Uri?, onRemove: () -> Unit) { +fun InputPhotoPreview(inputImage: String?, onRemove: () -> Unit) { if (inputImage == null) return Box(modifier = Modifier.padding(InputPhotoPreviewDefaults.ContentPadding)) { Box( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index ed26c0322..17fd1a2c1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -34,7 +34,6 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -49,11 +48,10 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiUserList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMNewConversationViewModel @@ -62,16 +60,10 @@ import moe.tlaster.precompose.navigation.PopUpTo @Composable fun DMNewConversationScene() { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val viewModel = assistedViewModel( - account - ) { - it.create(account) - } + val viewModel: DMNewConversationViewModel = getViewModel() val keyWord by viewModel.input.observeAsState("") - val sourceState by viewModel.sourceFlow.collectAsState(initial = null) - val searchSource = sourceState?.collectAsLazyPagingItems() + val source = viewModel.sourceFlow.collectAsLazyPagingItems() TwidereScene { InAppNotificationScaffold( topBar = { @@ -96,31 +88,36 @@ fun DMNewConversationScene() { } }, ) { - searchSource?.let { source -> - SearchResult( - source, - onItemClick = { user -> - viewModel.createNewConversation( - user, - onResult = { key -> - key?.let { - navController.navigate( - RootRoute.Messages.Conversation(it), - NavOptions(popUpTo = PopUpTo(RootRoute.Messages.Home)) - ) - } + SearchResult( + source, + onItemClick = { user -> + viewModel.createNewConversation( + user, + onResult = { key -> + key?.let { + navController.navigate( + RootRoute.Messages.Conversation(it), + NavOptions(popUpTo = PopUpTo(RootRoute.Messages.Home)) + ) } - ) - } - ) - } + } + ) + } + ) } } } @Composable -fun SearchInput(modifier: Modifier = Modifier, input: String, onValueChanged: (value: String) -> Unit) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(SearchInputDefaults.ContentPadding)) { +fun SearchInput( + modifier: Modifier = Modifier, + input: String, + onValueChanged: (value: String) -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.padding(SearchInputDefaults.ContentPadding) + ) { Icon( painter = painterResource(id = R.drawable.ic_search), contentDescription = stringResource( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt index 31808d578..c5234e08b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt @@ -32,7 +32,8 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel @@ -85,13 +86,7 @@ fun AllNotificationScene() { fun AllNotificationSceneContent( lazyListController: LazyListController? = null, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account - ) { - it.create(account = account) - } + val viewModel: NotificationTimelineViewModel = getViewModel() TimelineComponent( viewModel = viewModel, lazyListController = lazyListController, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt index 39b74d335..fb915d125 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.dm.DMConversationListSceneContent import com.twidere.twiderex.scenes.dm.DMConversationListSceneFab diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt index 58200bf5f..39a8d1812 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.DraftListSceneContent diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt new file mode 100644 index 000000000..8ef7eb574 --- /dev/null +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt @@ -0,0 +1,40 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.scenes.home + +import com.twidere.twiderex.model.HomeMenus +import com.twidere.twiderex.scenes.home.mastodon.FederatedTimelineItem +import com.twidere.twiderex.scenes.home.mastodon.LocalTimelineItem +import com.twidere.twiderex.scenes.home.mastodon.MastodonNotificationItem + +val HomeMenus.item + get() = when (this) { + HomeMenus.HomeTimeline -> HomeTimelineItem() + HomeMenus.MastodonNotification -> MastodonNotificationItem() + HomeMenus.Mention -> MentionItem() + HomeMenus.Search -> SearchItem() + HomeMenus.Me -> MeItem() + HomeMenus.Message -> DMConversationListItem() + HomeMenus.LocalTimeline -> LocalTimelineItem() + HomeMenus.FederatedTimeline -> FederatedTimelineItem() + HomeMenus.Draft -> DraftNavigationItem() + HomeMenus.Lists -> ListsNavigationItem() + } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 3e585185f..30643c113 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -36,10 +36,10 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel @@ -113,12 +113,7 @@ private fun HomeTimelineFab() { fun HomeTimelineSceneContent( lazyListController: LazyListController? = null ) { - val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account - ) { - it.create(account) - } + val viewModel: HomeTimelineViewModel = getViewModel() TimelineComponent( viewModel = viewModel, lazyListController = lazyListController, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt index 50f2c4410..e9f3ca4a9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.twidere.twiderex.R +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.lists.ListsSceneContent import com.twidere.twiderex.scenes.lists.ListsSceneFab diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index fd70457ff..d5398e2ad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -30,6 +30,7 @@ import com.twidere.twiderex.component.UserComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index a528862c3..8baafece8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -31,9 +31,9 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.MentionsTimelineViewModel @@ -48,13 +48,7 @@ class MentionItem : HomeNavigationItem() { @Composable override fun Content() { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account - ) { - it.create(account) - } + val viewModel: MentionsTimelineViewModel = getViewModel() TimelineComponent( viewModel = viewModel, lazyListController = lazyListController, @@ -86,13 +80,7 @@ fun MentionScene() { fun MentionSceneContent( lazyListController: LazyListController? = null ) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account - ) { - it.create(account) - } + val viewModel: MentionsTimelineViewModel = getViewModel() TimelineComponent( viewModel = viewModel, lazyListController = lazyListController, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index a3120c343..4e05a3f18 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -32,7 +32,8 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene @@ -83,12 +84,7 @@ fun NotificationContent( if (account.service !is NotificationService) { return } - val viewModel = - assistedViewModel( - account - ) { - it.create(account) - } + val viewModel: NotificationTimelineViewModel = getViewModel() TimelineComponent( viewModel = viewModel, lazyListController = lazyListController, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index a57ddce68..1f44c6561 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -55,8 +55,9 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.trend.MastodonTrendItem import com.twidere.twiderex.component.trend.TwitterTrendItem -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount @@ -107,17 +108,8 @@ fun SearchScene() { @Composable fun SearchSceneContent() { val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account - ) { - it.create(account = account) - } - val trendViewModel = assistedViewModel( - account - ) { - it.create(account = account) - } + val viewModel: SearchInputViewModel = getViewModel() + val trendViewModel: TrendViewModel = getViewModel() val source by viewModel.savedSource.observeAsState(initial = emptyList()) val trends = trendViewModel.source.collectAsLazyPagingItems() val navigator = LocalNavigator.current diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index ea3b185ae..f47a21dd7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -32,9 +32,9 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.scenes.home.HomeNavigationItem import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.mastodon.FederatedTimelineViewModel @@ -89,11 +89,6 @@ fun FederatedTimelineContent( if (account.service !is MastodonService) { return } - val viewModel = - assistedViewModel( - account - ) { - it.create(account = account) - } + val viewModel: FederatedTimelineViewModel = getViewModel() TimelineComponent(viewModel = viewModel, lazyListController = lazyListController) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index 1ce101ac0..52e0e8840 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -32,9 +32,9 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.scenes.home.HomeNavigationItem import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.mastodon.LocalTimelineViewModel @@ -89,11 +89,6 @@ fun LocalTimelineContent( if (account.service !is MastodonService) { return } - val viewModel = - assistedViewModel( - account - ) { - it.create(account = account) - } + val viewModel: LocalTimelineViewModel = getViewModel() TimelineComponent(viewModel = viewModel, lazyListController = lazyListController) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 044684ebf..2141ad5dc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -38,9 +38,9 @@ import com.twidere.twiderex.component.foundation.Pager import com.twidere.twiderex.component.foundation.TextTabsComponent import com.twidere.twiderex.component.foundation.rememberPagerState import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.home.AllNotificationItem -import com.twidere.twiderex.scenes.home.HomeNavigationItem import com.twidere.twiderex.scenes.home.MentionItem import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index 33012e7b8..4fb8bc161 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -56,10 +56,9 @@ import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.extensions.viewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser @@ -68,16 +67,15 @@ import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsAddMemberViewModel import com.twidere.twiderex.viewmodel.lists.ListsSearchUserViewModel +import org.koin.core.parameter.parametersOf @Composable fun ListsAddMembersScene( listKey: MicroBlogKey, ) { val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account, listKey.id - ) { - it.create(account, listKey.id) + val viewModel: ListsAddMemberViewModel = getViewModel { + parametersOf(listKey.id) } val loading by viewModel.loading.observeAsState(initial = false) @@ -87,10 +85,8 @@ fun ListsAddMembersScene( else -> false } - val searchViewModel = viewModel( - account, - ) { - ListsSearchUserViewModel(account, onlySearchFollowing) + val searchViewModel: ListsSearchUserViewModel = getViewModel { + parametersOf(onlySearchFollowing) } val keyword by searchViewModel.text.observeAsState(initial = "") diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index 62fadc979..97ff5224a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -53,15 +53,15 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsUserViewModel import kotlinx.coroutines.launch +import org.koin.core.parameter.parametersOf import java.util.Locale @Composable @@ -69,12 +69,9 @@ fun ListsMembersScene( listKey: MicroBlogKey, owned: Boolean, ) { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val viewModel = assistedViewModel( - account - ) { - it.create(account, listKey.id) + val viewModel: ListsUserViewModel = getViewModel { + parametersOf(listKey.id) } val source = viewModel.source.collectAsLazyPagingItems() val navigator = LocalNavigator.current @@ -95,7 +92,8 @@ fun ListsMembersScene( if (owned) FloatingActionButton( onClick = { scope.launch { - val result = navController.navigateForResult(RootRoute.Lists.AddMembers(listKey = listKey)) as? List<*>? + val result = + navController.navigateForResult(RootRoute.Lists.AddMembers(listKey = listKey)) as? List<*>? if (result != null && result.isNotEmpty()) source.refresh() } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index 99396c72b..76bd4c5fc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -42,7 +42,7 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiListsList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount @@ -114,11 +114,7 @@ fun ListsSceneContent() { val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current // if list type is all , display title of each type - val listsViewMode = assistedViewModel( - account, - ) { - it.create(account) - } + val listsViewMode: ListsViewModel = getViewModel() val ownerItems = listsViewMode.ownerSource.collectAsLazyPagingItems() val subscribeItems = listsViewMode.subscribedSource.collectAsLazyPagingItems() val sourceItems = listsViewMode.source.collectAsLazyPagingItems() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt index eaa01fde4..86739ec3d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt @@ -23,26 +23,22 @@ package com.twidere.twiderex.scenes.lists import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsUserViewModel +import org.koin.core.parameter.parametersOf @Composable fun ListsSubscribersScene( listKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account - ) { - it.create(account, listKey.id, viewMembers = false) + val viewModel: ListsUserViewModel = getViewModel { + parametersOf(listKey.id, false) } TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index 656f30466..5670f2040 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState +import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -58,9 +59,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootRoute @@ -70,6 +70,7 @@ import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsModifyViewModel import com.twidere.twiderex.viewmodel.lists.ListsTimelineViewModel +import org.koin.core.parameter.parametersOf @Composable fun ListTimeLineScene( @@ -77,10 +78,8 @@ fun ListTimeLineScene( ) { val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val viewModel = assistedViewModel( - account, listKey - ) { - it.create(account, listKey) + val viewModel: ListsModifyViewModel = getViewModel { + parametersOf(listKey) } val source by viewModel.source.observeAsState(initial = null) val loading by viewModel.loading.observeAsState(initial = false) @@ -100,7 +99,8 @@ fun ListTimeLineScene( title = { Row(verticalAlignment = Alignment.CenterVertically) { Text( - text = source?.title ?: stringResource(id = com.twidere.common.R.string.scene_lists_details_title), + text = source?.title + ?: stringResource(id = com.twidere.common.R.string.scene_lists_details_title), maxLines = 1, overflow = TextOverflow.Ellipsis ) @@ -189,7 +189,9 @@ fun ListTimeLineScene( onClick = { menuExpand = false when (account.type) { - PlatformType.Twitter -> navController.navigate(RootRoute.Lists.TwitterEdit(listKey = listKey)) + PlatformType.Twitter -> navController.navigate( + RootRoute.Lists.TwitterEdit(listKey = listKey) + ) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> showEditDialog = true @@ -222,7 +224,7 @@ fun ListTimeLineScene( }, ) { Box { - ListTimelineComponent(account, listKey) + ListTimelineComponent(listKey) if (showEditDialog) { MastodonListsEditDialog(listKey) { showEditDialog = false @@ -253,11 +255,9 @@ fun ListTimeLineScene( } @Composable -private fun ListTimelineComponent(account: AccountDetails, listKey: MicroBlogKey) { - val viewModel = assistedViewModel( - account, listKey - ) { - it.create(account, listKey) +private fun ListTimelineComponent(listKey: MicroBlogKey) { + val viewModel: ListsTimelineViewModel = getViewModel { + parametersOf(listKey) } val timelineSource = viewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @@ -274,14 +274,21 @@ private fun ListTimelineComponent(account: AccountDetails, listKey: MicroBlogKey } @Composable -private fun ListDeleteConfirmDialog(title: String, onDismissRequest: () -> Unit, onConfirm: () -> Unit) { +private fun ListDeleteConfirmDialog( + title: String, + onDismissRequest: () -> Unit, + onConfirm: () -> Unit +) { AlertDialog( onDismissRequest = { onDismissRequest.invoke() }, title = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_details_delete_list_title, title), + text = stringResource( + id = com.twidere.common.R.string.scene_lists_details_delete_list_title, + title + ), style = MaterialTheme.typography.subtitle1 ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index 9cab38831..0db5620a8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -30,16 +30,16 @@ import androidx.compose.ui.window.Dialog import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel +import org.koin.core.parameter.parametersOf @Composable fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current var showMastodonComponent by remember { mutableStateOf(true) @@ -51,19 +51,19 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { var name by remember { mutableStateOf("") } - val listsCreateViewModel = assistedViewModel( - account - ) { - it.create(account) { success, list -> - dismiss() - if (success) { - list?.apply { - navController.navigate( - RootRoute.Lists.Timeline(listKey), - ) + val listsCreateViewModel: ListsCreateViewModel = getViewModel { + parametersOf( + { success: Boolean, list: UiList? -> + dismiss() + if (success) { + list?.apply { + navController.navigate( + RootRoute.Lists.Timeline(listKey), + ) + } } } - } + ) } val loading by listsCreateViewModel.loading.observeAsState(initial = false) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt index 6bdb9574d..6ba3e8737 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt @@ -27,18 +27,16 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.Dialog -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.viewmodel.lists.ListsModifyViewModel +import org.koin.core.parameter.parametersOf @Composable fun MastodonListsEditDialog(listKey: MicroBlogKey, onDismissRequest: () -> Unit) { - val account = LocalActiveAccount.current ?: return var showMastodonComponent by remember { mutableStateOf(true) } @@ -46,10 +44,8 @@ fun MastodonListsEditDialog(listKey: MicroBlogKey, onDismissRequest: () -> Unit) onDismissRequest.invoke() showMastodonComponent = true } - val listsEditViewModel = assistedViewModel( - account - ) { - it.create(account, listKey) + val listsEditViewModel: ListsModifyViewModel = getViewModel { + parametersOf(listKey) } val source by listsEditViewModel.source.observeAsState(initial = null) val loading by listsEditViewModel.loading.observeAsState(initial = false) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index a34435421..54832ea3c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -43,33 +43,33 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.TwitterListsModifyComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.navigation.RootRoute -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.PopUpTo +import org.koin.core.parameter.parametersOf @Composable fun TwitterListsCreateScene() { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val listsCreateViewModel = assistedViewModel( - account - ) { - it.create(account) { success, list -> - if (success) list?.apply { - navController.navigate( - RootRoute.Lists.Timeline(listKey), - options = NavOptions( - popUpTo = PopUpTo(RootRoute.Lists.Home) + val listsCreateViewModel: ListsCreateViewModel = getViewModel { + parametersOf( + { success: Boolean, list: UiList? -> + if (success) list?.apply { + navController.navigate( + RootRoute.Lists.Timeline(listKey), + options = NavOptions( + popUpTo = PopUpTo(RootRoute.Lists.Home) + ) ) - ) + } } - } + ) } val loading by listsCreateViewModel.loading.observeAsState(initial = false) @@ -104,7 +104,9 @@ fun TwitterListsCreateScene() { Icon( imageVector = Icons.Default.Done, contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), - tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy( + alpha = LocalContentAlpha.current + ) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt index 1f0c67a41..14e97e28c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt @@ -34,30 +34,26 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.Dialog -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.TwitterListsModifyComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsModifyViewModel +import org.koin.core.parameter.parametersOf @Composable fun TwitterListsEditScene( listKey: MicroBlogKey ) { - val account = LocalActiveAccount.current ?: return val navController = LocalNavController.current - val listsEditViewModel = assistedViewModel( - account, listKey - ) { - it.create(account, listKey) + val listsEditViewModel: ListsModifyViewModel = getViewModel { + parametersOf(listKey) } val loading by listsEditViewModel.loading.observeAsState(initial = false) val source by listsEditViewModel.source.observeAsState(null) @@ -90,7 +86,9 @@ fun TwitterListsEditScene( Icon( imageVector = Icons.Default.Done, contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), - tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy( + alpha = LocalContentAlpha.current + ) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt index 3fa436af5..ed4a47168 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt @@ -29,22 +29,17 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.mastodon.MastodonHashtagViewModel +import org.koin.core.parameter.parametersOf @Composable fun MastodonHashtagScene(keyword: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - keyword, - account, - ) { - it.create(keyword = keyword, account = account) - } + val viewModel: MastodonHashtagViewModel = getViewModel { + parametersOf(keyword) + } val source = viewModel.source.collectAsLazyPagingItems() TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt index f65c73af3..edb08e7c9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt @@ -54,7 +54,7 @@ import androidx.compose.ui.unit.dp import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.utils.CustomTabSignInChannel @@ -64,16 +64,13 @@ import moe.tlaster.precompose.navigation.NavController @OptIn(ExperimentalMaterialApi::class) @Composable fun MastodonSignInScene() { - val viewModel = - assistedViewModel { - it.create() - } + val viewModel: MastodonSignInViewModel = getViewModel() val host by viewModel.host.observeAsState(initial = TextFieldValue()) val loading by viewModel.loading.observeAsState(initial = false) val navController = LocalNavController.current val context = LocalContext.current SignInScaffold { - if (loading == true) { + if (loading) { CircularProgressIndicator() } else { val focusRequester = remember { FocusRequester() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index 41d3feaaf..8b9a7e92d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -54,9 +54,8 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.search.SearchInputViewModel @@ -76,13 +75,7 @@ val fadeResumeTransition: GraphicsLayerScope.(factor: Float) -> Unit = { factor @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class) @Composable fun SearchInputScene(initial: String? = null) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account - ) { - it.create(account = account) - } + val viewModel: SearchInputViewModel = getViewModel() val source by viewModel.source.observeAsState(initial = emptyList()) val initialText = initial ?: "" var textFieldValue by remember { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt index eb3bb1295..8f23595e7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt @@ -59,7 +59,7 @@ import com.twidere.twiderex.component.foundation.AppBarDefaults import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.enums.PlatformType @@ -71,6 +71,7 @@ import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.search.SearchSaveViewModel import kotlinx.coroutines.launch +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalFoundationApi::class, ExperimentalPagerApi::class) @Composable @@ -78,12 +79,9 @@ fun SearchScene(keyword: String) { val account = LocalActiveAccount.current ?: return val navigator = LocalNavigator.current - val viewModel = - assistedViewModel( - account, keyword - ) { - it.create(account, keyword) - } + val viewModel: SearchSaveViewModel = getViewModel { + parametersOf(keyword) + } val tabs = remember { when (account.type) { @@ -135,7 +133,8 @@ fun SearchScene(keyword: String) { ) if (loading) { CircularProgressIndicator( - modifier = Modifier.size(SearchSceneDefaults.Loading.size) + modifier = Modifier + .size(SearchSceneDefaults.Loading.size) .padding(SearchSceneDefaults.Loading.padding), strokeWidth = SearchSceneDefaults.Loading.width, color = MaterialTheme.colors.onSurface.copy(0.08f) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt index 83ca6dbd9..4488af6a2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt @@ -31,13 +31,12 @@ import androidx.compose.ui.res.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.extensions.viewModel -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.viewmodel.mastodon.MastodonSearchHashtagViewModel +import org.koin.core.parameter.parametersOf class MastodonSearchHashtagItem : SearchSceneItem { @Composable @@ -48,12 +47,8 @@ class MastodonSearchHashtagItem : SearchSceneItem { @OptIn(ExperimentalMaterialApi::class) @Composable override fun Content(keyword: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = viewModel( - keyword, - account, - ) { - MastodonSearchHashtagViewModel(account, keyword) + val viewModel: MastodonSearchHashtagViewModel = getViewModel { + parametersOf(keyword) } val source = viewModel.source.collectAsLazyPagingItems() val navigator = LocalNavigator.current diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt index 657f82a66..81f7c4784 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt @@ -24,13 +24,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.viewmodel.search.SearchTweetsViewModel +import org.koin.core.parameter.parametersOf class SearchTweetsItem : SearchSceneItem { @Composable @@ -40,11 +39,9 @@ class SearchTweetsItem : SearchSceneItem { @Composable override fun Content(keyword: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel { - it.create(account, keyword) - } + val viewModel: SearchTweetsViewModel = getViewModel { + parametersOf(keyword) + } val source = viewModel.source.collectAsLazyPagingItems() SwipeToRefreshLayout( refreshingState = source.loadState.refresh is LoadState.Loading, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt index 42c16c1e5..9641847a2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt @@ -24,14 +24,13 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.extensions.viewModel -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.viewmodel.search.SearchUserViewModel +import org.koin.core.parameter.parametersOf class SearchUserItem : SearchSceneItem { @Composable @@ -41,12 +40,8 @@ class SearchUserItem : SearchSceneItem { @Composable override fun Content(keyword: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = viewModel( - account, - keyword, - ) { - SearchUserViewModel(account, keyword) + val viewModel: SearchUserViewModel = getViewModel { + parametersOf(keyword) } val source = viewModel.source.collectAsLazyPagingItems() val navigator = LocalNavigator.current diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index 72d98566d..b68f8757a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -25,15 +25,14 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.res.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusImageList -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.viewmodel.twitter.search.TwitterSearchMediaViewModel +import org.koin.core.parameter.parametersOf class TwitterSearchMediaItem : SearchSceneItem { @Composable @@ -43,11 +42,9 @@ class TwitterSearchMediaItem : SearchSceneItem { @Composable override fun Content(keyword: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel { - it.create(account, keyword) - } + val viewModel: TwitterSearchMediaViewModel = getViewModel { + parametersOf(keyword) + } val source = viewModel.source.collectAsLazyPagingItems() CompositionLocalProvider( LocalVideoPlayback provides DisplayPreferences.AutoPlayback.Off diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt index e6c369f60..b9acbc4fb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt @@ -46,25 +46,23 @@ import com.twidere.twiderex.component.foundation.ColoredSwitch import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.notification.notificationChannelId import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.AccountNotificationViewModel +import org.koin.core.parameter.parametersOf @OptIn(ExperimentalMaterialApi::class) @Composable fun AccountNotificationScene( accountKey: MicroBlogKey, ) { - val viewModel = - assistedViewModel( - accountKey - ) { - it.create(accountKey) - } + val viewModel: AccountNotificationViewModel = getViewModel { + parametersOf(accountKey) + } val account by viewModel.account.observeAsState(initial = null) val enabled by viewModel.isNotificationEnabled.observeAsState(initial = true) TwidereScene { @@ -89,10 +87,10 @@ fun AccountNotificationScene( viewModel.setIsNotificationEnabled(!enabled) }, text = { - UserName(user = it) + UserName(user = it.toUi()) }, secondaryText = { - UserScreenName(user = it) + UserScreenName(user = it.toUi()) }, trailing = { ColoredSwitch( @@ -106,7 +104,7 @@ fun AccountNotificationScene( } val context = LocalContext.current NotificationChannelSpec.values().filter { it.grouped } - .sortedBy { stringResource(id = it.nameRes) } + .sortedBy { stringResource(id = it.nameRes.resourceId) } .forEach { ListItem( modifier = Modifier.clickable( @@ -135,18 +133,20 @@ fun AccountNotificationScene( emptyArray() } ) { - Text(text = stringResource(id = it.nameRes)) + Text(text = stringResource(id = it.nameRes.resourceId)) } }, - secondaryText = { - CompositionLocalProvider( - *if (!enabled) { - arrayOf(LocalContentAlpha provides ContentAlpha.disabled) - } else { - emptyArray() + secondaryText = it.descriptionRes?.let { + { + CompositionLocalProvider( + *if (!enabled) { + arrayOf(LocalContentAlpha provides ContentAlpha.disabled) + } else { + emptyArray() + } + ) { + Text(text = stringResource(id = it.resourceId)) } - ) { - Text(text = stringResource(id = it.descriptionRes)) } } ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index ac8ae9684..aec98f333 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -55,7 +55,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -64,7 +63,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.component.status.UserAvatarDefaults -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.isDarkTheme import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences @@ -77,9 +76,7 @@ import com.twidere.twiderex.viewmodel.settings.AppearanceViewModel fun AppearanceScene() { var showPrimaryColorDialog by remember { mutableStateOf(false) } val appearance = LocalAppearancePreferences.current - val viewModel = assistedViewModel { - it.create() - } + val viewModel: AppearanceViewModel = getViewModel() TwidereScene { InAppNotificationScaffold( topBar = { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index faacc4480..0ddfc3235 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -40,7 +40,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.action.FakeStatusActions import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.component.foundation.AppBar @@ -53,7 +52,7 @@ import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.component.status.TimelineStatusComponent -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.preferences.model.DisplayPreferences @@ -63,9 +62,7 @@ import com.twidere.twiderex.viewmodel.settings.DisplayViewModel @OptIn(ExperimentalMaterialApi::class) @Composable fun DisplayScene() { - val viewModel = assistedViewModel { - it.create() - } + val viewModel: DisplayViewModel = getViewModel() val display = LocalDisplayPreferences.current TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index f1b1dcd1c..f31e5fe73 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -55,8 +55,10 @@ import com.twidere.twiderex.component.foundation.rememberReorderableColumnState import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel +import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.HomeMenus +import com.twidere.twiderex.scenes.home.item import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.LayoutViewModel @@ -65,13 +67,7 @@ import com.twidere.twiderex.viewmodel.settings.LayoutViewModel @Composable fun LayoutScene() { val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account - ) { - it.create( - account = account, - ) - } + val viewModel: LayoutViewModel = getViewModel() val user by viewModel.user.observeAsState(initial = null) val menuOrder by account.preferences.homeMenuOrder.collectAsState( initial = HomeMenus.values().map { it to it.showDefault } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 7508b3e8a..d7e740011 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -62,7 +62,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.preferences.model.MiscPreferences import com.twidere.twiderex.ui.TwidereScene @@ -70,9 +70,7 @@ import com.twidere.twiderex.viewmodel.settings.MiscViewModel @Composable fun MiscScene() { - val viewModel = assistedViewModel { - it.create() - } + val viewModel: MiscViewModel = getViewModel() TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index 8b39caaef..a0d442ccb 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -46,7 +46,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccountViewModel @@ -59,10 +59,7 @@ import com.twidere.twiderex.viewmodel.settings.NotificationViewModel fun NotificationScene() { val activeAccountViewModel = LocalActiveAccountViewModel.current val accounts by activeAccountViewModel.allAccounts.observeAsState(initial = emptyList()) - val viewModel = - assistedViewModel { - it.create() - } + val viewModel: NotificationViewModel = getViewModel() val notificationEnabled by viewModel.enabled.observeAsState(initial = true) TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 5de1bf4dc..147425d24 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -35,11 +35,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.StorageViewModel @@ -47,9 +46,7 @@ import com.twidere.twiderex.viewmodel.settings.StorageViewModel @OptIn(ExperimentalMaterialApi::class) @Composable fun StorageScene() { - val viewModel = assistedViewModel { - it.create() - } + val viewModel: StorageViewModel = getViewModel() val loading by viewModel.loading.observeAsState(initial = false) if (loading) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt index 574022f4d..a7bd0c33b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt @@ -28,11 +28,14 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext import com.twidere.twiderex.component.foundation.SignInScaffold import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.utils.CustomTabSignInChannel +import com.twidere.twiderex.viewmodel.twitter.OauthVerifierProvider +import com.twidere.twiderex.viewmodel.twitter.PinCodeProvider import com.twidere.twiderex.viewmodel.twitter.TwitterSignInViewModel +import org.koin.core.parameter.parametersOf @Composable fun TwitterSignInScene( @@ -42,25 +45,26 @@ fun TwitterSignInScene( val navController = LocalNavController.current val context = LocalContext.current val navigator = LocalNavigator.current - val viewModel = - assistedViewModel { - it.create( - consumerKey, - consumerSecret, - oauthVerifierProvider = { target -> - CustomTabsIntent.Builder() - .setShareState(CustomTabsIntent.SHARE_STATE_OFF) - .build().launchUrl(context, Uri.parse(target)) - CustomTabSignInChannel.waitOne().getQueryParameter("oauth_verifier") - }, - pinCodeProvider = { target -> - navigator.twitterSignInWeb(target) - }, - onResult = { success -> - navController.goBackWith(success) - } - ) - } + val oauthVerifierProvider: OauthVerifierProvider = { target -> + CustomTabsIntent.Builder() + .setShareState(CustomTabsIntent.SHARE_STATE_OFF) + .build().launchUrl(context, Uri.parse(target)) + CustomTabSignInChannel.waitOne().getQueryParameter("oauth_verifier") + } + val pinCodeProvider: PinCodeProvider = { target -> + navigator.twitterSignInWeb(target) + } + val viewModel: TwitterSignInViewModel = getViewModel { + parametersOf( + consumerKey, + consumerSecret, + oauthVerifierProvider, + pinCodeProvider, + { success: Boolean -> + navController.goBackWith(success) + } + ) + } val loading by viewModel.loading.observeAsState(initial = false) SignInScaffold { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt index 9edf4e18a..29e46bfe9 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt @@ -35,25 +35,20 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.twitter.user.TwitterUserViewModel import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.PopUpTo +import org.koin.core.parameter.parametersOf @Composable fun TwitterUserScene(screenName: String) { - val account = LocalActiveAccount.current ?: return - val viewModel = - assistedViewModel( - account, - screenName - ) { - it.create(account, screenName) - } + val viewModel: TwitterUserViewModel = getViewModel { + parametersOf(screenName) + } val user by viewModel.user.observeAsState(initial = null) val error by viewModel.error.observeAsState(initial = null) val navigator = LocalNavigator.current diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt index 173845dc4..44b1302cf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt @@ -23,27 +23,22 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold -import com.twidere.twiderex.extensions.viewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.user.FollowersViewModel +import org.koin.core.parameter.parametersOf @Composable fun FollowersScene( userKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = viewModel( - account, - userKey, - ) { - FollowersViewModel(account, userKey) + val viewModel: FollowersViewModel = getViewModel { + parametersOf(userKey) } TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt index 6dcaa83b9..e0bc41255 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt @@ -23,27 +23,22 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold -import com.twidere.twiderex.extensions.viewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.user.FollowingViewModel +import org.koin.core.parameter.parametersOf @Composable fun FollowingScene( userKey: MicroBlogKey, ) { - val account = LocalActiveAccount.current ?: return - val viewModel = viewModel( - account, - userKey, - ) { - FollowingViewModel(account, userKey) + val viewModel: FollowingViewModel = getViewModel { + parametersOf(userKey) } TwidereScene { InAppNotificationScaffold( diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index c84afa04d..62c281506 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -34,7 +34,7 @@ import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.status.UserName -import com.twidere.twiderex.di.assisted.assistedViewModel +import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.MicroBlogKey @@ -45,24 +45,17 @@ import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMNewConversationViewModel import com.twidere.twiderex.viewmodel.user.UserViewModel +import org.koin.core.parameter.parametersOf @Composable fun UserScene( userKey: MicroBlogKey, ) { val account = LocalActiveAccount.current ?: return - val viewModel = assistedViewModel( - account, - userKey, - ) { - it.create(account, userKey) - } - - val conversationViewModel = assistedViewModel( - account, - ) { - it.create(account) + val viewModel: UserViewModel = getViewModel { + parametersOf(userKey) } + val conversationViewModel: DMNewConversationViewModel = getViewModel() val user by viewModel.user.observeAsState(initial = null) val navController = LocalNavController.current TwidereScene { diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt deleted file mode 100644 index 38d484f7a..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/HttpErrorCodes.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils - -object HttpErrorCodes { - const val TooManyRequests = 429 -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt index a1de8bb7d..67b99472e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt @@ -24,9 +24,8 @@ import androidx.compose.runtime.staticCompositionLocalOf import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import javax.inject.Inject -class PlatformResolver @Inject constructor( +class PlatformResolver( private val database: CacheDatabase, ) { suspend fun resolveStatus(statusKey: MicroBlogKey, accountKey: MicroBlogKey): PlatformType? { diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7daf55518..d289f69db 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { implementation("org.jsoup:jsoup:1.13.1") implementation(projects.routeProcessor) ksp(projects.routeProcessor) - implementation("dev.icerock.moko:resources:${Versions.moko}") + api("dev.icerock.moko:resources:${Versions.moko}") } } val commonTest by getting { @@ -72,6 +72,7 @@ kotlin { implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") + implementation("androidx.startup:startup-runtime:${Versions.startup}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt index 7c68d129d..fef66e17f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -24,6 +24,7 @@ import android.app.Application import com.twidere.twiderex.di.setupModules import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger +import org.koin.androidx.workmanager.koin.workManagerFactory import org.koin.core.KoinExperimentalAPI import org.koin.core.context.startKoin @@ -34,7 +35,7 @@ abstract class TwidereApplication : Application() { startKoin { androidLogger() androidContext(this@TwidereApplication) - // workManagerFactory() + workManagerFactory() setupModules() } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt index 4af71a355..b97026daf 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.di +package com.twidere.twiderex.di.modules import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.action.DirectMessageAction diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt index ca8539bda..543af41fa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt @@ -25,6 +25,7 @@ import com.twidere.twiderex.kmp.FileResolver import com.twidere.twiderex.kmp.LocationProvider import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.kmp.ResLoader +import com.twidere.twiderex.notification.AppNotificationManager import org.koin.dsl.module actual val kmpModule = module { @@ -33,4 +34,5 @@ actual val kmpModule = module { single { LocationProvider(get()) } single { RemoteNavigator(get()) } single { ResLoader(get()) } + single { AppNotificationManager(get(), get()) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt similarity index 88% rename from android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt index 94e809e5c..5a7298b50 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt @@ -24,20 +24,18 @@ import android.content.Context import androidx.startup.Initializer import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.WorkManager -import com.twidere.twiderex.di.InitializerEntryPoint import com.twidere.twiderex.worker.dm.DirectMessageFetchWorker -import javax.inject.Inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject class DirectMessageInitializerHolder private const val DirectMessageWorkName = "twiderex_direct_message" -class DirectMessageInitializer : Initializer { - @Inject - lateinit var workManager: WorkManager +class DirectMessageInitializer : Initializer, KoinComponent { + private val workManager: WorkManager by inject() override fun create(context: Context): DirectMessageInitializerHolder { - InitializerEntryPoint.resolve(context).inject(this) workManager.enqueueUniquePeriodicWork( DirectMessageWorkName, ExistingPeriodicWorkPolicy.KEEP, diff --git a/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt index 2d5f38081..7d0fc4093 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt @@ -25,32 +25,29 @@ import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationChannelGroupCompat import androidx.core.app.NotificationManagerCompat import androidx.startup.Initializer -import com.twidere.twiderex.di.InitializerEntryPoint import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.notification.importance import com.twidere.twiderex.notification.notificationChannelGroupId import com.twidere.twiderex.notification.notificationChannelId import com.twidere.twiderex.repository.AccountRepository -import javax.inject.Inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject class NotificationChannelInitializerHolder -class NotificationChannelInitializer : Initializer { - @Inject - lateinit var repository: AccountRepository +class NotificationChannelInitializer : Initializer, KoinComponent { + private val repository: AccountRepository by inject() override fun create(context: Context): NotificationChannelInitializerHolder { - InitializerEntryPoint.resolve(context).inject(this) - val notificationManagerCompat = NotificationManagerCompat.from(context) val addedChannels = mutableListOf() for (spec in NotificationChannelSpec.values().filter { !it.grouped }) { val builder = NotificationChannelCompat.Builder(spec.id, spec.importance) - .setName(context.getString(spec.nameRes)) + .setName(context.getString(spec.nameRes.resourceId)) - if (spec.descriptionRes != 0) { - builder.setDescription(context.getString(spec.descriptionRes)) + if (spec.descriptionRes != null) { + builder.setDescription(context.getString(spec.descriptionRes.resourceId)) } builder.setShowBadge(spec.showBadge) val channel = builder.build() @@ -89,10 +86,10 @@ class NotificationChannelInitializer : Initializer { - @Inject - lateinit var workManager: WorkManager +class NotificationInitializer : Initializer, KoinComponent { + private val workManager: WorkManager by inject() override fun create(context: Context): NotificationInitializerHolder { - InitializerEntryPoint.resolve(context).inject(this) val request = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) .build() workManager.enqueueUniquePeriodicWork( diff --git a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt similarity index 78% rename from android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt index 2f5dfe567..c24134719 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt @@ -18,21 +18,21 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.http +package com.twidere.twiderex.initializer import android.content.Context import androidx.startup.Initializer -import com.twidere.twiderex.di.InitializerEntryPoint -import javax.inject.Inject +import com.twidere.twiderex.http.TwidereHttpConfigProvider +import com.twidere.twiderex.http.TwidereServiceFactory +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject class TwidereserviceInitializerHolder -class TwidereServiceInitializer : Initializer { - @Inject - lateinit var configProvider: TwidereHttpConfigProvider +class TwidereServiceInitializer : Initializer, KoinComponent { + private val configProvider: TwidereHttpConfigProvider by inject() override fun create(context: Context): TwidereserviceInitializerHolder { - InitializerEntryPoint.resolve(context).inject(this) TwidereServiceFactory.initiate(configProvider) return TwidereserviceInitializerHolder() } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt index 4ce5cd47d..921482cef 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.component.lazy import androidx.compose.foundation.lazy.LazyListState class LazyListController { - internal var listState: LazyListState? = null + var listState: LazyListState? = null suspend fun scrollToTop() { listState?.scrollToItem(0) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index b57e48727..57a850fd4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -28,7 +28,9 @@ import com.twidere.twiderex.di.modules.platformModule import com.twidere.twiderex.di.modules.preferencesModule import com.twidere.twiderex.di.modules.repositoryModule import com.twidere.twiderex.di.modules.viewModelModule +import com.twidere.twiderex.notification.InAppNotification import org.koin.core.KoinApplication +import org.koin.dsl.module fun KoinApplication.setupModules() { modules(preferencesModule) @@ -39,4 +41,7 @@ fun KoinApplication.setupModules() { modules(actionModule) modules(jobsModule) modules(kmpModule) + module { + single { InAppNotification() } + } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt index 476066737..312cb65fc 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.scenes.home +package com.twidere.twiderex.model import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.sizeIn diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index 8f1b4b894..8387efeeb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -20,13 +20,17 @@ */ package com.twidere.twiderex.notification +import com.twidere.twiderex.MR import com.twidere.twiderex.model.MicroBlogKey +import dev.icerock.moko.resources.StringResource import java.net.URLEncoder enum class NotificationChannelSpec( val id: String, val showBadge: Boolean = false, - val grouped: Boolean = false + val grouped: Boolean = false, + val nameRes: StringResource, + val descriptionRes: StringResource? = null, ) { /** * For notifications indicate that some lengthy operations are performing in the background. @@ -34,18 +38,23 @@ enum class NotificationChannelSpec( */ BackgroundProgresses( "background_progresses", + nameRes = MR.strings.common_notification_channel_background_progresses_name, ), ContentInteractions( "content_interactions", showBadge = true, - grouped = true + grouped = true, + nameRes = MR.strings.common_notification_channel_content_interactions_name, + descriptionRes = MR.strings.common_notification_channel_content_interactions_description, ), ContentMessages( "content_messages", showBadge = true, - grouped = true + grouped = true, + nameRes = MR.strings.common_notification_channel_content_messages_name, + descriptionRes = MR.strings.common_notification_channel_content_messages_description, ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 9555e7b5f..22ed7859f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -40,14 +40,18 @@ import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope +typealias OauthVerifierProvider = suspend (url: String) -> String? +typealias PinCodeProvider = suspend (url: String) -> String? +typealias OnResult = (success: Boolean) -> Unit + class TwitterSignInViewModel( private val repository: AccountRepository, private val inAppNotification: InAppNotification, private val consumerKey: String, private val consumerSecret: String, - private val oauthVerifierProvider: suspend (url: String) -> String?, - private val pinCodeProvider: suspend (url: String) -> String?, - private val onResult: (success: Boolean) -> Unit, + private val oauthVerifierProvider: OauthVerifierProvider, + private val pinCodeProvider: PinCodeProvider, + private val onResult: OnResult, ) : ViewModel() { val success = MutableStateFlow(false) From af05cb637d3d9381d67cba5754d78ee8be998dca Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 8 Sep 2021 18:42:37 +0800 Subject: [PATCH 144/615] fix buld --- android/src/main/AndroidManifest.xml | 3 ++- .../kotlin/com/twidere/twiderex/TwidereApp.kt | 2 ++ .../com/twidere/twiderex/TwidereXActivity.kt | 19 ++++++++++--------- .../di/modules/PlatformModule.android.kt | 9 ++++++++- .../twiderex/utils/PlatformResolver.kt | 0 .../kotlin/com/twidere/twiderex/di/Setup.kt | 5 ----- localization | 2 +- 7 files changed, 23 insertions(+), 17 deletions(-) rename {android/src/main => common/src/androidMain}/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt (100%) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 7c92be06a..e13cbc18d 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -33,7 +33,8 @@ tools:node="remove" /> + android:value="androidx.startup" + tools:node="remove" /> ().contentResolver } single { NotificationManagerCompat.from(get()) } single { WorkManager.getInstance(get()) } + single { TwidereHttpConfigProvider(get().miscPreferences) } + single { InAppNotification() } + single { PlatformResolver(get()) } workManager() } private fun Module.workManager() { worker { ShareMediaWorker(get(), get(), get()) } - worker { NotificationWorker(get(), get(), get(), get()) } + worker { NotificationWorker(get(), get(), get().notificationPreferences, get()) } worker { DownloadMediaWorker(get(), get(), get()) } worker { DeleteStatusWorker(get(), get(), get()) } worker { LikeWorker(get(), get(), get()) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index 57a850fd4..b57e48727 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -28,9 +28,7 @@ import com.twidere.twiderex.di.modules.platformModule import com.twidere.twiderex.di.modules.preferencesModule import com.twidere.twiderex.di.modules.repositoryModule import com.twidere.twiderex.di.modules.viewModelModule -import com.twidere.twiderex.notification.InAppNotification import org.koin.core.KoinApplication -import org.koin.dsl.module fun KoinApplication.setupModules() { modules(preferencesModule) @@ -41,7 +39,4 @@ fun KoinApplication.setupModules() { modules(actionModule) modules(jobsModule) modules(kmpModule) - module { - single { InAppNotification() } - } } diff --git a/localization b/localization index 3c7fba16b..9490971a1 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 3c7fba16b14d5970003f8d946138450c19112e09 +Subproject commit 9490971a1e0429701b15181dd3a1aa890a29a912 From bde565fb266f5a60bbeadc745c53794ab3ba6b9e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 9 Sep 2021 17:10:36 +0800 Subject: [PATCH 145/615] migrate some ui component from android to common --- common/build.gradle.kts | 1 + .../twidere/twiderex/component/Resources.kt | 32 ++++++++++++++++++ .../twidere/twiderex/component/Resources.kt | 33 +++++++++++++++++++ .../twiderex/component/lazy/LazyGrid.kt | 0 .../twiderex/component/lazy/itemDivider.kt | 0 .../twiderex/component/lazy/itemHeader.kt | 0 .../component/lazy/itemsGridIndexed.kt | 0 .../twiderex/component/lazy/itemsPaging.kt | 0 .../component/settings/SettingRadioItem.kt | 0 .../twiderex/component/settings/SwitchItem.kt | 0 10 files changed, 66 insertions(+) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/component/Resources.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt (100%) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 53e4961d3..1baada3ae 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { api(compose.material) implementation(projects.services) api("androidx.paging:paging-common:${Versions.paging}") + api("androidx.paging:paging-compose:${Versions.paging_compose}") api("androidx.datastore:datastore-core:${Versions.datastore}") api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}") diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/Resources.kt new file mode 100644 index 000000000..089138368 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component + +import androidx.compose.runtime.Composable + +@Composable +actual fun stringResource(id: Int, vararg formatArgs: Any) = androidx.compose.ui.res.stringResource(id, *formatArgs) + +@Composable +actual fun stringResource(id: Int) = androidx.compose.ui.res.stringResource(id) + +@Composable +actual fun painterResource(id: Int) = androidx.compose.ui.res.painterResource(id) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt new file mode 100644 index 000000000..8bfad9160 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter + +@Composable +expect fun stringResource(id: Int, vararg formatArgs: Any): String + +@Composable +expect fun stringResource(id: Int): String + +@Composable +expect fun painterResource(id: Int): Painter diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt From 273fe4045c97c13a48a3cabef6b9aad845222bb6 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 10 Sep 2021 11:47:06 +0800 Subject: [PATCH 146/615] migrate time component from android to common --- .../twiderex/component/FormattedTime.kt | 0 .../twiderex/component/HumanizedTime.kt | 0 .../twiderex/extensions/TimestampExtension.kt | 60 +++++++++++++++++++ 3 files changed, 60 insertions(+) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/FormattedTime.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/HumanizedTime.kt (100%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/FormattedTime.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/HumanizedTime.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt new file mode 100644 index 000000000..4676a90b2 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt @@ -0,0 +1,60 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.extensions + +import android.text.format.DateUtils +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +fun Long.humanizedTimestamp(): String { + return DateUtils.getRelativeTimeSpanString( + this, System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_ALL + ).toString() +} + +fun Long.formattedTimestamp(): String { + return SimpleDateFormat.getDateTimeInstance().format(Date(this)) +} + +val countUnits = arrayOf(null, "K", "M", "B") + +fun Long.humanizedCount(): String { + if (this < 1000) { + return this.toString() + } + var value = this.toDouble() + var index = 0 + while (index < countUnits.size) { + if (value < 1000) { + break + } + value /= 1000.0 + index++ + } + return if (value < 10 && value % 1.0 >= 0.049 && value % 1.0 < 0.5) { + String.format(Locale.getDefault(), "%.1f %s", value, countUnits[index]) + } else { + String.format(Locale.getDefault(), "%.0f %s", value, countUnits[index]) + } +} From cd42af8a8e09ea787f860afed7e8bdd6472124b2 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 10 Sep 2021 16:40:32 +0800 Subject: [PATCH 147/615] add svg support to common --- common/build.gradle.kts | 3 ++ .../com/twidere/twiderex/kmp/ResLoader.kt | 29 +++++++++++ .../twidere/twiderex/component/Resources.kt | 50 +++++++++++++++++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 13 +++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 21 ++++++++ 5 files changed, 116 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 53e4961d3..b4b98eaef 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -64,6 +64,9 @@ kotlin { implementation("androidx.room:room-paging:${Versions.room}") kapt("androidx.room:room-compiler:${Versions.room}") implementation("io.coil-kt:coil-base:${Versions.coil}") + implementation("io.coil-kt:coil-compose:${Versions.coil}") + implementation("io.coil-kt:coil-gif:${Versions.coil}") + implementation("io.coil-kt:coil-svg:${Versions.coil}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f6cef3392..ea5710abd 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -21,6 +21,14 @@ package com.twidere.twiderex.kmp import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import coil.compose.LocalImageLoader +import coil.compose.rememberImagePainter +import coil.decode.SvgDecoder +import com.twidere.twiderex.di.ext.get +import dev.icerock.moko.resources.FileResource +import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource actual class ResLoader( @@ -32,4 +40,25 @@ actual class ResLoader( ): String { return context.getString(res.resourceId, *args) } + + actual companion object { + actual val get: ResLoader + get() = get() + } + + @Composable + actual fun getSvg(res: FileResource): Painter { + val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" + return rememberImagePainter( + data, + LocalImageLoader.current + .newBuilder().componentRegistry { add(SvgDecoder(context)) }.build() + ) + } + + @Composable + actual fun getImage(res: ImageResource): Painter { + val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" + return rememberImagePainter(data) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt new file mode 100644 index 000000000..cbaababf7 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -0,0 +1,50 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import com.twidere.twiderex.kmp.ResLoader +import dev.icerock.moko.resources.FileResource +import dev.icerock.moko.resources.ImageResource +import dev.icerock.moko.resources.StringResource + +@Composable +fun stringResource(res: StringResource, vararg formatArgs: Any): String { + return ResLoader.get.getString(res, *formatArgs) +} + +@Composable +fun stringResource(res: StringResource): String { + return ResLoader.get.getString(res) +} + +/** + * res: FileResource:svg, ImageResource + */ +@Composable +fun painterResource(res: Any): Painter { + return when (res) { + is FileResource -> ResLoader.get.getSvg(res) + is ImageResource -> ResLoader.get.getImage(res) + else -> throw NotImplementedError() + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 99c52f6c9..728f53644 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -20,8 +20,21 @@ */ package com.twidere.twiderex.kmp +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import dev.icerock.moko.resources.FileResource +import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource expect class ResLoader { + companion object { + val get: ResLoader + } fun getString(res: StringResource, vararg args: Any): String + + @Composable + fun getSvg(res: FileResource): Painter + + @Composable + fun getImage(res: ImageResource): Painter } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 0287c1415..f06a26eb1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -20,6 +20,12 @@ */ package com.twidere.twiderex.kmp +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.di.ext.get +import dev.icerock.moko.resources.FileResource +import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource actual class ResLoader { @@ -29,4 +35,19 @@ actual class ResLoader { ): String { return res.localized(args = args) } + + actual companion object { + actual val get: ResLoader + get() = get() + } + + @Composable + actual fun getSvg(res: FileResource): Painter { + return painterResource(res.filePath) + } + + @Composable + actual fun getImage(res: ImageResource): Painter { + return painterResource(res.filePath) + } } From fdda86f4e927faa33ef5b58fdf19d541fde8d4d3 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 10 Sep 2021 16:47:14 +0800 Subject: [PATCH 148/615] update getImage for android --- .../androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index ea5710abd..9eeee9767 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.painter.Painter import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.SvgDecoder +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.di.ext.get import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource @@ -58,7 +59,6 @@ actual class ResLoader( @Composable actual fun getImage(res: ImageResource): Painter { - val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" - return rememberImagePainter(data) + return painterResource(res.drawableResId) } } From 5db88fc1ce8432259c03b34b772721c452c9bf20 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 10 Sep 2021 17:22:08 +0800 Subject: [PATCH 149/615] add LocalResLoader --- .../com/twidere/twiderex/kmp/ResLoader.kt | 6 ---- .../kotlin/com/twidere/twiderex/App.kt | 13 +++++++-- .../twidere/twiderex/component/Resources.kt | 10 +++---- .../com/twidere/twiderex/compose/ResLocal.kt | 28 +++++++++++++++++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 3 -- .../com/twidere/twiderex/kmp/ResLoader.kt | 6 ---- 6 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 9eeee9767..f3e60c7a5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -27,7 +27,6 @@ import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.SvgDecoder import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.di.ext.get import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -42,11 +41,6 @@ actual class ResLoader( return context.getString(res.resourceId, *args) } - actual companion object { - actual val get: ResLoader - get() = get() - } - @Composable actual fun getSvg(res: FileResource): Painter { val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 5e22e6535..15aa68d62 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -24,12 +24,19 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import com.twidere.twiderex.compose.LocalResLoader +import com.twidere.twiderex.di.ext.get @Composable fun App() { - MaterialTheme { - Scaffold { - Text("Twidere X!") + CompositionLocalProvider( + LocalResLoader provides get() + ) { + MaterialTheme { + Scaffold { + Text("Twidere X!") + } } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index cbaababf7..2e8f9a346 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -22,19 +22,19 @@ package com.twidere.twiderex.component import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.kmp.ResLoader +import com.twidere.twiderex.compose.LocalResLoader import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @Composable fun stringResource(res: StringResource, vararg formatArgs: Any): String { - return ResLoader.get.getString(res, *formatArgs) + return LocalResLoader.current.getString(res, *formatArgs) } @Composable fun stringResource(res: StringResource): String { - return ResLoader.get.getString(res) + return LocalResLoader.current.getString(res) } /** @@ -43,8 +43,8 @@ fun stringResource(res: StringResource): String { @Composable fun painterResource(res: Any): Painter { return when (res) { - is FileResource -> ResLoader.get.getSvg(res) - is ImageResource -> ResLoader.get.getImage(res) + is FileResource -> LocalResLoader.current.getSvg(res) + is ImageResource -> LocalResLoader.current.getImage(res) else -> throw NotImplementedError() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt new file mode 100644 index 000000000..2a1003535 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.compose + +import androidx.compose.runtime.staticCompositionLocalOf +import com.twidere.twiderex.kmp.ResLoader + +val LocalResLoader = staticCompositionLocalOf { + error("No ResLoader") +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 728f53644..f2cea261c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -27,9 +27,6 @@ import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource expect class ResLoader { - companion object { - val get: ResLoader - } fun getString(res: StringResource, vararg args: Any): String @Composable diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f06a26eb1..f24a62a39 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.kmp import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource -import com.twidere.twiderex.di.ext.get import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -36,11 +35,6 @@ actual class ResLoader { return res.localized(args = args) } - actual companion object { - actual val get: ResLoader - get() = get() - } - @Composable actual fun getSvg(res: FileResource): Painter { return painterResource(res.filePath) From 168e0fa0b5aa5013057646c43ad3c6090f413d52 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 17:41:52 +0800 Subject: [PATCH 150/615] fix new observer state issue --- .../kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt index 39b4a02be..1bc4fe527 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt @@ -43,6 +43,7 @@ class LifecycleRegistry : Lifecycle { override fun addObserver(observer: LifecycleObserver) { observers.add(observer) + observer.onStateChanged(currentState) } override fun hasObserver(): Boolean { From 875751725ec7c00bbe17f0702118adf7907a90b2 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 17:42:22 +0800 Subject: [PATCH 151/615] update account repository flow --- .../com/twidere/twiderex/repository/AccountRepository.kt | 5 +++-- .../com/twidere/twiderex/repository/AccountRepository.kt | 6 +++--- .../com/twidere/twiderex/repository/AccountRepository.kt | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 6474d35ad..a8ebf2d5e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -36,6 +36,7 @@ import com.twidere.twiderex.room.db.transform.toAndroid import com.twidere.twiderex.room.db.transform.toTwidere import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -68,14 +69,14 @@ actual class AccountRepository( private val _activeAccount = MutableStateFlow(if (hasAccount()) getCurrentAccount() else null) - actual val activeAccount + actual val activeAccount: Flow get() = _activeAccount.asSharedFlow() private val _accounts = MutableStateFlow( getAccounts() ) - actual val accounts + actual val accounts: Flow> get() = _accounts.asSharedFlow() actual fun hasAccount(): Boolean { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index d7e158932..ea2ac404f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -27,11 +27,11 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.Flow expect class AccountRepository { - val activeAccount: SharedFlow - val accounts: SharedFlow> + val activeAccount: Flow + val accounts: Flow> fun updateAccount(user: UiUser) fun getAccounts(): List fun hasAccount(): Boolean diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 574de46ad..3e69b1c40 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -27,12 +27,12 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.Flow actual class AccountRepository { - actual val activeAccount: SharedFlow + actual val activeAccount: Flow get() = TODO("Not yet implemented") - actual val accounts: SharedFlow> + actual val accounts: Flow> get() = TODO("Not yet implemented") actual fun updateAccount(user: UiUser) { From f88dcedfddc677bbc58bbb440adbb8853aa34496 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 17:42:58 +0800 Subject: [PATCH 152/615] fix di module resolve --- .../com/twidere/twiderex/di/modules/PreferencesModule.kt | 6 ++++++ .../com/twidere/twiderex/di/modules/ViewModelModule.kt | 9 +++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt index f3882567c..968413a59 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.di.modules import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.Serializer +import androidx.datastore.preferences.core.PreferenceDataStoreFactory import com.twidere.twiderex.preferences.PreferencesHolder import com.twidere.twiderex.preferences.serializer.AppearancePreferencesSerializer import com.twidere.twiderex.preferences.serializer.DisplayPreferencesSerializer @@ -46,6 +47,11 @@ internal val preferencesModule = module { ), ) } + single { + PreferenceDataStoreFactory.create { + createDataStoreFile("perferences.preferences_pb") + } + } } internal inline fun Scope.createDataStore( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt index d79cc93fb..33f45c6df 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -25,6 +25,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.preferences.PreferencesHolder import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import com.twidere.twiderex.viewmodel.DraftViewModel import com.twidere.twiderex.viewmodel.MediaViewModel @@ -181,11 +182,11 @@ private fun Module.search() { private fun Module.settings() { viewModel { AccountNotificationViewModel(get()) } - viewModel { AppearanceViewModel(get()) } - viewModel { DisplayViewModel(get()) } + viewModel { AppearanceViewModel(get().appearancePreferences) } + viewModel { DisplayViewModel(get().displayPreferences) } viewModel { LayoutViewModel(get()) } - viewModel { MiscViewModel(get(), get()) } - viewModel { NotificationViewModel(get()) } + viewModel { MiscViewModel(get().miscPreferences) } + viewModel { NotificationViewModel(get().notificationPreferences) } viewModel { StorageViewModel(get()) } } From cf9cb4c27c8c2d62c25641eece9c181acf405e19 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 17:43:21 +0800 Subject: [PATCH 153/615] clean up misc view model --- .../com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index a8d81ee28..e7874c984 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.model.MiscPreferences import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first @@ -31,7 +30,6 @@ import moe.tlaster.precompose.viewmodel.viewModelScope class MiscViewModel( private val miscPreferences: DataStore, - private val inAppNotification: InAppNotification, ) : ViewModel() { val nitter by lazy { MutableStateFlow("") From 8ae9ce45b6c41dbeee3577accb1d167acc26259b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 17:49:41 +0800 Subject: [PATCH 154/615] redesign PreComposeActivity --- .../com/twidere/twiderex/TwidereXActivity.kt | 2 +- .../lifecycle/PreComposeActivity.kt | 119 +----------------- 2 files changed, 3 insertions(+), 118 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index c16bfc0ac..c5e65ca80 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -28,6 +28,7 @@ import android.net.NetworkRequest import android.net.Uri import android.os.Bundle import android.view.WindowManager +import androidx.activity.compose.setContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn @@ -78,7 +79,6 @@ import com.twidere.twiderex.utils.LocalPlatformResolver import com.twidere.twiderex.utils.PlatformResolver import kotlinx.coroutines.flow.MutableStateFlow import moe.tlaster.precompose.lifecycle.PreComposeActivity -import moe.tlaster.precompose.lifecycle.setContent import moe.tlaster.precompose.navigation.NavController import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt index 69b198b7b..e98ca2d20 100644 --- a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt +++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -20,28 +20,15 @@ */ package moe.tlaster.precompose.lifecycle -import android.app.Activity -import android.os.Bundle -import android.view.ViewGroup -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionContext -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.platform.ComposeView -import androidx.lifecycle.ViewTreeLifecycleOwner -import androidx.savedstate.SavedStateRegistry -import androidx.savedstate.SavedStateRegistryController +import androidx.activity.ComponentActivity import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner import moe.tlaster.precompose.ui.BackDispatcher import moe.tlaster.precompose.ui.BackDispatcherOwner -import moe.tlaster.precompose.ui.LocalBackDispatcherOwner -import moe.tlaster.precompose.ui.LocalLifecycleOwner -import moe.tlaster.precompose.ui.LocalViewModelStoreOwner import moe.tlaster.precompose.viewmodel.ViewModelStore import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner open class PreComposeActivity : - Activity(), + ComponentActivity(), LifecycleOwner, ViewModelStoreOwner, androidx.lifecycle.LifecycleOwner, @@ -55,59 +42,19 @@ open class PreComposeActivity : ViewModelStore() } - private val androidLifecycle by lazy { - androidx.lifecycle.LifecycleRegistry(this) - } - - private val savedStateRegistryController by lazy { - SavedStateRegistryController.create(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - savedStateRegistryController.performRestore(savedInstanceState) - super.onCreate(savedInstanceState) - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_CREATE) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - savedStateRegistryController.performSave(outState) - } - - override fun onStart() { - super.onStart() - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_START) - } - override fun onResume() { super.onResume() - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_RESUME) lifecycle.currentState = Lifecycle.State.Active } override fun onPause() { super.onPause() lifecycle.currentState = Lifecycle.State.InActive - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_PAUSE) - } - - override fun onStop() { - super.onStop() - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_STOP) } override fun onDestroy() { super.onDestroy() lifecycle.currentState = Lifecycle.State.Destroyed - androidLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_DESTROY) - } - - override fun getLifecycle(): androidx.lifecycle.Lifecycle { - return androidLifecycle - } - - override fun getSavedStateRegistry(): SavedStateRegistry { - return savedStateRegistryController.savedStateRegistry } override val backDispatcher by lazy { @@ -120,65 +67,3 @@ open class PreComposeActivity : } } } - -fun PreComposeActivity.setContent( - parent: CompositionContext? = null, - content: @Composable () -> Unit -) { - val existingComposeView = window.decorView - .findViewById(android.R.id.content) - .getChildAt(0) as? ComposeView - - if (existingComposeView != null) with(existingComposeView) { - setParentCompositionContext(parent) - setContent { - ContentInternal(content) - } - } else ComposeView(this).apply { - // Set content and parent **before** setContentView - // to have ComposeView create the composition on attach - setParentCompositionContext(parent) - setContent { - ContentInternal(content) - } - // Set the view tree owners before setting the content view so that the inflation process - // and attach listeners will see them already present - setOwners() - setContentView(this, DefaultActivityContentLayoutParams) - } -} - -private fun PreComposeActivity.setOwners() { - val decorView = window.decorView - if (ViewTreeLifecycleOwner.get(decorView) == null) { - ViewTreeLifecycleOwner.set(decorView, this) - } - if (ViewTreeSavedStateRegistryOwner.get(decorView) == null) { - ViewTreeSavedStateRegistryOwner.set(decorView, this) - } -} - -@Composable -private fun PreComposeActivity.ContentInternal(content: @Composable () -> Unit) { - ProvideAndroidCompositionLocals { - content.invoke() - } -} - -@Composable -private fun PreComposeActivity.ProvideAndroidCompositionLocals( - content: @Composable () -> Unit, -) { - CompositionLocalProvider( - LocalLifecycleOwner provides this, - LocalViewModelStoreOwner provides this, - LocalBackDispatcherOwner provides this, - ) { - content.invoke() - } -} - -private val DefaultActivityContentLayoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT -) From 9b897a500fc57edd3cbfe83f12ec154da0b98e86 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Sep 2021 18:24:45 +0800 Subject: [PATCH 155/615] fix PreComposeActivity view model owner issue --- .../com/twidere/twiderex/TwidereXActivity.kt | 1 + .../lifecycle/PreComposeActivity.kt | 75 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index c5e65ca80..5b30275b1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -79,6 +79,7 @@ import com.twidere.twiderex.utils.LocalPlatformResolver import com.twidere.twiderex.utils.PlatformResolver import kotlinx.coroutines.flow.MutableStateFlow import moe.tlaster.precompose.lifecycle.PreComposeActivity +import moe.tlaster.precompose.lifecycle.setContent import moe.tlaster.precompose.navigation.NavController import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt index e98ca2d20..4012fe437 100644 --- a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt +++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -20,10 +20,20 @@ */ package moe.tlaster.precompose.lifecycle +import android.view.ViewGroup import androidx.activity.ComponentActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionContext +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.ComposeView +import androidx.lifecycle.ViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistryOwner +import androidx.savedstate.ViewTreeSavedStateRegistryOwner import moe.tlaster.precompose.ui.BackDispatcher import moe.tlaster.precompose.ui.BackDispatcherOwner +import moe.tlaster.precompose.ui.LocalBackDispatcherOwner +import moe.tlaster.precompose.ui.LocalLifecycleOwner +import moe.tlaster.precompose.ui.LocalViewModelStoreOwner import moe.tlaster.precompose.viewmodel.ViewModelStore import moe.tlaster.precompose.viewmodel.ViewModelStoreOwner @@ -33,7 +43,8 @@ open class PreComposeActivity : ViewModelStoreOwner, androidx.lifecycle.LifecycleOwner, SavedStateRegistryOwner, - BackDispatcherOwner { + BackDispatcherOwner, + androidx.lifecycle.LifecycleObserver { override val lifecycle by lazy { LifecycleRegistry() } @@ -67,3 +78,65 @@ open class PreComposeActivity : } } } + +fun PreComposeActivity.setContent( + parent: CompositionContext? = null, + content: @Composable () -> Unit +) { + val existingComposeView = window.decorView + .findViewById(android.R.id.content) + .getChildAt(0) as? ComposeView + + if (existingComposeView != null) with(existingComposeView) { + setParentCompositionContext(parent) + setContent { + ContentInternal(content) + } + } else ComposeView(this).apply { + // Set content and parent **before** setContentView + // to have ComposeView create the composition on attach + setParentCompositionContext(parent) + setContent { + ContentInternal(content) + } + // Set the view tree owners before setting the content view so that the inflation process + // and attach listeners will see them already present + setOwners() + setContentView(this, DefaultActivityContentLayoutParams) + } +} + +private fun PreComposeActivity.setOwners() { + val decorView = window.decorView + if (ViewTreeLifecycleOwner.get(decorView) == null) { + ViewTreeLifecycleOwner.set(decorView, this) + } + if (ViewTreeSavedStateRegistryOwner.get(decorView) == null) { + ViewTreeSavedStateRegistryOwner.set(decorView, this) + } +} + +@Composable +private fun PreComposeActivity.ContentInternal(content: @Composable () -> Unit) { + ProvideAndroidCompositionLocals { + content.invoke() + } +} + +@Composable +private fun PreComposeActivity.ProvideAndroidCompositionLocals( + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalLifecycleOwner provides this, + LocalViewModelStoreOwner provides this, + LocalBackDispatcherOwner provides this, + ) { + content.invoke() + } +} + +private val DefaultActivityContentLayoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT +) From fff3edb13300bbaa76262cf77cf8803464b0dcf7 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sat, 11 Sep 2021 11:38:55 +0800 Subject: [PATCH 156/615] attempt to fix notification repository test --- .../twidere/twiderex/mock/service/MockNotificationService.kt | 4 +++- .../com/twidere/twiderex/mock/service/MockTimelineService.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt index 9d021f234..24969d018 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt @@ -25,6 +25,7 @@ import com.twidere.services.microblog.NotificationService import com.twidere.services.microblog.model.INotification import com.twidere.twiderex.mock.model.mockINotification import com.twidere.twiderex.mock.model.toIPaging +import kotlinx.coroutines.delay import org.jetbrains.annotations.TestOnly internal class MockNotificationService @TestOnly constructor() : NotificationService, ErrorService(), MicroBlogService { @@ -36,8 +37,9 @@ internal class MockNotificationService @TestOnly constructor() : NotificationSer checkError() val list = mutableListOf() for (i in 0 until count) { + delay(1) list.add(mockINotification()) } - return list.toIPaging() + return list.reversed().toIPaging() } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt index a0de13c7d..6a34c1b50 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt @@ -84,6 +84,6 @@ internal class MockTimelineService @TestOnly constructor() : TimelineService, Mi for (i in 0 until count) { list.add(create()) } - return list.toIPaging() + return list.reversed().toIPaging() } } From 9995fe9aaad71ef1f5f9afeb9a826b0b3abd7749 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 13 Sep 2021 14:00:40 +0800 Subject: [PATCH 157/615] optimize video with same url --- .../twiderex/component/foundation/VideoPlayer.kt | 13 ++++++++----- .../com/twidere/twiderex/scenes/MediaScene.kt | 3 ++- .../twidere/twiderex/utils/video/VideoPool.kt | 16 ++++++++-------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index b5849f0fc..6ba676252 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -162,6 +162,9 @@ fun VideoPlayer( var isResume by remember { mutableStateOf(true) } + val videoKey by remember { + mutableStateOf(url + System.currentTimeMillis()) + } DisposableEffect(Unit) { val observer = object : LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -180,7 +183,7 @@ fun VideoPlayer( onDispose { updateState() player.release() - VideoPool.removeRect(url) + VideoPool.removeRect(videoKey) lifecycle.removeObserver(observer) } } @@ -204,16 +207,16 @@ fun VideoPlayer( } } coordinates.boundsInWindow().run { - VideoPool.setRect(url, this) - if (!isMostCenter && VideoPool.containsMiddleLine(url, middleLine)) { + VideoPool.setRect(videoKey, this) + if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { debounceJob?.cancel() debounceJob = composableScope.launch { delay(VideoPool.DEBOUNCE_DELAY) - if (VideoPool.containsMiddleLine(url, middleLine)) { + if (VideoPool.containsMiddleLine(videoKey, middleLine)) { isMostCenter = true } } - } else if (isMostCenter && !VideoPool.isMostCenter(url, middleLine)) { + } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { isMostCenter = false } } diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index d3a7fe6cc..398693c5c 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -464,7 +464,8 @@ fun MediaView( showControls = false, zOrderMediaOverlay = true, keepScreenOn = true, - volume = volume + volume = volume, + isListItem = false ) } MediaType.other -> Unit diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt index bad2376f0..b7f2018e0 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt @@ -40,11 +40,11 @@ object VideoPool { private val positionPool = ConcurrentHashMap() - fun setRect(url: String, rect: Rect) { + fun setRect(videoKey: String, rect: Rect) { if (rect.top <= 0.0f && rect.bottom <= 0.0f) { - removeRect(url) + removeRect(videoKey) } else { - positionPool[url] = rect + positionPool[videoKey] = rect } } @@ -52,21 +52,21 @@ object VideoPool { positionPool.remove(url) } - fun containsMiddleLine(url: String, middle: Float): Boolean { - positionPool[url]?.let { + fun containsMiddleLine(videoKey: String, middle: Float): Boolean { + positionPool[videoKey]?.let { return it.top <= middle && it.bottom >= middle } return false } - fun isMostCenter(url: String, middle: Float): Boolean { + fun isMostCenter(videoKey: String, middle: Float): Boolean { if (positionPool.size == 0) { return false } if (positionPool.size == 1) { return true } - var centerUrl = url + var centerUrl = videoKey var minGap = Float.MAX_VALUE positionPool.forEach { abs((it.value.top + it.value.bottom) / 2 - middle).let { curGap -> @@ -76,6 +76,6 @@ object VideoPool { } } } - return url == centerUrl + return videoKey == centerUrl } } From 387b86b726b5ae870de6eccffb2bcb816e2832e7 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 13 Sep 2021 14:02:33 +0800 Subject: [PATCH 158/615] change scroll to top strategy one reason is https://issuetracker.google.com/issues/196413671?pli=1 --- .../twiderex/component/lazy/LazyListController.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt b/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt index 4ce5cd47d..14c2be40a 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt @@ -25,7 +25,16 @@ import androidx.compose.foundation.lazy.LazyListState class LazyListController { internal var listState: LazyListState? = null + companion object { + const val SMOOTH_THRESHOLD = 5 + } + suspend fun scrollToTop() { - listState?.scrollToItem(0) + listState?.run { + if (firstVisibleItemIndex > SMOOTH_THRESHOLD) { + scrollToItem(SMOOTH_THRESHOLD) + } + animateScrollToItem(0) + } } } From 95c1ea19d2b733754ce48065564b88c698239550 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 13 Sep 2021 14:38:46 +0800 Subject: [PATCH 159/615] fix merge --- app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 398693c5c..21231530c 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -254,6 +254,7 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM swiperState = swiperState, customControl = videoControl, pagerState = pagerState, + volume = if (isMute) 0f else 1f ) DisposableEffect(Unit) { window.setOnSystemBarsVisibilityChangeListener { visibility -> From 7016adb3f689c9207a767bb757fa09a1bac6b957 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 13 Sep 2021 15:43:01 +0800 Subject: [PATCH 160/615] migrate loginLogo and placeholder from android to common --- .../main/kotlin/com/twidere/twiderex/TwidereXActivity.kt | 3 +++ common/build.gradle.kts | 4 ++++ .../kotlin/com/twidere/twiderex/component/LoginLogo.kt | 8 +++----- .../twiderex/component/placeholder/UiImagePlaceholder.kt | 0 .../commonMain/resources/MR/files/svg/ic_login_logo.svg | 8 ++++++++ 5 files changed, 18 insertions(+), 5 deletions(-) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/LoginLogo.kt (79%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt (100%) create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_login_logo.svg diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index aa6b1a13e..dbdf2e82b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -64,8 +64,10 @@ import com.google.accompanist.insets.ProvideWindowInsets import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.action.StatusActions import com.twidere.twiderex.component.foundation.LocalInAppNotification +import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.assisted.ProvideAssistedFactory import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.navigation.Router import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.preferences.PreferencesHolder @@ -195,6 +197,7 @@ class TwidereXActivity : ComponentActivity() { LocalActiveAccountViewModel provides accountViewModel, LocalIsActiveNetworkMetered provides isActiveNetworkMetered, LocalPlatformResolver provides platformResolver, + LocalResLoader provides ResLoader(this) ) { ProvidePreferences( preferencesHolder, diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 2d053901a..2512980d8 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -47,6 +47,10 @@ kotlin { implementation(projects.routeProcessor) ksp(projects.routeProcessor) implementation("dev.icerock.moko:resources:${Versions.moko}") + implementation("com.mxalbert.zoomable:zoomable:${Versions.zoomable}") + implementation("com.github.Tlaster:NestedScrollView:${ Versions.nestedScrollView}") + implementation("com.github.Tlaster:Swiper:${Versions.swiper}") + implementation("com.github.Tlaster:Placeholder:${Versions.placeholder}") } } val commonTest by getting { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt similarity index 79% rename from android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt index 1fb9708c9..9285b6f60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/LoginLogo.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt @@ -24,9 +24,7 @@ import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.MR @Composable fun LoginLogo( @@ -35,7 +33,7 @@ fun LoginLogo( Image( modifier = modifier, contentScale = ContentScale.FillWidth, - painter = painterResource(id = R.drawable.ic_login_logo), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twidere) + painter = painterResource(res = MR.files.ic_login_logo), + contentDescription = stringResource(res = MR.strings.accessibility_common_logo_twidere) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt diff --git a/common/src/commonMain/resources/MR/files/svg/ic_login_logo.svg b/common/src/commonMain/resources/MR/files/svg/ic_login_logo.svg new file mode 100644 index 000000000..db688b87c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_login_logo.svg @@ -0,0 +1,8 @@ + + + + + + + + From 2a7cb234f87118839846945b079fa31a88df89a8 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 13 Sep 2021 21:12:42 +0800 Subject: [PATCH 161/615] update vm flow timeline usage --- .../twiderex/repository/AccountRepository.kt | 5 +- .../twiderex/extensions/FlowExtensions.kt | 9 ++-- .../viewmodel/compose/ComposeViewModel.kt | 9 +++- .../timeline/HomeTimelineViewModel.kt | 36 ++++++++------ .../timeline/MentionsTimelineViewModel.kt | 48 +++++++++---------- .../timeline/NotificationTimelineViewModel.kt | 18 ++++--- .../viewmodel/timeline/TimelineViewModel.kt | 23 ++++++--- .../mastodon/FederatedTimelineViewModel.kt | 12 +++-- .../mastodon/LocalTimelineViewModel.kt | 12 +++-- 9 files changed, 99 insertions(+), 73 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index a8ebf2d5e..68ffb27cb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -38,7 +38,6 @@ import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow private const val ACCOUNT_TYPE = "com.twidere.twiderex.account" private const val ACCOUNT_AUTH_TOKEN_TYPE = "com.twidere.twiderex.account.token" @@ -70,14 +69,14 @@ actual class AccountRepository( MutableStateFlow(if (hasAccount()) getCurrentAccount() else null) actual val activeAccount: Flow - get() = _activeAccount.asSharedFlow() + get() = _activeAccount private val _accounts = MutableStateFlow( getAccounts() ) actual val accounts: Flow> - get() = _accounts.asSharedFlow() + get() = _accounts actual fun hasAccount(): Boolean { return getAccounts().isNotEmpty() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index 9aba1ef10..cc0b94b09 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -28,7 +28,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.stateIn @@ -59,6 +58,10 @@ fun Flow.flowWithLifecycle( fun Flow.asStateIn( scope: CoroutineScope, initialValue: T -): StateFlow { - return stateIn(scope, SharingStarted.Lazily, initialValue) +): Flow { + return stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = initialValue + ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 52f16bca7..bec91b512 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -47,6 +47,7 @@ import com.twidere.twiderex.utils.notifyError import com.twitter.twittertext.Extractor import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest @@ -54,6 +55,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -182,7 +184,11 @@ open class ComposeViewModel( } val location by lazy { - locationProvider.location.asStateIn(viewModelScope, null) + locationProvider.location.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5000), + null + ) } val excludedReplyUserIds = MutableStateFlow>(emptyList()) val replyToUserName by lazy { @@ -245,6 +251,7 @@ open class ComposeViewModel( .asStateIn(viewModelScope, false) val locationEnabled = MutableStateFlow(false) val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index cd6f83d28..4c031e7d7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -27,7 +27,8 @@ import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.mapLatest import moe.tlaster.precompose.viewmodel.viewModelScope class HomeTimelineViewModel( @@ -39,20 +40,25 @@ class HomeTimelineViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } - override val pagingMediator = account.map { - if (it != null) { - HomeTimelineMediator( - it.service as TimelineService, - it.accountKey, - database, - ) - } else { - null - } + @OptIn(ExperimentalCoroutinesApi::class) + override val pagingMediator by lazy { + account.mapLatest { + it?.let { + HomeTimelineMediator( + it.service as TimelineService, + it.accountKey, + database, + ) + } + }.asStateIn(viewModelScope, null) } - override val savedStateKey = account.map { - it?.let { - "${it.accountKey}_home" - } + + @OptIn(ExperimentalCoroutinesApi::class) + override val savedStateKey by lazy { + account.mapLatest { + it?.let { + "${it.accountKey}_home" + } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 96010c7e8..7e0ab938d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -42,30 +42,30 @@ class MentionsTimelineViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } - override val pagingMediator = account.map { - if (it != null) { - MentionTimelineMediator( - service = it.service as TimelineService, - accountKey = it.accountKey, - database = database, - addCursorIfNeed = { data, accountKey -> - notificationRepository.addCursorIfNeeded( - accountKey, - NotificationCursorType.Mentions, - data.status.statusId, - data.status.timestamp, - ) - } - ) - } else { - null - } + override val pagingMediator by lazy { + account.map { + it?.let { + MentionTimelineMediator( + service = it.service as TimelineService, + accountKey = it.accountKey, + database = database, + addCursorIfNeed = { data, accountKey -> + notificationRepository.addCursorIfNeeded( + accountKey, + NotificationCursorType.Mentions, + data.status.statusId, + data.status.timestamp, + ) + } + ) + } + }.asStateIn(viewModelScope, null) } - override val savedStateKey = account.map { - if (it != null) { - "${it.accountKey}_mentions" - } else { - null - } + override val savedStateKey by lazy { + account.map { + it?.let { + "${it.accountKey}_mentions" + } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 1ebe55400..7dca6c3dd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -44,7 +44,7 @@ class NotificationTimelineViewModel( override val pagingMediator by lazy { account.map { - if (it != null) { + it?.let { NotificationTimelineMediator( service = it.service as NotificationService, accountKey = it.accountKey, @@ -58,16 +58,14 @@ class NotificationTimelineViewModel( ) } ) - } else { - null } - } + }.asStateIn(viewModelScope, null) } - override val savedStateKey = account.map { - if (it == null) { - null - } else { - "${it.accountKey}_notification" - } + override val savedStateKey by lazy { + account.map { + it?.let { + "${it.accountKey}_notification" + } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 1fafb1775..7b18d9ead 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -32,11 +32,14 @@ import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.paging.toUi import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -44,17 +47,23 @@ import moe.tlaster.precompose.viewmodel.viewModelScope abstract class TimelineViewModel( private val dataStore: DataStore ) : ViewModel() { - @OptIn(ExperimentalCoroutinesApi::class) + abstract val pagingMediator: Flow + abstract val savedStateKey: Flow + + @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) val source by lazy { - pagingMediator.flatMapLatest { - it?.pager()?.toUi() ?: emptyFlow() + pagingMediator.transformLatest { + it?.let { + emitAll(it.pager().toUi()) + } }.cachedIn(viewModelScope) } - abstract val pagingMediator: Flow - abstract val savedStateKey: Flow + @OptIn(ExperimentalCoroutinesApi::class) - val loadingBetween: Flow> - get() = pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + val loadingBetween: Flow> by lazy { + pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + .asStateIn(viewModelScope, emptyList()) + } @OptIn(ExperimentalCoroutinesApi::class) val timelineScrollState by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 13fec87e3..9aa551784 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -49,12 +49,14 @@ class FederatedTimelineViewModel( database, ) } - } + }.asStateIn(viewModelScope, null) } - override val savedStateKey = account.map { - it?.let { - "${it.accountKey}_federated" - } + override val savedStateKey by lazy { + account.map { + it?.let { + "${it.accountKey}_federated" + } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 867d61edc..79d15e4a8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -49,12 +49,14 @@ class LocalTimelineViewModel( database, ) } - } + }.asStateIn(viewModelScope, null) } - override val savedStateKey = account.map { - it?.let { - "${it.accountKey}_local" - } + override val savedStateKey by lazy { + account.map { + it?.let { + "${it.accountKey}_local" + } + }.asStateIn(viewModelScope, null) } } From a20df2bf9bb8cbb5c538da8e8b1ca1e8d0d896a1 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Sep 2021 10:59:46 +0800 Subject: [PATCH 162/615] format code --- .../kotlin/com/twidere/twiderex/component/Resources.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index 3b94d1ad3..2e8f9a346 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -47,4 +47,4 @@ fun painterResource(res: Any): Painter { is ImageResource -> LocalResLoader.current.getImage(res) else -> throw NotImplementedError() } -} \ No newline at end of file +} From 9cf30410535eca33c9bec2c1d803f7568b330afb Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Sep 2021 13:28:06 +0800 Subject: [PATCH 163/615] fix vm flow usage --- .../twiderex/component/TimelineComponent.kt | 57 +++++++++---------- .../twiderex/component/UserComponent.kt | 7 +-- build.gradle.kts | 2 +- .../twiderex/extensions/FlowExtensions.kt | 14 ----- .../twiderex/viewmodel/MediaViewModel.kt | 3 +- .../twiderex/viewmodel/StatusViewModel.kt | 9 +-- .../compose/ComposeSearchUserViewModel.kt | 7 +-- .../viewmodel/compose/ComposeViewModel.kt | 17 +++--- .../MastodonComposeSearchHashtagViewModel.kt | 7 +-- .../viewmodel/dm/DMConversationViewModel.kt | 7 +-- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 6 +- .../dm/DMNewConversationViewModel.kt | 6 +- .../lists/ListsAddMemberViewModel.kt | 3 +- .../lists/ListsSearchUserViewModel.kt | 7 +-- .../viewmodel/lists/ListsTimelineViewModel.kt | 7 +-- .../viewmodel/lists/ListsUserViewModel.kt | 8 +-- .../viewmodel/lists/ListsViewModel.kt | 14 ++--- .../mastodon/MastodonHashtagViewModel.kt | 7 +-- .../MastodonSearchHashtagViewModel.kt | 7 +-- .../viewmodel/search/SearchInputViewModel.kt | 3 +- .../viewmodel/search/SearchSaveViewModel.kt | 3 +- .../viewmodel/search/SearchTweetsViewModel.kt | 9 +-- .../viewmodel/search/SearchUserViewModel.kt | 7 +-- .../settings/AccountNotificationViewModel.kt | 6 +- .../viewmodel/settings/LayoutViewModel.kt | 3 +- .../timeline/HomeTimelineViewModel.kt | 8 +-- .../timeline/MentionsTimelineViewModel.kt | 8 +-- .../timeline/NotificationTimelineViewModel.kt | 8 +-- .../viewmodel/timeline/TimelineViewModel.kt | 7 +-- .../mastodon/FederatedTimelineViewModel.kt | 8 +-- .../mastodon/LocalTimelineViewModel.kt | 8 +-- .../viewmodel/trend/TrendViewModel.kt | 7 +-- .../search/TwitterSearchMediaViewModel.kt | 7 +-- .../twitter/user/TwitterUserViewModel.kt | 4 +- .../viewmodel/user/FollowersViewModel.kt | 7 +-- .../viewmodel/user/FollowingViewModel.kt | 7 +-- .../user/UserFavouriteTimelineViewModel.kt | 7 +-- .../user/UserMediaTimelineViewModel.kt | 9 +-- .../viewmodel/user/UserTimelineViewModel.kt | 29 ++++------ .../twiderex/viewmodel/user/UserViewModel.kt | 5 +- 40 files changed, 124 insertions(+), 231 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 4e5230c03..0a4689468 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -34,11 +33,7 @@ import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.refreshOrRetry -import com.twidere.twiderex.viewmodel.timeline.TimelineScrollState import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter @Composable fun TimelineComponent( @@ -55,38 +50,38 @@ fun TimelineComponent( }, refreshIndicatorPadding = contentPadding ) { - val scrollState by viewModel.timelineScrollState.observeAsState( - initial = TimelineScrollState.Zero - ) + // val scrollState by viewModel.timelineScrollState.observeAsState( + // initial = TimelineScrollState.Zero + // ) val listState = rememberLazyListState() - LaunchedEffect(Unit) { - snapshotFlow { scrollState } - .distinctUntilChanged() - .collect { - listState.scrollToItem( - it.firstVisibleItemIndex, - it.firstVisibleItemScrollOffset - ) - } - } + // LaunchedEffect(Unit) { + // snapshotFlow { scrollState } + // .distinctUntilChanged() + // .collect { + // listState.scrollToItem( + // it.firstVisibleItemIndex, + // it.firstVisibleItemScrollOffset + // ) + // } + // } if (items.itemCount > 0) { LaunchedEffect(lazyListController) { lazyListController?.listState = listState } } - LaunchedEffect(Unit) { - snapshotFlow { listState.isScrollInProgress } - .distinctUntilChanged() - .filter { !it } - .collect { - viewModel.saveScrollState( - TimelineScrollState( - firstVisibleItemIndex = listState.firstVisibleItemIndex, - firstVisibleItemScrollOffset = listState.firstVisibleItemScrollOffset, - ) - ) - } - } + // LaunchedEffect(Unit) { + // snapshotFlow { listState.isScrollInProgress } + // .distinctUntilChanged() + // .filter { !it } + // .collect { + // viewModel.saveScrollState( + // TimelineScrollState( + // firstVisibleItemIndex = listState.firstVisibleItemIndex, + // firstVisibleItemScrollOffset = listState.firstVisibleItemScrollOffset, + // ) + // ) + // } + // } LazyUiStatusList( items = items, state = listState, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 6e04aa848..e79f24993 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -65,7 +65,6 @@ import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -230,10 +229,10 @@ fun UserStatusTimeline( viewModel: UserViewModel, ) { val user by viewModel.user.observeAsState(initial = null) - var excludeReplies by rememberSaveable { mutableStateOf(false) } val timelineViewModel: UserTimelineViewModel = getViewModel { - parametersOf(userKey, excludeReplies) + parametersOf(userKey) } + val excludeReplies by timelineViewModel.excludeReplies.observeAsState(initial = false) val timelineSource = timelineViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -244,7 +243,7 @@ fun UserStatusTimeline( user?.let { user -> item { UserStatusTimelineFilter(user, excludeReplies) { - excludeReplies = it + timelineViewModel.setExcludeReplies(it) } } } diff --git a/build.gradle.kts b/build.gradle.kts index 8ee741f0a..53ed2ab88 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,7 @@ allprojects { tasks.withType { kotlinOptions { jvmTarget = Versions.Java.jvmTarget - allWarningsAsErrors = true + // allWarningsAsErrors = true freeCompilerArgs = listOf( "-Xopt-in=kotlin.RequiresOptIn", ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index cc0b94b09..73aba9449 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -24,13 +24,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.stateIn import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.repeatOnLifecycle import moe.tlaster.precompose.ui.LocalLifecycleOwner @@ -54,14 +51,3 @@ fun Flow.flowWithLifecycle( } close() } - -fun Flow.asStateIn( - scope: CoroutineScope, - initialValue: T -): Flow { - return stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = initialValue - ) -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 2960336b9..902b8297c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.action.MediaAction -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.repository.AccountRepository @@ -42,7 +41,7 @@ class MediaViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index afe91491a..f55655cb4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.viewmodel -import androidx.paging.cachedIn -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository @@ -29,7 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class StatusViewModel( private val statusRepository: StatusRepository, @@ -37,7 +34,7 @@ class StatusViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { @@ -47,7 +44,7 @@ class StatusViewModel( } else { emptyFlow() } - }.asStateIn(viewModelScope, null) + } } @OptIn(ExperimentalCoroutinesApi::class) @@ -63,6 +60,6 @@ class StatusViewModel( } else { emptyFlow() } - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index b498c001c..0ecb6a34b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -22,10 +22,8 @@ package com.twidere.twiderex.viewmodel.compose import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,13 +33,12 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class ComposeSearchUserViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val text = MutableStateFlow("") @@ -66,5 +63,5 @@ class ComposeSearchUserViewModel( } ?: emptyFlow() } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index bec91b512..e1ed66fd3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.text.input.TextFieldValue import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.action.DraftAction -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.kmp.LocationProvider @@ -161,7 +160,7 @@ open class ComposeViewModel( val composeType: ComposeType, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } open val draftId: String = UUID.randomUUID().toString() @@ -176,7 +175,7 @@ open class ComposeViewModel( emptyFlow() } } ?: emptyFlow() - }.asStateIn(viewModelScope, emptyList()) + } } val draftCount by lazy { @@ -204,7 +203,7 @@ open class ComposeViewModel( } else { emptyList() } - }.asStateIn(viewModelScope, emptyList()) + } } val loadingReplyUser = MutableStateFlow(false) @@ -232,7 +231,7 @@ open class ComposeViewModel( } else { emptyList() } - }.asStateIn(viewModelScope, emptyList()) + } } val voteState = MutableStateFlow(null) @@ -245,10 +244,10 @@ open class ComposeViewModel( val images = MutableStateFlow>(emptyList()) val canSend = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - .asStateIn(viewModelScope, false) + val canSaveDraft = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - .asStateIn(viewModelScope, false) + val locationEnabled = MutableStateFlow(false) val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) @@ -294,7 +293,7 @@ open class ComposeViewModel( } else { flowOf(null) } - }.asStateIn(viewModelScope, null) + } } fun setText(value: TextFieldValue) { @@ -385,7 +384,7 @@ open class ComposeViewModel( PlatformType.Mastodon -> 4 else -> 4 } - }.asStateIn(viewModelScope, 4) + } } fun trackingLocation() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 60b7682e0..446cc5009 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -22,10 +22,8 @@ package com.twidere.twiderex.viewmodel.compose import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,13 +33,12 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonComposeSearchHashtagViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val text = MutableStateFlow("") @@ -65,5 +62,5 @@ class MastodonComposeSearchHashtagViewModel( } ?: emptyFlow() } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index bf55a8913..915d11a41 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -20,24 +20,21 @@ */ package com.twidere.twiderex.viewmodel.dm -import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class DMConversationViewModel( private val repository: DirectMessageRepository, private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -50,6 +47,6 @@ class DMConversationViewModel( lookupService = account.service as LookupService ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 450364953..b0ca2e481 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -20,11 +20,9 @@ */ package com.twidere.twiderex.viewmodel.dm -import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData @@ -50,7 +48,7 @@ class DMEventViewModel( private val conversationKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -76,7 +74,7 @@ class DMEventViewModel( lookupService = account.service as LookupService ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } // input diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index c2b782772..0b00438be 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -22,10 +22,8 @@ package com.twidere.twiderex.viewmodel.dm import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource @@ -47,7 +45,7 @@ class DMNewConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val input = MutableStateFlow("") @@ -72,7 +70,7 @@ class DMNewConversationViewModel( } ?: emptyFlow() } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } fun createNewConversation(receiver: UiUser, onResult: (key: MicroBlogKey?) -> Unit) { viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt index 6c8a3e073..f8e1dc008 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification @@ -42,7 +41,7 @@ class ListsAddMemberViewModel( private val listId: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 1ba158d9f..779fee8e5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -22,10 +22,8 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,14 +33,13 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class ListsSearchUserViewModel( private val accountRepository: AccountRepository, following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val text = MutableStateFlow("") @@ -68,5 +65,5 @@ class ListsSearchUserViewModel( } ?: emptyFlow() } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index a2d126d80..d115ac5bb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -20,9 +20,7 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -30,7 +28,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class ListsTimelineViewModel( repository: TimelineRepository, @@ -38,7 +35,7 @@ class ListsTimelineViewModel( listKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -51,6 +48,6 @@ class ListsTimelineViewModel( service = it.service as TimelineService ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5440af03d..e918982ac 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -21,9 +21,7 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.PagingData -import androidx.paging.cachedIn import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository @@ -43,7 +41,7 @@ class ListsUserViewModel( private val viewMembers: Boolean = true, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -56,7 +54,7 @@ class ListsUserViewModel( listId = listId ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } @OptIn(ExperimentalCoroutinesApi::class) @@ -69,7 +67,7 @@ class ListsUserViewModel( listId = listId ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } override val source: Flow> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index a3534ab97..153d57b65 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -20,10 +20,8 @@ */ package com.twidere.twiderex.viewmodel.lists -import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList @@ -47,7 +45,7 @@ class ListsViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -59,7 +57,7 @@ class ListsViewModel( service = account.service as ListsService ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } @OptIn(ExperimentalCoroutinesApi::class) @@ -70,7 +68,7 @@ class ListsViewModel( it.filter { it.isOwner(account.user.userId) } } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } @OptIn(ExperimentalCoroutinesApi::class) @@ -81,7 +79,7 @@ class ListsViewModel( pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } @@ -120,7 +118,7 @@ class ListsCreateViewModel( private val onResult: (success: Boolean, list: UiList?) -> Unit ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } fun createList( @@ -149,7 +147,7 @@ class ListsModifyViewModel( private val listKey: MicroBlogKey, ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val editName = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 2f60d096f..5ceb602cc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,16 +20,13 @@ */ package com.twidere.twiderex.viewmodel.mastodon -import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonHashtagViewModel( private val repository: TimelineRepository, @@ -37,7 +34,7 @@ class MastodonHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -50,6 +47,6 @@ class MastodonHashtagViewModel( service = it.service as MastodonService ) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index f86888eb0..40538743e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -22,24 +22,21 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonSearchHashtagViewModel( private val accountRepository: AccountRepository, keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -58,6 +55,6 @@ class MastodonSearchHashtagViewModel( ) }.flow } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index dde1157c7..52e4bf8f1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository @@ -38,7 +37,7 @@ class SearchInputViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 6fce97ac3..82decd3df 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow @@ -35,7 +34,7 @@ class SearchSaveViewModel( private val content: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 39929427f..5e4ed99f2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -20,11 +20,9 @@ */ package com.twidere.twiderex.viewmodel.search -import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository @@ -33,7 +31,6 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class SearchTweetsViewModel( val database: CacheDatabase, @@ -41,7 +38,7 @@ class SearchTweetsViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -53,8 +50,8 @@ class SearchTweetsViewModel( database, account.accountKey, account.service as SearchService - ).pager().flow.map { it.map { it.status } }.cachedIn(viewModelScope) + ).pager().flow.map { it.map { it.status } } } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index cc146c0e7..0b962d421 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -22,17 +22,14 @@ package com.twidere.twiderex.viewmodel.search import androidx.paging.Pager import androidx.paging.PagingConfig -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class SearchUserViewModel( private val accountRepository: AccountRepository, @@ -40,7 +37,7 @@ class SearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -61,6 +58,6 @@ class SearchUserViewModel( ) }.flow } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 13db48818..1d5ec2616 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flatMapLatest @@ -35,7 +34,7 @@ class AccountNotificationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val preferences by lazy { @@ -43,13 +42,12 @@ class AccountNotificationViewModel( it?.let { accountRepository.getAccountPreferences(it.accountKey) } - }.asStateIn(viewModelScope, null) + } } @OptIn(ExperimentalCoroutinesApi::class) val isNotificationEnabled by lazy { preferences.flatMapLatest { it?.isNotificationEnabled ?: flowOf(false) } - .asStateIn(viewModelScope, false) } fun setIsNotificationEnabled(value: Boolean) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 467bcfc7a..54dcafad7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.lastOrNull @@ -36,7 +35,7 @@ class LayoutViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } fun updateHomeMenu(oldIndex: Int, newIndex: Int, menus: List) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 4c031e7d7..011d7444d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -24,12 +24,10 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.mapLatest -import moe.tlaster.precompose.viewmodel.viewModelScope class HomeTimelineViewModel( dataStore: DataStore, @@ -37,7 +35,7 @@ class HomeTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -50,7 +48,7 @@ class HomeTimelineViewModel( database, ) } - }.asStateIn(viewModelScope, null) + } } @OptIn(ExperimentalCoroutinesApi::class) @@ -59,6 +57,6 @@ class HomeTimelineViewModel( it?.let { "${it.accountKey}_home" } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 7e0ab938d..fb1b7bbd3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -24,13 +24,11 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map -import moe.tlaster.precompose.viewmodel.viewModelScope class MentionsTimelineViewModel( dataStore: DataStore, @@ -39,7 +37,7 @@ class MentionsTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } override val pagingMediator by lazy { @@ -59,13 +57,13 @@ class MentionsTimelineViewModel( } ) } - }.asStateIn(viewModelScope, null) + } } override val savedStateKey by lazy { account.map { it?.let { "${it.accountKey}_mentions" } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 7dca6c3dd..212c117c2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -24,13 +24,11 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map -import moe.tlaster.precompose.viewmodel.viewModelScope class NotificationTimelineViewModel( dataStore: DataStore, @@ -39,7 +37,7 @@ class NotificationTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } override val pagingMediator by lazy { @@ -59,13 +57,13 @@ class NotificationTimelineViewModel( } ) } - }.asStateIn(viewModelScope, null) + } } override val savedStateKey by lazy { account.map { it?.let { "${it.accountKey}_notification" } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 7b18d9ead..b7641f658 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -24,9 +24,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey -import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager @@ -56,13 +54,12 @@ abstract class TimelineViewModel( it?.let { emitAll(it.pager().toUi()) } - }.cachedIn(viewModelScope) + } } @OptIn(ExperimentalCoroutinesApi::class) val loadingBetween: Flow> by lazy { pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } - .asStateIn(viewModelScope, emptyList()) } @OptIn(ExperimentalCoroutinesApi::class) @@ -79,7 +76,7 @@ abstract class TimelineViewModel( firstVisibleItemScrollOffset = firstVisibleItemScrollOffset, ) } - }.asStateIn(viewModelScope, TimelineScrollState.Zero) + } } fun loadBetween( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 9aa551784..0a5f59c9b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -24,12 +24,10 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import kotlinx.coroutines.flow.map -import moe.tlaster.precompose.viewmodel.viewModelScope class FederatedTimelineViewModel( dataStore: DataStore, @@ -37,7 +35,7 @@ class FederatedTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } override val pagingMediator by lazy { @@ -49,7 +47,7 @@ class FederatedTimelineViewModel( database, ) } - }.asStateIn(viewModelScope, null) + } } override val savedStateKey by lazy { @@ -57,6 +55,6 @@ class FederatedTimelineViewModel( it?.let { "${it.accountKey}_federated" } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 79d15e4a8..e63bc0f2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -24,12 +24,10 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import kotlinx.coroutines.flow.map -import moe.tlaster.precompose.viewmodel.viewModelScope class LocalTimelineViewModel( dataStore: DataStore, @@ -37,7 +35,7 @@ class LocalTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } override val pagingMediator by lazy { @@ -49,7 +47,7 @@ class LocalTimelineViewModel( database, ) } - }.asStateIn(viewModelScope, null) + } } override val savedStateKey by lazy { @@ -57,6 +55,6 @@ class LocalTimelineViewModel( it?.let { "${it.accountKey}_local" } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index a1a23ea2d..d14d8821d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -20,23 +20,20 @@ */ package com.twidere.twiderex.viewmodel.trend -import androidx.paging.cachedIn import com.twidere.services.microblog.TrendService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class TrendViewModel( private val repository: TrendRepository, private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -50,6 +47,6 @@ class TrendViewModel( } else { emptyFlow() } - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 0a9fe7317..e370b5c7d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -20,16 +20,13 @@ */ package com.twidere.twiderex.viewmodel.twitter.search -import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterSearchMediaViewModel( private val repository: SearchRepository, @@ -37,7 +34,7 @@ class TwitterSearchMediaViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -46,6 +43,6 @@ class TwitterSearchMediaViewModel( it?.let { repository.media(keyword, it.accountKey, it.service as SearchService) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 9e68292d8..2c1b77416 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.twitter.user import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository @@ -29,7 +28,6 @@ import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterUserViewModel( private val repository: UserRepository, @@ -39,7 +37,7 @@ class TwitterUserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val error = MutableStateFlow(null) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 0e5badc5e..74f1704a2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -20,16 +20,13 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest -import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( private val accountRepository: AccountRepository, @@ -37,7 +34,7 @@ class FollowersViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -46,6 +43,6 @@ class FollowersViewModel( it?.let { account -> repository.followers(userKey, account.service as RelationshipService) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 54005cd84..195f2d0f7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -20,16 +20,13 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest -import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( private val accountRepository: AccountRepository, @@ -37,7 +34,7 @@ class FollowingViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -46,6 +43,6 @@ class FollowingViewModel( it?.let { account -> repository.following(userKey, account.service as RelationshipService) } ?: emptyFlow() - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 3b37c468a..6dbc60d0a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -20,9 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -30,7 +28,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class UserFavouriteTimelineViewModel( private val repository: TimelineRepository, @@ -38,7 +35,7 @@ class UserFavouriteTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -54,6 +51,6 @@ class UserFavouriteTimelineViewModel( } else { emptyFlow() } - }.cachedIn(viewModelScope) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index d9658d3ef..96b03b7f5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -22,12 +22,10 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.PagingConfig import androidx.paging.PagingData -import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus @@ -40,7 +38,6 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class UserMediaTimelineViewModel( private val database: CacheDatabase, @@ -48,7 +45,7 @@ class UserMediaTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - repository.activeAccount.asStateIn(viewModelScope, null) + repository.activeAccount } @OptIn(ExperimentalCoroutinesApi::class) @@ -65,7 +62,7 @@ class UserMediaTimelineViewModel( pagingData.map { it.status } - }.cachedIn(viewModelScope).map { + }.map { it.flatMap { it.media.map { media -> media to it } } @@ -84,6 +81,6 @@ class UserMediaTimelineViewModel( service = it.service as TimelineService ) } - }.asStateIn(viewModelScope, null) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index d0699dad4..8ec14dc56 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -20,19 +20,16 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flattenMerge +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class UserTimelineViewModel( private val repository: TimelineRepository, @@ -40,9 +37,9 @@ class UserTimelineViewModel( userKey: MicroBlogKey, ) : ViewModel() { private val _excludeReplies = MutableStateFlow(false) - val excludeReplies = _excludeReplies.asStateIn(viewModelScope, false) + val excludeReplies = _excludeReplies private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } fun setExcludeReplies(value: Boolean) { @@ -51,17 +48,13 @@ class UserTimelineViewModel( @OptIn(FlowPreview::class) val source by lazy { - combine(account, _excludeReplies) { account, excludeReplies -> - if (account != null) { - repository.userTimeline( - userKey = userKey, - accountKey = account.accountKey, - service = account.service as TimelineService, - exclude_replies = excludeReplies, - ) - } else { - emptyFlow() - } - }.flattenMerge().cachedIn(viewModelScope) + combine(account.mapNotNull { it }, _excludeReplies) { account, excludeReplies -> + repository.userTimeline( + userKey = userKey, + accountKey = account.accountKey, + service = account.service as TimelineService, + exclude_replies = excludeReplies, + ) + }.flattenMerge() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 3215b52ff..ff4c3aded 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository @@ -44,7 +43,7 @@ class UserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount } val refreshing = MutableStateFlow(false) @@ -54,7 +53,7 @@ class UserViewModel( val isMe by lazy { account.map { userKey == it?.accountKey - }.asStateIn(viewModelScope, false) + } } fun refresh() = viewModelScope.launch { From 9d31c8ec4e8473134a6475b29abc889edf617229 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Sep 2021 16:13:36 +0800 Subject: [PATCH 164/615] fix compose --- build.gradle.kts | 2 +- .../twidere/twiderex/action/ComposeAction.kt | 48 +++++++++++-------- .../di/modules/ActionsModule.android.kt | 2 +- .../twidere/twiderex/action/ComposeAction.kt | 2 - .../viewmodel/compose/ComposeViewModel.kt | 15 ++---- .../twidere/twiderex/action/ComposeAction.kt | 4 -- 6 files changed, 36 insertions(+), 37 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 53ed2ab88..8ee741f0a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,7 @@ allprojects { tasks.withType { kotlinOptions { jvmTarget = Versions.Java.jvmTarget - // allWarningsAsErrors = true + allWarningsAsErrors = true freeCompilerArgs = listOf( "-Xopt-in=kotlin.RequiresOptIn", ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index 4fb641993..74ab6a4d7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -21,38 +21,48 @@ package com.twidere.twiderex.action import androidx.work.WorkManager -import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData +import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.worker.compose.MastodonComposeWorker import com.twidere.twiderex.worker.compose.TwitterComposeWorker import com.twidere.twiderex.worker.draft.RemoveDraftWorker import com.twidere.twiderex.worker.draft.SaveDraftWorker +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch actual class ComposeAction( private val workManager: WorkManager, + private val repository: AccountRepository, ) { + val scope = CoroutineScope(Dispatchers.IO) actual fun commit( - accountKey: MicroBlogKey, - platformType: PlatformType, data: ComposeData, ) { - val worker = when (platformType) { - PlatformType.Twitter -> TwitterComposeWorker.create( - accountKey = accountKey, - data = data, - ) - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> MastodonComposeWorker.create( - accountKey = accountKey, - data = data, - ) + scope.launch { + repository.activeAccount.firstOrNull()?.toUi()?.let { account -> + val platformType = account.platformType + val accountKey = account.userKey + val worker = when (platformType) { + PlatformType.Twitter -> TwitterComposeWorker.create( + accountKey = accountKey, + data = data, + ) + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> MastodonComposeWorker.create( + accountKey = accountKey, + data = data, + ) + } + workManager + .beginWith(SaveDraftWorker.create(data = data)) + .then(worker) + .then(RemoveDraftWorker.create(draftId = data.draftId)) + .enqueue() + } } - workManager - .beginWith(SaveDraftWorker.create(data = data)) - .then(worker) - .then(RemoveDraftWorker.create(draftId = data.draftId)) - .enqueue() } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt index b97026daf..2121299eb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.action.StatusActions import org.koin.dsl.module actual val actionModule = module { - single { ComposeAction(get()) } + single { ComposeAction(get(), get()) } single { DirectMessageAction(get()) } single { DraftAction(get(), get()) } single { MediaAction(get(), get()) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index 95c0de5ce..c83ddd446 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -26,8 +26,6 @@ import com.twidere.twiderex.model.job.ComposeData expect class ComposeAction { fun commit( - accountKey: MicroBlogKey, - platformType: PlatformType, data: ComposeData, ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index e1ed66fd3..f6d6fc284 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -329,16 +329,11 @@ open class ComposeViewModel( isInVoteMode.value = value } - fun compose() = viewModelScope.launch { - val account = account.lastOrNull() - if (account != null) { - textFieldValue.value.text.let { - composeAction.commit( - account.accountKey, - account.type, - buildComposeData(it) - ) - } + fun compose() { + textFieldValue.value.text.let { + composeAction.commit( + buildComposeData(it) + ) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index 2c3599a6f..d0b5aae18 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -20,14 +20,10 @@ */ package com.twidere.twiderex.action -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData actual class ComposeAction { actual fun commit( - accountKey: MicroBlogKey, - platformType: PlatformType, data: ComposeData, ) { } From ba7291b681d1af3ccb77558134aa15433a559fe2 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Sep 2021 17:48:33 +0800 Subject: [PATCH 165/615] migrate Network from android to common and redesign blur image --- .../component/foundation/BlurImage.kt | 78 ---------- .../component/status/StatusMediaComponent.kt | 8 +- .../http/TwidereNetworkImageLoader.kt | 137 ++++++++++++++++++ .../com/twidere/twiderex/kmp/ImagePainter.kt | 70 ++++----- .../com/twidere/twiderex/kmp/ResLoader.kt | 44 +++++- .../kotlin/com/twidere/twiderex/Consts.kt | 2 + .../twidere/twiderex/component/Resources.kt | 18 ++- .../component/foundation/NetworkImage.kt | 85 +++++++++++ .../com/twidere/twiderex/kmp/ImagePainter.kt | 37 +++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 5 +- .../com/twidere/twiderex/kmp/ResLoader.kt | 5 +- 11 files changed, 356 insertions(+), 133 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt rename android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt => common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt (63%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt index 873db16b7..654883da6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt @@ -43,19 +43,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.core.graphics.drawable.toBitmap -import androidx.core.graphics.drawable.toDrawable import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat -import coil.ImageLoader -import coil.bitmap.BitmapPool -import coil.compose.rememberImagePainter -import coil.memory.MemoryCache -import coil.request.DefaultRequestOptions -import coil.request.Disposable -import coil.request.ImageRequest -import coil.request.ImageResult -import coil.request.SuccessResult -import com.twidere.twiderex.http.TwidereNetworkImageLoader -import com.twidere.twiderex.ui.LocalActiveAccount @Composable fun BlurImage( @@ -89,38 +77,6 @@ fun BlurImage( ) } -@Composable -fun NetworkBlurImage( - data: Any, - modifier: Modifier = Modifier, - contentScale: ContentScale = ContentScale.Crop, - blurRadius: Float = 12f, - bitmapScale: Float = 0.5f, - placeholder: @Composable (() -> Unit)? = null, -) { - val context = LocalContext.current - val accountDetails = LocalActiveAccount.current - val painter = rememberImagePainter( - data = data, - imageLoader = BlurImageLoader( - context, - blurRadius, - bitmapScale, - TwidereNetworkImageLoader( - realImageLoader = buildRealImageLoader(), - context = context, - account = accountDetails - ) - ) - ) - NetworkImage( - data = if (blurRadius != 0f || bitmapScale != 1f) painter else data, - modifier = modifier, - contentScale = contentScale, - placeholder = placeholder - ) -} - private fun applyBlurFilter(src: Bitmap, context: Context, blurRadius: Float, bitmapScale: Float): Bitmap { val rs = RenderScript.create(context.applicationContext) @@ -143,37 +99,3 @@ private fun applyBlurFilter(src: Bitmap, context: Context, blurRadius: Float, bi output.copyTo(scaleBitmap) return scaleBitmap } - -class BlurImageLoader( - private val context: Context, - private val blurRadius: Float, - private val bitmapScale: Float, - private val realPainter: ImageLoader -) : ImageLoader { - override val bitmapPool: BitmapPool - get() = realPainter.bitmapPool - override val defaults: DefaultRequestOptions - get() = realPainter.defaults - override val memoryCache: MemoryCache - get() = realPainter.memoryCache - - override fun enqueue(request: ImageRequest): Disposable { - return realPainter.enqueue(request) - } - - override suspend fun execute(request: ImageRequest): ImageResult { - val result = realPainter.execute(request) - if (result !is SuccessResult) return result - return result.drawable.let { - applyBlurFilter(it.toBitmap(), context, blurRadius, bitmapScale) - }.let { - SuccessResult(it.toDrawable(context.resources), request, result.metadata) - } - } - - override fun shutdown() { - realPainter.shutdown() - } - - override fun newBuilder() = ImageLoader.Builder(context) -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 3f909fa22..ba3c74c00 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -54,8 +54,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.R +import com.twidere.twiderex.component.ImageBlur import com.twidere.twiderex.component.foundation.GridLayout -import com.twidere.twiderex.component.foundation.NetworkBlurImage import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer import com.twidere.twiderex.component.navigation.LocalNavigator @@ -276,8 +276,9 @@ fun StatusMediaPreviewItem( enter = fadeIn(), exit = fadeOut() ) { - NetworkBlurImage( + NetworkImage( data = it, + blur = ImageBlur.Default, modifier = Modifier .fillMaxSize(), placeholder = { @@ -308,8 +309,9 @@ fun StatusMediaPreviewItem( MediaType.video, MediaType.animated_gif -> media.mediaUrl?.let { val previewUrl = media.previewUrl if (sensitive && previewUrl != null) { - NetworkBlurImage( + NetworkImage( data = previewUrl, + blur = ImageBlur.Default, modifier = Modifier .fillMaxSize(), placeholder = { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt new file mode 100644 index 000000000..9df0cd78e --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt @@ -0,0 +1,137 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.http + +import android.content.Context +import android.graphics.drawable.Drawable +import android.view.View +import coil.ImageLoader +import coil.bitmap.BitmapPool +import coil.memory.MemoryCache +import coil.request.DefaultRequestOptions +import coil.request.Disposable +import coil.request.ImageRequest +import coil.request.ImageResult +import coil.target.Target +import coil.target.ViewTarget +import com.twidere.services.http.authorization.Authorization +import com.twidere.twiderex.component.foundation.NetworkImageState +import okhttp3.Headers +import okhttp3.Request +import java.net.URL + +internal class TwidereNetworkImageLoader( + private val realImageLoader: ImageLoader, + private val context: Context, + private val authorization: Authorization, + private val onImageStateChanged: (NetworkImageState) -> Unit = {} +) : ImageLoader { + override val bitmapPool: BitmapPool + get() = realImageLoader.bitmapPool + override val defaults: DefaultRequestOptions + get() = realImageLoader.defaults + override val memoryCache: MemoryCache + get() = realImageLoader.memoryCache + + override fun enqueue(request: ImageRequest): Disposable { + return realImageLoader.enqueue(handleRequest(request)) + } + + override suspend fun execute(request: ImageRequest): ImageResult { + return realImageLoader.execute(handleRequest(request)) + } + + override fun newBuilder(): ImageLoader.Builder { + return ImageLoader.Builder(context) + } + + override fun shutdown() { + realImageLoader.shutdown() + } + + private fun handleRequest(request: ImageRequest): ImageRequest { + val data = request.data + request.newBuilder(request.context) + .apply { + if (authorization.hasAuthorization) { + headers( + headers = Headers.headersOf( + "Authorization", + authorization.getAuthorizationHeader(Request.Builder().url(URL(data.toString())).build()) + ) + ) + } + } + .target( + if (request.target is ViewTarget<*>) { + ViewTargetWrapper(request.target as ViewTarget<*>, onImageStateChanged) + } else { + TargetWrapper(request.target, onImageStateChanged) + } + ) + .build() + return if (authorization.hasAuthorization) { + request.newBuilder( + request.context + ).headers( + headers = Headers.headersOf( + "Authorization", + authorization.getAuthorizationHeader(Request.Builder().url(URL(data.toString())).build()) + ) + ).build() + } else { + request.newBuilder(request.context) + .data(data) + .build() + } + } + + private open class TargetWrapper( + private val target: Target?, + private val onImageStateChanged: (NetworkImageState) -> Unit = {} + ) : Target { + override fun onStart(placeholder: Drawable?) { + super.onStart(placeholder) + target?.onStart(placeholder) + onImageStateChanged.invoke(NetworkImageState.LOADING) + } + + override fun onError(error: Drawable?) { + super.onError(error) + target?.onError(error) + onImageStateChanged.invoke(NetworkImageState.ERROR) + } + + override fun onSuccess(result: Drawable) { + super.onSuccess(result) + target?.onSuccess(result) + onImageStateChanged.invoke(NetworkImageState.SUCCESS) + } + } + + private class ViewTargetWrapper( + private val target: ViewTarget, + onImageStateChanged: (NetworkImageState) -> Unit = {} + ) : TargetWrapper(target, onImageStateChanged), ViewTarget { + override val view: T + get() = target.view + } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt similarity index 63% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 56c383639..40bacf87b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -18,61 +18,55 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.component.foundation +package com.twidere.twiderex.kmp import android.os.Build -import androidx.compose.foundation.Image import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import coil.ImageLoader -import coil.annotation.ExperimentalCoilApi -import coil.compose.ImagePainter import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.GifDecoder import coil.decode.ImageDecoderDecoder +import coil.transform.BlurTransformation import coil.util.CoilUtils -import com.twidere.twiderex.R +import com.twidere.services.http.authorization.Authorization +import com.twidere.services.http.config.HttpConfig +import com.twidere.twiderex.component.ImageBlur +import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.http.TwidereNetworkImageLoader import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig -import com.twidere.twiderex.ui.LocalActiveAccount -@OptIn(ExperimentalCoilApi::class) @Composable -fun NetworkImage( +internal actual fun rememberNetworkImagePainter( data: Any, - modifier: Modifier = Modifier, - contentScale: ContentScale = ContentScale.Crop, - placeholder: @Composable (() -> Unit)? = null, -) { - val painter = if (data is Painter) { - data - } else { - rememberImagePainter( - data = data, - imageLoader = TwidereNetworkImageLoader( - buildRealImageLoader(), - LocalContext.current, - LocalActiveAccount.current - ), - builder = { - crossfade(true) - }, - ) - } - if (painter is ImagePainter && painter.state is ImagePainter.State.Loading) { - placeholder?.invoke() - } - Image( - painter = painter, - modifier = modifier, - contentScale = contentScale, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_network_image) + authorization: Authorization, + httpConfig: HttpConfig, + blur: ImageBlur?, + onImageStateChanged: @Composable (NetworkImageState) -> Unit +): Painter { + val context = LocalContext.current + return rememberImagePainter( + data = data, + imageLoader = TwidereNetworkImageLoader( + buildRealImageLoader(), + context, + authorization = authorization, + ), + builder = { + crossfade(true) + if (blur != null) { + transformations( + BlurTransformation( + context = context, + radius = blur.blurRadius, + sampling = blur.bitmapScale + ) + ) + } + }, ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f3e60c7a5..fabce421f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -26,7 +26,8 @@ import androidx.compose.ui.graphics.painter.Painter import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.SvgDecoder -import com.twidere.twiderex.component.painterResource +import coil.transform.BlurTransformation +import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -42,17 +43,46 @@ actual class ResLoader( } @Composable - actual fun getSvg(res: FileResource): Painter { + actual fun getSvg(res: FileResource, blur: ImageBlur?): Painter { val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" return rememberImagePainter( - data, - LocalImageLoader.current - .newBuilder().componentRegistry { add(SvgDecoder(context)) }.build() + data = data, + imageLoader = LocalImageLoader.current.newBuilder() + .componentRegistry { add(SvgDecoder(context)) } + .build(), + builder = { + if (blur != null) { + transformations( + BlurTransformation( + context = context, + radius = blur.blurRadius, + sampling = blur.bitmapScale + ) + ) + } + } ) } @Composable - actual fun getImage(res: ImageResource): Painter { - return painterResource(res.drawableResId) + actual fun getImage(res: ImageResource, blur: ImageBlur?): Painter { + val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" + return rememberImagePainter( + data = data, + imageLoader = LocalImageLoader.current.newBuilder() + .componentRegistry { add(SvgDecoder(context)) } + .build(), + builder = { + if (blur != null) { + transformations( + BlurTransformation( + context = context, + radius = blur.blurRadius, + sampling = blur.bitmapScale + ) + ) + } + } + ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt index f7976e425..7de2ce975 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt @@ -29,3 +29,5 @@ val twitterHosts = listOf( "http://mobile.twitter.com", "http://www.twitter.com", ) + +internal const val twitterTonApiHost = "ton.twitter.com" diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index 2e8f9a346..3c49f97ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -41,10 +41,22 @@ fun stringResource(res: StringResource): String { * res: FileResource:svg, ImageResource */ @Composable -fun painterResource(res: Any): Painter { +fun painterResource(res: Any, blur: ImageBlur? = null): Painter { return when (res) { - is FileResource -> LocalResLoader.current.getSvg(res) - is ImageResource -> LocalResLoader.current.getImage(res) + is FileResource -> LocalResLoader.current.getSvg(res, blur) + is ImageResource -> LocalResLoader.current.getImage(res, blur) else -> throw NotImplementedError() } } + +class ImageBlur( + val blurRadius: Float, + val bitmapScale: Float, +) { + companion object { + val Default = ImageBlur( + blurRadius = 12f, + bitmapScale = 0.5f + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt new file mode 100644 index 000000000..6b5289721 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation + +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import com.twidere.services.http.authorization.EmptyAuthorization +import com.twidere.twiderex.MR +import com.twidere.twiderex.component.ImageBlur +import com.twidere.twiderex.component.stringResource +import com.twidere.twiderex.kmp.rememberNetworkImagePainter +import com.twidere.twiderex.preferences.LocalHttpConfig +import com.twidere.twiderex.twitterTonApiHost + +@Composable +fun NetworkImage( + data: Any, + modifier: Modifier = Modifier, + contentScale: ContentScale = ContentScale.Crop, + blur: ImageBlur? = null, + placeholder: @Composable (() -> Unit)? = null, +) { + val painter = if (data is Painter) { + data + } else { + val httpConfig = LocalHttpConfig.current + val auth = if (data is Uri && twitterTonApiHost == data.host) { + TODO("Waiting for LocalActiveAccount") + // (account.credentials as OAuthCredentials).let { + // OAuth1Authorization( + // consumerKey = it.consumer_key, + // consumerSecret = it.consumer_secret, + // accessToken = it.access_token, + // accessSecret = it.access_token_secret, + // ) + // } + } else { + EmptyAuthorization() + } + rememberNetworkImagePainter( + data = data, + httpConfig = httpConfig, + authorization = auth, + blur = blur, + onImageStateChanged = { + if (it == NetworkImageState.LOADING) placeholder?.invoke() + } + ) + } + Image( + painter = painter, + modifier = modifier, + contentScale = contentScale, + contentDescription = stringResource(MR.strings.accessibility_common_network_image) + ) +} + +internal enum class NetworkImageState { + EMPTY, + LOADING, + SUCCESS, + ERROR +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt new file mode 100644 index 000000000..8fd3cbe85 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -0,0 +1,37 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter +import com.twidere.services.http.authorization.Authorization +import com.twidere.services.http.config.HttpConfig +import com.twidere.twiderex.component.ImageBlur +import com.twidere.twiderex.component.foundation.NetworkImageState + +@Composable +internal expect fun rememberNetworkImagePainter( + data: Any, + authorization: Authorization, + httpConfig: HttpConfig, + blur: ImageBlur?, + onImageStateChanged: @Composable (NetworkImageState) -> Unit +): Painter diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f2cea261c..2b4008fc4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.kmp import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter +import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -30,8 +31,8 @@ expect class ResLoader { fun getString(res: StringResource, vararg args: Any): String @Composable - fun getSvg(res: FileResource): Painter + fun getSvg(res: FileResource, blur: ImageBlur?): Painter @Composable - fun getImage(res: ImageResource): Painter + fun getImage(res: ImageResource, blur: ImageBlur?): Painter } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f24a62a39..44230ff9a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.kmp import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -36,12 +37,12 @@ actual class ResLoader { } @Composable - actual fun getSvg(res: FileResource): Painter { + actual fun getSvg(res: FileResource, blur: ImageBlur?): Painter { return painterResource(res.filePath) } @Composable - actual fun getImage(res: ImageResource): Painter { + actual fun getImage(res: ImageResource, blur: ImageBlur?): Painter { return painterResource(res.filePath) } } From 04fe8ded548ecc38917b1ed13cc444e1e493a4b0 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Sep 2021 18:40:59 +0800 Subject: [PATCH 166/615] opt code in android ImagePainter --- .../component/status/StatusMediaComponent.kt | 4 +- .../http/TwidereNetworkImageLoader.kt | 137 ------------------ .../com/twidere/twiderex/kmp/ImagePainter.kt | 52 ++++--- .../twidere/twiderex/component/Resources.kt | 6 +- .../component/foundation/NetworkImage.kt | 11 +- .../com/twidere/twiderex/kmp/ImagePainter.kt | 2 +- 6 files changed, 46 insertions(+), 166 deletions(-) delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index ba3c74c00..5778ee16a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -278,7 +278,7 @@ fun StatusMediaPreviewItem( ) { NetworkImage( data = it, - blur = ImageBlur.Default, + blur = ImageBlur.Sensitive, modifier = Modifier .fillMaxSize(), placeholder = { @@ -311,7 +311,7 @@ fun StatusMediaPreviewItem( if (sensitive && previewUrl != null) { NetworkImage( data = previewUrl, - blur = ImageBlur.Default, + blur = ImageBlur.Sensitive, modifier = Modifier .fillMaxSize(), placeholder = { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt deleted file mode 100644 index 9df0cd78e..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.http - -import android.content.Context -import android.graphics.drawable.Drawable -import android.view.View -import coil.ImageLoader -import coil.bitmap.BitmapPool -import coil.memory.MemoryCache -import coil.request.DefaultRequestOptions -import coil.request.Disposable -import coil.request.ImageRequest -import coil.request.ImageResult -import coil.target.Target -import coil.target.ViewTarget -import com.twidere.services.http.authorization.Authorization -import com.twidere.twiderex.component.foundation.NetworkImageState -import okhttp3.Headers -import okhttp3.Request -import java.net.URL - -internal class TwidereNetworkImageLoader( - private val realImageLoader: ImageLoader, - private val context: Context, - private val authorization: Authorization, - private val onImageStateChanged: (NetworkImageState) -> Unit = {} -) : ImageLoader { - override val bitmapPool: BitmapPool - get() = realImageLoader.bitmapPool - override val defaults: DefaultRequestOptions - get() = realImageLoader.defaults - override val memoryCache: MemoryCache - get() = realImageLoader.memoryCache - - override fun enqueue(request: ImageRequest): Disposable { - return realImageLoader.enqueue(handleRequest(request)) - } - - override suspend fun execute(request: ImageRequest): ImageResult { - return realImageLoader.execute(handleRequest(request)) - } - - override fun newBuilder(): ImageLoader.Builder { - return ImageLoader.Builder(context) - } - - override fun shutdown() { - realImageLoader.shutdown() - } - - private fun handleRequest(request: ImageRequest): ImageRequest { - val data = request.data - request.newBuilder(request.context) - .apply { - if (authorization.hasAuthorization) { - headers( - headers = Headers.headersOf( - "Authorization", - authorization.getAuthorizationHeader(Request.Builder().url(URL(data.toString())).build()) - ) - ) - } - } - .target( - if (request.target is ViewTarget<*>) { - ViewTargetWrapper(request.target as ViewTarget<*>, onImageStateChanged) - } else { - TargetWrapper(request.target, onImageStateChanged) - } - ) - .build() - return if (authorization.hasAuthorization) { - request.newBuilder( - request.context - ).headers( - headers = Headers.headersOf( - "Authorization", - authorization.getAuthorizationHeader(Request.Builder().url(URL(data.toString())).build()) - ) - ).build() - } else { - request.newBuilder(request.context) - .data(data) - .build() - } - } - - private open class TargetWrapper( - private val target: Target?, - private val onImageStateChanged: (NetworkImageState) -> Unit = {} - ) : Target { - override fun onStart(placeholder: Drawable?) { - super.onStart(placeholder) - target?.onStart(placeholder) - onImageStateChanged.invoke(NetworkImageState.LOADING) - } - - override fun onError(error: Drawable?) { - super.onError(error) - target?.onError(error) - onImageStateChanged.invoke(NetworkImageState.ERROR) - } - - override fun onSuccess(result: Drawable) { - super.onSuccess(result) - target?.onSuccess(result) - onImageStateChanged.invoke(NetworkImageState.SUCCESS) - } - } - - private class ViewTargetWrapper( - private val target: ViewTarget, - onImageStateChanged: (NetworkImageState) -> Unit = {} - ) : TargetWrapper(target, onImageStateChanged), ViewTarget { - override val view: T - get() = target.view - } -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 40bacf87b..d6c4d5834 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import coil.ImageLoader +import coil.annotation.ExperimentalCoilApi import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.GifDecoder @@ -35,9 +36,10 @@ import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig import com.twidere.twiderex.component.ImageBlur import com.twidere.twiderex.component.foundation.NetworkImageState -import com.twidere.twiderex.http.TwidereNetworkImageLoader import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig +import okhttp3.Request +import java.net.URL @Composable internal actual fun rememberNetworkImagePainter( @@ -45,16 +47,12 @@ internal actual fun rememberNetworkImagePainter( authorization: Authorization, httpConfig: HttpConfig, blur: ImageBlur?, - onImageStateChanged: @Composable (NetworkImageState) -> Unit + onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val context = LocalContext.current return rememberImagePainter( data = data, - imageLoader = TwidereNetworkImageLoader( - buildRealImageLoader(), - context, - authorization = authorization, - ), + imageLoader = buildImageLoader(), builder = { crossfade(true) if (blur != null) { @@ -66,32 +64,44 @@ internal actual fun rememberNetworkImagePainter( ) ) } + listener( + onSuccess = { _, _ -> onImageStateChanged(NetworkImageState.SUCCESS) }, + onError = { _, _ -> onImageStateChanged(NetworkImageState.ERROR) }, + onStart = { onImageStateChanged(NetworkImageState.LOADING) } + ) + if (authorization.hasAuthorization) { + addHeader( + "Authorization", + authorization.getAuthorizationHeader( + Request.Builder() + .url(URL(data.toString())) + .build() + ) + ) + } }, ) } +@OptIn(ExperimentalCoilApi::class) @Composable -fun buildRealImageLoader(): ImageLoader { +private fun buildImageLoader(): ImageLoader { val context = LocalContext.current val httpConfig = LocalHttpConfig.current - return ( - if (httpConfig.proxyConfig.enable && - httpConfig.proxyConfig.server.isNotEmpty() - ) { - LocalImageLoader.current - .newBuilder() - .callFactory( + return LocalImageLoader.current + .newBuilder() + .apply { + if (httpConfig.proxyConfig.enable && + httpConfig.proxyConfig.server.isNotEmpty() + ) { + callFactory( TwidereServiceFactory.createHttpClientFactory() .createHttpClientBuilder() .cache(CoilUtils.createDefaultCache(context)) .build() ) - .build() - } else { - LocalImageLoader.current - } - ).newBuilder() - .componentRegistry { + } + }.componentRegistry { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { add(ImageDecoderDecoder(context)) } else { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index 3c49f97ef..c66a0aeef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -54,9 +54,9 @@ class ImageBlur( val bitmapScale: Float, ) { companion object { - val Default = ImageBlur( - blurRadius = 12f, - bitmapScale = 0.5f + val Sensitive = ImageBlur( + blurRadius = 25f, + bitmapScale = 0.4f ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 6b5289721..995cb4cae 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -23,6 +23,8 @@ package com.twidere.twiderex.component.foundation import android.net.Uri import androidx.compose.foundation.Image import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale @@ -42,6 +44,9 @@ fun NetworkImage( blur: ImageBlur? = null, placeholder: @Composable (() -> Unit)? = null, ) { + val state = remember { + mutableStateOf(NetworkImageState.LOADING) + } val painter = if (data is Painter) { data } else { @@ -65,10 +70,13 @@ fun NetworkImage( authorization = auth, blur = blur, onImageStateChanged = { - if (it == NetworkImageState.LOADING) placeholder?.invoke() + state.value = it } ) } + if (state.value == NetworkImageState.LOADING) { + placeholder?.invoke() + } Image( painter = painter, modifier = modifier, @@ -78,7 +86,6 @@ fun NetworkImage( } internal enum class NetworkImageState { - EMPTY, LOADING, SUCCESS, ERROR diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 8fd3cbe85..a22923784 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -33,5 +33,5 @@ internal expect fun rememberNetworkImagePainter( authorization: Authorization, httpConfig: HttpConfig, blur: ImageBlur?, - onImageStateChanged: @Composable (NetworkImageState) -> Unit + onImageStateChanged: (NetworkImageState) -> Unit ): Painter From 67ab13188135daf9337aeb6695829011342b135c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 12:55:09 +0800 Subject: [PATCH 167/615] revert vm usage --- .../twiderex/component/TimelineComponent.kt | 57 ++++++++++--------- .../twiderex/component/UserComponent.kt | 7 ++- .../twiderex/extensions/FlowExtensions.kt | 14 +++++ .../twiderex/viewmodel/MediaViewModel.kt | 3 +- .../twiderex/viewmodel/StatusViewModel.kt | 9 ++- .../compose/ComposeSearchUserViewModel.kt | 7 ++- .../viewmodel/compose/ComposeViewModel.kt | 17 +++--- .../MastodonComposeSearchHashtagViewModel.kt | 7 ++- .../viewmodel/dm/DMConversationViewModel.kt | 7 ++- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 6 +- .../dm/DMNewConversationViewModel.kt | 6 +- .../lists/ListsAddMemberViewModel.kt | 3 +- .../lists/ListsSearchUserViewModel.kt | 7 ++- .../viewmodel/lists/ListsTimelineViewModel.kt | 7 ++- .../viewmodel/lists/ListsUserViewModel.kt | 8 ++- .../viewmodel/lists/ListsViewModel.kt | 14 +++-- .../mastodon/MastodonHashtagViewModel.kt | 7 ++- .../MastodonSearchHashtagViewModel.kt | 7 ++- .../viewmodel/search/SearchInputViewModel.kt | 3 +- .../viewmodel/search/SearchSaveViewModel.kt | 3 +- .../viewmodel/search/SearchTweetsViewModel.kt | 9 ++- .../viewmodel/search/SearchUserViewModel.kt | 7 ++- .../settings/AccountNotificationViewModel.kt | 6 +- .../viewmodel/settings/LayoutViewModel.kt | 3 +- .../timeline/HomeTimelineViewModel.kt | 8 ++- .../timeline/MentionsTimelineViewModel.kt | 8 ++- .../timeline/NotificationTimelineViewModel.kt | 8 ++- .../viewmodel/timeline/TimelineViewModel.kt | 7 ++- .../mastodon/FederatedTimelineViewModel.kt | 8 ++- .../mastodon/LocalTimelineViewModel.kt | 8 ++- .../viewmodel/trend/TrendViewModel.kt | 7 ++- .../search/TwitterSearchMediaViewModel.kt | 7 ++- .../twitter/user/TwitterUserViewModel.kt | 4 +- .../viewmodel/user/FollowersViewModel.kt | 7 ++- .../viewmodel/user/FollowingViewModel.kt | 7 ++- .../user/UserFavouriteTimelineViewModel.kt | 7 ++- .../user/UserMediaTimelineViewModel.kt | 9 ++- .../viewmodel/user/UserTimelineViewModel.kt | 29 ++++++---- .../twiderex/viewmodel/user/UserViewModel.kt | 5 +- 39 files changed, 230 insertions(+), 123 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 0a4689468..4e5230c03 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -33,7 +34,11 @@ import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.refreshOrRetry +import com.twidere.twiderex.viewmodel.timeline.TimelineScrollState import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter @Composable fun TimelineComponent( @@ -50,38 +55,38 @@ fun TimelineComponent( }, refreshIndicatorPadding = contentPadding ) { - // val scrollState by viewModel.timelineScrollState.observeAsState( - // initial = TimelineScrollState.Zero - // ) + val scrollState by viewModel.timelineScrollState.observeAsState( + initial = TimelineScrollState.Zero + ) val listState = rememberLazyListState() - // LaunchedEffect(Unit) { - // snapshotFlow { scrollState } - // .distinctUntilChanged() - // .collect { - // listState.scrollToItem( - // it.firstVisibleItemIndex, - // it.firstVisibleItemScrollOffset - // ) - // } - // } + LaunchedEffect(Unit) { + snapshotFlow { scrollState } + .distinctUntilChanged() + .collect { + listState.scrollToItem( + it.firstVisibleItemIndex, + it.firstVisibleItemScrollOffset + ) + } + } if (items.itemCount > 0) { LaunchedEffect(lazyListController) { lazyListController?.listState = listState } } - // LaunchedEffect(Unit) { - // snapshotFlow { listState.isScrollInProgress } - // .distinctUntilChanged() - // .filter { !it } - // .collect { - // viewModel.saveScrollState( - // TimelineScrollState( - // firstVisibleItemIndex = listState.firstVisibleItemIndex, - // firstVisibleItemScrollOffset = listState.firstVisibleItemScrollOffset, - // ) - // ) - // } - // } + LaunchedEffect(Unit) { + snapshotFlow { listState.isScrollInProgress } + .distinctUntilChanged() + .filter { !it } + .collect { + viewModel.saveScrollState( + TimelineScrollState( + firstVisibleItemIndex = listState.firstVisibleItemIndex, + firstVisibleItemScrollOffset = listState.firstVisibleItemScrollOffset, + ) + ) + } + } LazyUiStatusList( items = items, state = listState, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index e79f24993..6e04aa848 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -65,6 +65,7 @@ import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -229,10 +230,10 @@ fun UserStatusTimeline( viewModel: UserViewModel, ) { val user by viewModel.user.observeAsState(initial = null) + var excludeReplies by rememberSaveable { mutableStateOf(false) } val timelineViewModel: UserTimelineViewModel = getViewModel { - parametersOf(userKey) + parametersOf(userKey, excludeReplies) } - val excludeReplies by timelineViewModel.excludeReplies.observeAsState(initial = false) val timelineSource = timelineViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -243,7 +244,7 @@ fun UserStatusTimeline( user?.let { user -> item { UserStatusTimelineFilter(user, excludeReplies) { - timelineViewModel.setExcludeReplies(it) + excludeReplies = it } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index 73aba9449..cc0b94b09 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -24,10 +24,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.stateIn import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.repeatOnLifecycle import moe.tlaster.precompose.ui.LocalLifecycleOwner @@ -51,3 +54,14 @@ fun Flow.flowWithLifecycle( } close() } + +fun Flow.asStateIn( + scope: CoroutineScope, + initialValue: T +): Flow { + return stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = initialValue + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 902b8297c..2960336b9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.action.MediaAction +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.repository.AccountRepository @@ -41,7 +42,7 @@ class MediaViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index f55655cb4..afe91491a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.viewmodel +import androidx.paging.cachedIn +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository @@ -27,6 +29,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class StatusViewModel( private val statusRepository: StatusRepository, @@ -34,7 +37,7 @@ class StatusViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { @@ -44,7 +47,7 @@ class StatusViewModel( } else { emptyFlow() } - } + }.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -60,6 +63,6 @@ class StatusViewModel( } else { emptyFlow() } - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 0ecb6a34b..b498c001c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -22,8 +22,10 @@ package com.twidere.twiderex.viewmodel.compose import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,12 +35,13 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ComposeSearchUserViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val text = MutableStateFlow("") @@ -63,5 +66,5 @@ class ComposeSearchUserViewModel( } ?: emptyFlow() } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index f6d6fc284..95f7d51a7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.text.input.TextFieldValue import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.ComposeAction import com.twidere.twiderex.action.DraftAction +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.extensions.getTextAfterSelection import com.twidere.twiderex.extensions.getTextBeforeSelection import com.twidere.twiderex.kmp.LocationProvider @@ -160,7 +161,7 @@ open class ComposeViewModel( val composeType: ComposeType, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } open val draftId: String = UUID.randomUUID().toString() @@ -175,7 +176,7 @@ open class ComposeViewModel( emptyFlow() } } ?: emptyFlow() - } + }.asStateIn(viewModelScope, emptyList()) } val draftCount by lazy { @@ -203,7 +204,7 @@ open class ComposeViewModel( } else { emptyList() } - } + }.asStateIn(viewModelScope, emptyList()) } val loadingReplyUser = MutableStateFlow(false) @@ -231,7 +232,7 @@ open class ComposeViewModel( } else { emptyList() } - } + }.asStateIn(viewModelScope, emptyList()) } val voteState = MutableStateFlow(null) @@ -244,10 +245,10 @@ open class ComposeViewModel( val images = MutableStateFlow>(emptyList()) val canSend = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - + .asStateIn(viewModelScope, false) val canSaveDraft = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - + .asStateIn(viewModelScope, false) val locationEnabled = MutableStateFlow(false) val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) @@ -293,7 +294,7 @@ open class ComposeViewModel( } else { flowOf(null) } - } + }.asStateIn(viewModelScope, null) } fun setText(value: TextFieldValue) { @@ -379,7 +380,7 @@ open class ComposeViewModel( PlatformType.Mastodon -> 4 else -> 4 } - } + }.asStateIn(viewModelScope, 4) } fun trackingLocation() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 446cc5009..60b7682e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -22,8 +22,10 @@ package com.twidere.twiderex.viewmodel.compose import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,12 +35,13 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonComposeSearchHashtagViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val text = MutableStateFlow("") @@ -62,5 +65,5 @@ class MastodonComposeSearchHashtagViewModel( } ?: emptyFlow() } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 915d11a41..bf55a8913 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -20,21 +20,24 @@ */ package com.twidere.twiderex.viewmodel.dm +import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class DMConversationViewModel( private val repository: DirectMessageRepository, private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -47,6 +50,6 @@ class DMConversationViewModel( lookupService = account.service as LookupService ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index b0ca2e481..450364953 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -20,9 +20,11 @@ */ package com.twidere.twiderex.viewmodel.dm +import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData @@ -48,7 +50,7 @@ class DMEventViewModel( private val conversationKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -74,7 +76,7 @@ class DMEventViewModel( lookupService = account.service as LookupService ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } // input diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 0b00438be..c2b782772 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -22,8 +22,10 @@ package com.twidere.twiderex.viewmodel.dm import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource @@ -45,7 +47,7 @@ class DMNewConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val input = MutableStateFlow("") @@ -70,7 +72,7 @@ class DMNewConversationViewModel( } ?: emptyFlow() } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) fun createNewConversation(receiver: UiUser, onResult: (key: MicroBlogKey?) -> Unit) { viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt index f8e1dc008..6c8a3e073 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -22,6 +22,7 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification @@ -41,7 +42,7 @@ class ListsAddMemberViewModel( private val listId: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 779fee8e5..1ba158d9f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -22,8 +22,10 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,13 +35,14 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsSearchUserViewModel( private val accountRepository: AccountRepository, following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val text = MutableStateFlow("") @@ -65,5 +68,5 @@ class ListsSearchUserViewModel( } ?: emptyFlow() } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index d115ac5bb..a2d126d80 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -20,7 +20,9 @@ */ package com.twidere.twiderex.viewmodel.lists +import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -28,6 +30,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class ListsTimelineViewModel( repository: TimelineRepository, @@ -35,7 +38,7 @@ class ListsTimelineViewModel( listKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -48,6 +51,6 @@ class ListsTimelineViewModel( service = it.service as TimelineService ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index e918982ac..5440af03d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -21,7 +21,9 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.PagingData +import androidx.paging.cachedIn import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository @@ -41,7 +43,7 @@ class ListsUserViewModel( private val viewMembers: Boolean = true, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -54,7 +56,7 @@ class ListsUserViewModel( listId = listId ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) @@ -67,7 +69,7 @@ class ListsUserViewModel( listId = listId ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } override val source: Flow> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 153d57b65..a3534ab97 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -20,8 +20,10 @@ */ package com.twidere.twiderex.viewmodel.lists +import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.services.microblog.ListsService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList @@ -45,7 +47,7 @@ class ListsViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -57,7 +59,7 @@ class ListsViewModel( service = account.service as ListsService ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) @@ -68,7 +70,7 @@ class ListsViewModel( it.filter { it.isOwner(account.user.userId) } } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) @@ -79,7 +81,7 @@ class ListsViewModel( pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } } } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } @@ -118,7 +120,7 @@ class ListsCreateViewModel( private val onResult: (success: Boolean, list: UiList?) -> Unit ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } fun createList( @@ -147,7 +149,7 @@ class ListsModifyViewModel( private val listKey: MicroBlogKey, ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val editName = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 5ceb602cc..2f60d096f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -20,13 +20,16 @@ */ package com.twidere.twiderex.viewmodel.mastodon +import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonHashtagViewModel( private val repository: TimelineRepository, @@ -34,7 +37,7 @@ class MastodonHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -47,6 +50,6 @@ class MastodonHashtagViewModel( service = it.service as MastodonService ) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 40538743e..f86888eb0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -22,21 +22,24 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class MastodonSearchHashtagViewModel( private val accountRepository: AccountRepository, keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -55,6 +58,6 @@ class MastodonSearchHashtagViewModel( ) }.flow } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 52e4bf8f1..dde1157c7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.search +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository @@ -37,7 +38,7 @@ class SearchInputViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 82decd3df..6fce97ac3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.search +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow @@ -34,7 +35,7 @@ class SearchSaveViewModel( private val content: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 5e4ed99f2..39929427f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -20,9 +20,11 @@ */ package com.twidere.twiderex.viewmodel.search +import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository @@ -31,6 +33,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchTweetsViewModel( val database: CacheDatabase, @@ -38,7 +41,7 @@ class SearchTweetsViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -50,8 +53,8 @@ class SearchTweetsViewModel( database, account.accountKey, account.service as SearchService - ).pager().flow.map { it.map { it.status } } + ).pager().flow.map { it.map { it.status } }.cachedIn(viewModelScope) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index 0b962d421..cc146c0e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -22,14 +22,17 @@ package com.twidere.twiderex.viewmodel.search import androidx.paging.Pager import androidx.paging.PagingConfig +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class SearchUserViewModel( private val accountRepository: AccountRepository, @@ -37,7 +40,7 @@ class SearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -58,6 +61,6 @@ class SearchUserViewModel( ) }.flow } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 1d5ec2616..13db48818 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.settings +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flatMapLatest @@ -34,7 +35,7 @@ class AccountNotificationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val preferences by lazy { @@ -42,12 +43,13 @@ class AccountNotificationViewModel( it?.let { accountRepository.getAccountPreferences(it.accountKey) } - } + }.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) val isNotificationEnabled by lazy { preferences.flatMapLatest { it?.isNotificationEnabled ?: flowOf(false) } + .asStateIn(viewModelScope, false) } fun setIsNotificationEnabled(value: Boolean) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 54dcafad7..467bcfc7a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.settings +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.lastOrNull @@ -35,7 +36,7 @@ class LayoutViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } fun updateHomeMenu(oldIndex: Int, newIndex: Int, menus: List) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 011d7444d..4c031e7d7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -24,10 +24,12 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.mapLatest +import moe.tlaster.precompose.viewmodel.viewModelScope class HomeTimelineViewModel( dataStore: DataStore, @@ -35,7 +37,7 @@ class HomeTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -48,7 +50,7 @@ class HomeTimelineViewModel( database, ) } - } + }.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -57,6 +59,6 @@ class HomeTimelineViewModel( it?.let { "${it.accountKey}_home" } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index fb1b7bbd3..7e0ab938d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -24,11 +24,13 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope class MentionsTimelineViewModel( dataStore: DataStore, @@ -37,7 +39,7 @@ class MentionsTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { @@ -57,13 +59,13 @@ class MentionsTimelineViewModel( } ) } - } + }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { account.map { it?.let { "${it.accountKey}_mentions" } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 212c117c2..7dca6c3dd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -24,11 +24,13 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope class NotificationTimelineViewModel( dataStore: DataStore, @@ -37,7 +39,7 @@ class NotificationTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { @@ -57,13 +59,13 @@ class NotificationTimelineViewModel( } ) } - } + }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { account.map { it?.let { "${it.accountKey}_notification" } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index b7641f658..7b18d9ead 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -24,7 +24,9 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey +import androidx.paging.cachedIn import com.twidere.twiderex.defaultLoadCount +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager @@ -54,12 +56,13 @@ abstract class TimelineViewModel( it?.let { emitAll(it.pager().toUi()) } - } + }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) val loadingBetween: Flow> by lazy { pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + .asStateIn(viewModelScope, emptyList()) } @OptIn(ExperimentalCoroutinesApi::class) @@ -76,7 +79,7 @@ abstract class TimelineViewModel( firstVisibleItemScrollOffset = firstVisibleItemScrollOffset, ) } - } + }.asStateIn(viewModelScope, TimelineScrollState.Zero) } fun loadBetween( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 0a5f59c9b..9aa551784 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -24,10 +24,12 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope class FederatedTimelineViewModel( dataStore: DataStore, @@ -35,7 +37,7 @@ class FederatedTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { @@ -47,7 +49,7 @@ class FederatedTimelineViewModel( database, ) } - } + }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { @@ -55,6 +57,6 @@ class FederatedTimelineViewModel( it?.let { "${it.accountKey}_federated" } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index e63bc0f2d..79d15e4a8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -24,10 +24,12 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import kotlinx.coroutines.flow.map +import moe.tlaster.precompose.viewmodel.viewModelScope class LocalTimelineViewModel( dataStore: DataStore, @@ -35,7 +37,7 @@ class LocalTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } override val pagingMediator by lazy { @@ -47,7 +49,7 @@ class LocalTimelineViewModel( database, ) } - } + }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { @@ -55,6 +57,6 @@ class LocalTimelineViewModel( it?.let { "${it.accountKey}_local" } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index d14d8821d..a1a23ea2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -20,20 +20,23 @@ */ package com.twidere.twiderex.viewmodel.trend +import androidx.paging.cachedIn import com.twidere.services.microblog.TrendService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class TrendViewModel( private val repository: TrendRepository, private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -47,6 +50,6 @@ class TrendViewModel( } else { emptyFlow() } - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index e370b5c7d..0a9fe7317 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -20,13 +20,16 @@ */ package com.twidere.twiderex.viewmodel.twitter.search +import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterSearchMediaViewModel( private val repository: SearchRepository, @@ -34,7 +37,7 @@ class TwitterSearchMediaViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -43,6 +46,6 @@ class TwitterSearchMediaViewModel( it?.let { repository.media(keyword, it.accountKey, it.service as SearchService) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 2c1b77416..9e68292d8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.viewmodel.twitter.user import com.twidere.services.microblog.LookupService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository @@ -28,6 +29,7 @@ import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterUserViewModel( private val repository: UserRepository, @@ -37,7 +39,7 @@ class TwitterUserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val error = MutableStateFlow(null) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 74f1704a2..0e5badc5e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -20,13 +20,16 @@ */ package com.twidere.twiderex.viewmodel.user +import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( private val accountRepository: AccountRepository, @@ -34,7 +37,7 @@ class FollowersViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -43,6 +46,6 @@ class FollowersViewModel( it?.let { account -> repository.followers(userKey, account.service as RelationshipService) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 195f2d0f7..54005cd84 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -20,13 +20,16 @@ */ package com.twidere.twiderex.viewmodel.user +import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( private val accountRepository: AccountRepository, @@ -34,7 +37,7 @@ class FollowingViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -43,6 +46,6 @@ class FollowingViewModel( it?.let { account -> repository.following(userKey, account.service as RelationshipService) } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 6dbc60d0a..3b37c468a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -20,7 +20,9 @@ */ package com.twidere.twiderex.viewmodel.user +import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -28,6 +30,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserFavouriteTimelineViewModel( private val repository: TimelineRepository, @@ -35,7 +38,7 @@ class UserFavouriteTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -51,6 +54,6 @@ class UserFavouriteTimelineViewModel( } else { emptyFlow() } - } + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 96b03b7f5..d9658d3ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -22,10 +22,12 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.PagingConfig import androidx.paging.PagingData +import androidx.paging.cachedIn import androidx.paging.flatMap import androidx.paging.map import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus @@ -38,6 +40,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserMediaTimelineViewModel( private val database: CacheDatabase, @@ -45,7 +48,7 @@ class UserMediaTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - repository.activeAccount + repository.activeAccount.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) @@ -62,7 +65,7 @@ class UserMediaTimelineViewModel( pagingData.map { it.status } - }.map { + }.cachedIn(viewModelScope).map { it.flatMap { it.media.map { media -> media to it } } @@ -81,6 +84,6 @@ class UserMediaTimelineViewModel( service = it.service as TimelineService ) } - } + }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 8ec14dc56..d0699dad4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -20,16 +20,19 @@ */ package com.twidere.twiderex.viewmodel.user +import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flattenMerge -import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope class UserTimelineViewModel( private val repository: TimelineRepository, @@ -37,9 +40,9 @@ class UserTimelineViewModel( userKey: MicroBlogKey, ) : ViewModel() { private val _excludeReplies = MutableStateFlow(false) - val excludeReplies = _excludeReplies + val excludeReplies = _excludeReplies.asStateIn(viewModelScope, false) private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } fun setExcludeReplies(value: Boolean) { @@ -48,13 +51,17 @@ class UserTimelineViewModel( @OptIn(FlowPreview::class) val source by lazy { - combine(account.mapNotNull { it }, _excludeReplies) { account, excludeReplies -> - repository.userTimeline( - userKey = userKey, - accountKey = account.accountKey, - service = account.service as TimelineService, - exclude_replies = excludeReplies, - ) - }.flattenMerge() + combine(account, _excludeReplies) { account, excludeReplies -> + if (account != null) { + repository.userTimeline( + userKey = userKey, + accountKey = account.accountKey, + service = account.service as TimelineService, + exclude_replies = excludeReplies, + ) + } else { + emptyFlow() + } + }.flattenMerge().cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index ff4c3aded..3215b52ff 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository @@ -43,7 +44,7 @@ class UserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount + accountRepository.activeAccount.asStateIn(viewModelScope, null) } val refreshing = MutableStateFlow(false) @@ -53,7 +54,7 @@ class UserViewModel( val isMe by lazy { account.map { userKey == it?.accountKey - } + }.asStateIn(viewModelScope, false) } fun refresh() = viewModelScope.launch { From 893a194424beb2825b9e3072d8165fbdfa85d432 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 13:01:08 +0800 Subject: [PATCH 168/615] fix vm creation --- .../twidere/twiderex/di/ext/ViewModelExt.kt | 2 +- .../timeline/HomeTimelineViewModel.kt | 1 + .../user/UserFavouriteTimelineViewModel.kt | 20 ++++++++----------- .../user/UserMediaTimelineViewModel.kt | 9 ++++----- .../viewmodel/user/UserTimelineViewModel.kt | 20 ++++++++----------- .../twiderex/viewmodel/user/UserViewModel.kt | 11 +++++++--- 6 files changed, 30 insertions(+), 33 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt index ec42863ed..0b2b49a69 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt @@ -73,7 +73,7 @@ fun ViewModelStoreOwner.getViewModel( clazz: KClass, parameters: ParametersDefinition? = null, ): T { - return this.viewModelStore.getViewModel(qualifier.toString(), clazz) { + return this.viewModelStore.getViewModel(qualifier?.value ?: clazz.toString(), clazz) { KoinPlatformTools.defaultContext().get().get(clazz, qualifier, parameters) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 4c031e7d7..f32b316cf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.mapLatest import moe.tlaster.precompose.viewmodel.viewModelScope diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 3b37c468a..7a3512aa8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -27,8 +27,8 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -43,17 +43,13 @@ class UserFavouriteTimelineViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - if (it != null) { - repository.favouriteTimeline( - userKey = userKey, - accountKey = it.accountKey, - platformType = it.type, - service = it.service as TimelineService - ) - } else { - emptyFlow() - } + account.mapNotNull { it }.flatMapLatest { + repository.favouriteTimeline( + userKey = userKey, + accountKey = it.accountKey, + platformType = it.type, + service = it.service as TimelineService + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index d9658d3ef..ad9b7a343 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -36,9 +36,9 @@ import com.twidere.twiderex.paging.mediator.user.UserMediaMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -53,8 +53,8 @@ class UserMediaTimelineViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source: Flow>> by lazy { - pagingMediator.flatMapLatest { - it?.let { pagingMediator -> + pagingMediator.mapNotNull { it } + .flatMapLatest { pagingMediator -> pagingMediator.pager( config = PagingConfig( pageSize = 200, @@ -70,8 +70,7 @@ class UserMediaTimelineViewModel( it.media.map { media -> media to it } } } - } ?: emptyFlow() - } + }.cachedIn(viewModelScope) } val pagingMediator by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index d0699dad4..9cd2fc895 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -29,8 +29,8 @@ import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flattenMerge +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -51,17 +51,13 @@ class UserTimelineViewModel( @OptIn(FlowPreview::class) val source by lazy { - combine(account, _excludeReplies) { account, excludeReplies -> - if (account != null) { - repository.userTimeline( - userKey = userKey, - accountKey = account.accountKey, - service = account.service as TimelineService, - exclude_replies = excludeReplies, - ) - } else { - emptyFlow() - } + combine(account.mapNotNull { it }, _excludeReplies) { account, excludeReplies -> + repository.userTimeline( + userKey = userKey, + accountKey = account.accountKey, + service = account.service as TimelineService, + exclude_replies = excludeReplies, + ) }.flattenMerge().cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 3215b52ff..53dec2597 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -29,9 +29,10 @@ import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notifyError +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.lastOrNull -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -51,9 +52,13 @@ class UserViewModel( val loadingRelationship = MutableStateFlow(false) val user = repository.getUserFlow(userKey) val relationship = MutableStateFlow(null) + + @OptIn(ExperimentalCoroutinesApi::class) val isMe by lazy { - account.map { - userKey == it?.accountKey + account.transformLatest { + it?.let { + emit(it.accountKey == userKey) + } }.asStateIn(viewModelScope, false) } From d0c42ad77a784d9f5c5b048d7c41140655389626 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 15:21:57 +0800 Subject: [PATCH 169/615] fix flow usage of lastornull --- .../com/twidere/twiderex/action/ComposeAction.kt | 2 -- .../com/twidere/twiderex/viewmodel/MediaViewModel.kt | 6 +++--- .../twidere/twiderex/viewmodel/dm/DMEventViewModel.kt | 7 +++---- .../viewmodel/dm/DMNewConversationViewModel.kt | 4 ++-- .../twiderex/viewmodel/lists/ListsUserViewModel.kt | 4 ++-- .../twiderex/viewmodel/lists/ListsViewModel.kt | 11 +++++------ .../twiderex/viewmodel/search/SearchInputViewModel.kt | 4 ++-- .../twiderex/viewmodel/search/SearchSaveViewModel.kt | 6 +++--- .../twiderex/viewmodel/settings/LayoutViewModel.kt | 4 ++-- .../viewmodel/timeline/HomeTimelineViewModel.kt | 1 - 10 files changed, 22 insertions(+), 27 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index c83ddd446..b1123a9cb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -20,8 +20,6 @@ */ package com.twidere.twiderex.action -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData expect class ComposeAction { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 2960336b9..792a3b709 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -29,8 +29,8 @@ import com.twidere.twiderex.repository.StatusRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -46,7 +46,7 @@ class MediaViewModel( } fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { - val account = account.lastOrNull() ?: return@launch + val account = account.firstOrNull() ?: return@launch currentMedia.mediaUrl?.let { mediaAction.download( accountKey = account.accountKey, @@ -57,7 +57,7 @@ class MediaViewModel( } fun shareMedia(currentMedia: UiMedia) = viewModelScope.launch { - val account = account.lastOrNull() ?: return@launch + val account = account.firstOrNull() ?: return@launch currentMedia.mediaUrl?.let { mediaAction.share( source = it, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 450364953..b67d5be10 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -88,7 +87,7 @@ class DMEventViewModel( fun sendMessage() { if (input.value.isEmpty() && inputImage.value == null) return viewModelScope.launch { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> conversation.firstOrNull()?.let { messageAction.send( account.type, @@ -117,7 +116,7 @@ class DMEventViewModel( } fun sendDraftMessage(event: UiDMEvent) = viewModelScope.launch { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> messageAction.send( account.type, data = DirectMessageSendData( @@ -133,7 +132,7 @@ class DMEventViewModel( } fun deleteMessage(event: UiDMEvent) = viewModelScope.launch { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> messageAction.delete( data = DirectMessageDeleteData( messageId = event.messageId, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index c2b782772..64d6ef4e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -36,8 +36,8 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -77,7 +77,7 @@ class DMNewConversationViewModel( fun createNewConversation(receiver: UiUser, onResult: (key: MicroBlogKey?) -> Unit) { viewModelScope.launch { kotlin.runCatching { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> dmRepository.createNewConversation( receiver = receiver, accountKey = account.accountKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index 5440af03d..d16afc6a1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -31,8 +31,8 @@ import com.twidere.twiderex.viewmodel.user.UserListViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.viewModelScope @@ -80,7 +80,7 @@ class ListsUserViewModel( fun removeMember(user: UiUser) { try { viewModelScope.launch { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsUsersRepository.removeMember( service = account.service as ListsService, listId = listId, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index a3534ab97..920a54e5d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -129,7 +128,7 @@ class ListsCreateViewModel( private: Boolean = false ) { loadingRequest(onResult) { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsRepository.createLists( accountKey = account.accountKey, service = account.service as ListsService, @@ -186,7 +185,7 @@ class ListsModifyViewModel( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsRepository.updateLists( accountKey = account.accountKey, service = account.service as ListsService, @@ -205,7 +204,7 @@ class ListsModifyViewModel( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsRepository.deleteLists( accountKey = account.accountKey, service = account.service as ListsService, @@ -221,7 +220,7 @@ class ListsModifyViewModel( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsRepository.subscribeLists( accountKey = account.accountKey, service = account.service as ListsService, @@ -236,7 +235,7 @@ class ListsModifyViewModel( onResult: (success: Boolean, list: UiList?) -> Unit = { _, _ -> } ) { loadingRequest(onResult) { - account.lastOrNull()?.let { account -> + account.firstOrNull()?.let { account -> listsRepository.unsubscribeLists( accountKey = account.accountKey, service = account.service as ListsService, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index dde1157c7..d9b482027 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -26,9 +26,9 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -66,7 +66,7 @@ class SearchInputViewModel( } fun addOrUpgrade(content: String) = viewModelScope.launch { - account.lastOrNull()?.let { + account.firstOrNull()?.let { repository.addOrUpgrade(content, it.accountKey) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 6fce97ac3..047a239cc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -24,7 +24,7 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -44,7 +44,7 @@ class SearchSaveViewModel( init { viewModelScope.launch { - account.lastOrNull()?.let { + account.firstOrNull()?.let { isSaved.value = repository.get(content, it.accountKey)?.saved ?: false } } @@ -54,7 +54,7 @@ class SearchSaveViewModel( viewModelScope.launch { loading.value = true try { - account.lastOrNull()?.let { + account.firstOrNull()?.let { repository.addOrUpgrade( content = content, accountKey = it.accountKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 467bcfc7a..b210710b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository -import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -48,7 +48,7 @@ class LayoutViewModel( .map { it to true } + list.subList(index, list.size) .filterIsInstance().map { it to false } }.let { - account.lastOrNull()?.preferences?.setHomeMenuOrder(it) + account.firstOrNull()?.preferences?.setHomeMenuOrder(it) } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index f32b316cf..4c031e7d7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -28,7 +28,6 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.mapLatest import moe.tlaster.precompose.viewmodel.viewModelScope From 2787fd02f6e4476c8bae1d1a8491db42c1b8efb5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 15:22:12 +0800 Subject: [PATCH 170/615] redesign user view model --- .../twiderex/viewmodel/user/UserViewModel.kt | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 53dec2597..18f454b90 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService -import com.twidere.services.microblog.model.IRelationship import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification @@ -31,11 +30,14 @@ import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.lastOrNull +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope +import java.util.UUID class UserViewModel( private val repository: UserRepository, @@ -43,7 +45,7 @@ class UserViewModel( private val inAppNotification: InAppNotification, private val userKey: MicroBlogKey, ) : ViewModel() { - + private val refreshFlow = MutableStateFlow(UUID.randomUUID()) private val account by lazy { accountRepository.activeAccount.asStateIn(viewModelScope, null) } @@ -51,7 +53,17 @@ class UserViewModel( val refreshing = MutableStateFlow(false) val loadingRelationship = MutableStateFlow(false) val user = repository.getUserFlow(userKey) - val relationship = MutableStateFlow(null) + val relationship = combine(account.mapNotNull { it }, refreshFlow) { account, _ -> + loadingRelationship.value = true + val relationshipService = account.service as RelationshipService + try { + relationshipService.showRelationship(userKey.id) + } catch (e: Throwable) { + null + } finally { + loadingRelationship.value = false + } + } @OptIn(ExperimentalCoroutinesApi::class) val isMe by lazy { @@ -62,29 +74,30 @@ class UserViewModel( }.asStateIn(viewModelScope, false) } - fun refresh() = viewModelScope.launch { - refreshing.value = true - val account = account.lastOrNull() ?: return@launch - runCatching { - repository.lookupUserById( - userKey.id, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - }.onFailure { - inAppNotification.notifyError(it) + private fun collectUser() = viewModelScope.launch { + combine(account.mapNotNull { it }, refreshFlow) { account, _ -> + refreshing.value = true + runCatching { + repository.lookupUserById( + userKey.id, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + }.onFailure { + inAppNotification.notifyError(it) + } + refreshing.value = false } - refreshing.value = false } fun follow() = viewModelScope.launch { loadingRelationship.value = true - val account = account.lastOrNull() ?: return@launch + val account = account.firstOrNull() ?: return@launch val relationshipService = account.service as? RelationshipService ?: return@launch runCatching { relationshipService.follow(userKey.id) }.onSuccess { - loadRelationShip() + refresh() }.onFailure { loadingRelationship.value = false inAppNotification.notifyError(it) @@ -93,33 +106,23 @@ class UserViewModel( fun unfollow() = viewModelScope.launch { loadingRelationship.value = true - val account = account.lastOrNull() ?: return@launch + val account = account.firstOrNull() ?: return@launch val relationshipService = account.service as? RelationshipService ?: return@launch runCatching { relationshipService.unfollow(userKey.id) }.onSuccess { - loadRelationShip() + refresh() }.onFailure { loadingRelationship.value = false inAppNotification.notifyError(it) } } - private fun loadRelationShip() = viewModelScope.launch { - loadingRelationship.value = true - val account = account.lastOrNull() ?: return@launch - val relationshipService = account.service as? RelationshipService ?: return@launch - try { - relationshipService.showRelationship(userKey.id).let { - relationship.value = it - } - } catch (e: Exception) { - } - loadingRelationship.value = false + fun refresh() { + refreshFlow.value = UUID.randomUUID() } init { - refresh() - loadRelationShip() + collectUser() } } From 1049cebe91cefc9f93769cfbce191c9617577d74 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 15:22:29 +0800 Subject: [PATCH 171/615] fix user component relationship display --- .../main/kotlin/com/twidere/twiderex/component/UserComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 6e04aa848..4c4cd630e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -430,7 +430,7 @@ fun UserInfo( user?.let { user -> UserInfoName(user) } - if (isMe) { + if (!isMe) { Spacer(modifier = Modifier.height(UserInfoDefaults.RelationshipSpacing)) UserRelationship(viewModel) } From b17247d3250215a58075d74e9183294189be49c9 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 16:22:15 +0800 Subject: [PATCH 172/615] fix testing --- .../com/twidere/twiderex/repository/StatusRepository.kt | 4 ++-- .../twidere/twiderex/repository/SearchRepositoryTest.kt | 9 +++++---- .../twidere/twiderex/repository/StatusRepositoryTest.kt | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index a2cf2e3e2..e7751e406 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -47,7 +47,7 @@ import kotlinx.coroutines.flow.flow class StatusRepository( private val database: CacheDatabase, - private val preferences: DataStore, + private val preferences: DataStore?, ) { fun loadStatus( statusKey: MicroBlogKey, @@ -101,7 +101,7 @@ class StatusRepository( val remoteMediator = when (platformType) { PlatformType.Twitter -> TwitterConversationMediator( service = service as TwitterService, - nitterService = preferences.data.first().nitterInstance.takeIf { it.isNotEmpty() } + nitterService = preferences?.data?.first()?.nitterInstance?.takeIf { it.isNotEmpty() } ?.let { NitterService( it.trimEnd('/'), diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt index 5a28fdace..8966feb03 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.repository import com.twidere.twiderex.mock.db.MockAppDatabase +import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking @@ -31,7 +32,7 @@ internal class SearchRepositoryTest { @Test fun addToSearchHistoryWhenSavedIsFalse() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val repo = SearchRepository(MockAppDatabase()) + val repo = SearchRepository(MockAppDatabase(), MockCacheDatabase()) repo.addOrUpgrade(content = "query1", accountKey = accountKey) repo.addOrUpgrade(content = "query2", accountKey = accountKey) repo.addOrUpgrade(content = "query3", accountKey = accountKey, saved = true) @@ -45,7 +46,7 @@ internal class SearchRepositoryTest { @Test fun addOrUpdateToSavedSearchWhenSavedIsTrue() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val repo = SearchRepository(MockAppDatabase()) + val repo = SearchRepository(MockAppDatabase(), MockCacheDatabase()) repo.addOrUpgrade(content = "query1", accountKey = accountKey, saved = false) assert(repo.savedSearch(accountKey).first().isEmpty()) repo.addOrUpgrade(content = "query1", accountKey = accountKey, saved = true) @@ -60,7 +61,7 @@ internal class SearchRepositoryTest { @Test fun canNotSetSavedToFalseOnSavedSearch() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val repo = SearchRepository(MockAppDatabase()) + val repo = SearchRepository(MockAppDatabase(), MockCacheDatabase()) repo.addOrUpgrade(content = "query", accountKey = accountKey, saved = true) repo.addOrUpgrade(content = "query", accountKey = accountKey, saved = false) val savedSearch = repo.savedSearch(accountKey).first() @@ -70,7 +71,7 @@ internal class SearchRepositoryTest { @Test fun deleteFromDbAfterRemove() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val repo = SearchRepository(MockAppDatabase()) + val repo = SearchRepository(MockAppDatabase(), MockCacheDatabase()) val savedSearchFlow = repo.savedSearch(accountKey) val searchHistoryFlow = repo.searchHistory(accountKey) repo.addOrUpgrade(content = "query saved", accountKey = accountKey, saved = true) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt index af40eb21a..36d708800 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt @@ -43,7 +43,7 @@ class StatusRepositoryTest { val status = mockIStatus().toUi(accountKey) StatusRepository( database = database, - nitterService = null + preferences = null, ).let { database.statusDao().insertAll(listOf(status), accountKey) it.updateStatus( @@ -67,7 +67,7 @@ class StatusRepositoryTest { }.toUi(accountKey) StatusRepository( database = database, - nitterService = null + preferences = null, ).let { val statusFlow = it.loadStatus(statusKey = status.statusKey, accountKey = accountKey) assertEquals(status.statusKey, statusFlow.first()?.statusKey) @@ -86,7 +86,7 @@ class StatusRepositoryTest { val accountKey = MicroBlogKey.twitter("test") val repo = StatusRepository( database = database, - nitterService = null + preferences = null, ) val mockStatus = mockIStatus().toUi(accountKey) From 550daf2c94eac1476833a366849d2b2a74446538 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Sep 2021 17:23:58 +0800 Subject: [PATCH 173/615] fix timeline scroll position restore --- .../twiderex/component/TimelineComponent.kt | 21 +++++++++------ .../viewmodel/timeline/TimelineViewModel.kt | 27 ++++++++++--------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 4e5230c03..2f091a080 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -55,18 +55,21 @@ fun TimelineComponent( }, refreshIndicatorPadding = contentPadding ) { - val scrollState by viewModel.timelineScrollState.observeAsState( - initial = TimelineScrollState.Zero - ) val listState = rememberLazyListState() LaunchedEffect(Unit) { - snapshotFlow { scrollState } + var inited = false + snapshotFlow { listState.layoutInfo.totalItemsCount } .distinctUntilChanged() + .filter { it != 0 } + .filter { !inited } .collect { - listState.scrollToItem( - it.firstVisibleItemIndex, - it.firstVisibleItemScrollOffset - ) + inited = true + viewModel.provideScrollState().let { + listState.scrollToItem( + it.firstVisibleItemIndex, + it.firstVisibleItemScrollOffset + ) + } } } if (items.itemCount > 0) { @@ -78,6 +81,8 @@ fun TimelineComponent( snapshotFlow { listState.isScrollInProgress } .distinctUntilChanged() .filter { !it } + .filter { listState.layoutInfo.totalItemsCount != 0 } + .filter { listState.firstVisibleItemIndex != 0 && listState.firstVisibleItemScrollOffset != 0 } .collect { viewModel.saveScrollState( TimelineScrollState( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 7b18d9ead..572764608 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -36,9 +36,9 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.lastOrNull -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -65,13 +65,12 @@ abstract class TimelineViewModel( .asStateIn(viewModelScope, emptyList()) } - @OptIn(ExperimentalCoroutinesApi::class) - val timelineScrollState by lazy { - savedStateKey.flatMapLatest { + suspend fun provideScrollState(): TimelineScrollState { + return savedStateKey.firstOrNull()?.let { val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") val firstVisibleItemScrollOffsetKey = intPreferencesKey("${it}_firstVisibleItemScrollOffset") - dataStore.data.map { + dataStore.data.firstOrNull()?.let { val firstVisibleItemIndex = it[firstVisibleItemIndexKey] ?: 0 val firstVisibleItemScrollOffset = it[firstVisibleItemScrollOffsetKey] ?: 0 TimelineScrollState( @@ -79,7 +78,7 @@ abstract class TimelineViewModel( firstVisibleItemScrollOffset = firstVisibleItemScrollOffset, ) } - }.asStateIn(viewModelScope, TimelineScrollState.Zero) + } ?: TimelineScrollState.Zero } fun loadBetween( @@ -93,13 +92,15 @@ abstract class TimelineViewModel( ) } - fun saveScrollState(offset: TimelineScrollState) = viewModelScope.launch { - dataStore.edit { - val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") - val firstVisibleItemScrollOffsetKey = - intPreferencesKey("${it}_firstVisibleItemScrollOffset") - it[firstVisibleItemIndexKey] = offset.firstVisibleItemIndex - it[firstVisibleItemScrollOffsetKey] = offset.firstVisibleItemScrollOffset + suspend fun saveScrollState(offset: TimelineScrollState) { + dataStore.edit { preferences -> + savedStateKey.firstOrNull()?.let { + val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") + val firstVisibleItemScrollOffsetKey = + intPreferencesKey("${it}_firstVisibleItemScrollOffset") + preferences[firstVisibleItemIndexKey] = offset.firstVisibleItemIndex + preferences[firstVisibleItemScrollOffsetKey] = offset.firstVisibleItemScrollOffset + } } } } From 91787e791661e522db94d6f303914c34975151d9 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 15 Sep 2021 18:44:53 +0800 Subject: [PATCH 174/615] implement network image for desktop --- .../component/status/StatusMediaComponent.kt | 6 +- .../com/twidere/twiderex/kmp/ImagePainter.kt | 12 +- .../com/twidere/twiderex/kmp/ResLoader.kt | 28 +--- .../twidere/twiderex/component/Resources.kt | 18 +-- .../component/foundation/NetworkImage.kt | 6 +- .../twiderex/component/image/ImageBlur.kt | 33 ++++ .../twiderex/component/image/ImageEffects.kt | 43 ++++++ .../com/twidere/twiderex/kmp/ImagePainter.kt | 4 +- .../com/twidere/twiderex/kmp/ResLoader.kt | 5 +- .../twidere/twiderex/image/GifImagePainter.kt | 82 ++++++++++ .../twidere/twiderex/image/ImagePainter.kt | 144 ++++++++++++++++++ .../com/twidere/twiderex/kmp/ImagePainter.kt | 47 ++++++ .../com/twidere/twiderex/kmp/ResLoader.kt | 5 +- 13 files changed, 372 insertions(+), 61 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 5778ee16a..02579fe2f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -54,10 +54,10 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.R -import com.twidere.twiderex.component.ImageBlur import com.twidere.twiderex.component.foundation.GridLayout import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer +import com.twidere.twiderex.component.image.ImageBlur import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType @@ -278,7 +278,7 @@ fun StatusMediaPreviewItem( ) { NetworkImage( data = it, - blur = ImageBlur.Sensitive, + effects = { blur(ImageBlur.Sensitive) }, modifier = Modifier .fillMaxSize(), placeholder = { @@ -311,7 +311,7 @@ fun StatusMediaPreviewItem( if (sensitive && previewUrl != null) { NetworkImage( data = previewUrl, - blur = ImageBlur.Sensitive, + effects = { blur(ImageBlur.Sensitive) }, modifier = Modifier .fillMaxSize(), placeholder = { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index d6c4d5834..e38ee9e79 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -34,8 +34,8 @@ import coil.transform.BlurTransformation import coil.util.CoilUtils import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig -import com.twidere.twiderex.component.ImageBlur import com.twidere.twiderex.component.foundation.NetworkImageState +import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig import okhttp3.Request @@ -46,7 +46,7 @@ internal actual fun rememberNetworkImagePainter( data: Any, authorization: Authorization, httpConfig: HttpConfig, - blur: ImageBlur?, + effects: ImageEffects, onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val context = LocalContext.current @@ -54,13 +54,13 @@ internal actual fun rememberNetworkImagePainter( data = data, imageLoader = buildImageLoader(), builder = { - crossfade(true) - if (blur != null) { + crossfade(effects.crossFade) + if (effects.blur != null) { transformations( BlurTransformation( context = context, - radius = blur.blurRadius, - sampling = blur.bitmapScale + radius = effects.blur.blurRadius, + sampling = effects.blur.bitmapScale ) ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index fabce421f..5ad08b70e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -26,8 +26,6 @@ import androidx.compose.ui.graphics.painter.Painter import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.SvgDecoder -import coil.transform.BlurTransformation -import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -43,46 +41,24 @@ actual class ResLoader( } @Composable - actual fun getSvg(res: FileResource, blur: ImageBlur?): Painter { + actual fun getSvg(res: FileResource): Painter { val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" return rememberImagePainter( data = data, imageLoader = LocalImageLoader.current.newBuilder() .componentRegistry { add(SvgDecoder(context)) } .build(), - builder = { - if (blur != null) { - transformations( - BlurTransformation( - context = context, - radius = blur.blurRadius, - sampling = blur.bitmapScale - ) - ) - } - } ) } @Composable - actual fun getImage(res: ImageResource, blur: ImageBlur?): Painter { + actual fun getImage(res: ImageResource): Painter { val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" return rememberImagePainter( data = data, imageLoader = LocalImageLoader.current.newBuilder() .componentRegistry { add(SvgDecoder(context)) } .build(), - builder = { - if (blur != null) { - transformations( - BlurTransformation( - context = context, - radius = blur.blurRadius, - sampling = blur.bitmapScale - ) - ) - } - } ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index c66a0aeef..2e8f9a346 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -41,22 +41,10 @@ fun stringResource(res: StringResource): String { * res: FileResource:svg, ImageResource */ @Composable -fun painterResource(res: Any, blur: ImageBlur? = null): Painter { +fun painterResource(res: Any): Painter { return when (res) { - is FileResource -> LocalResLoader.current.getSvg(res, blur) - is ImageResource -> LocalResLoader.current.getImage(res, blur) + is FileResource -> LocalResLoader.current.getSvg(res) + is ImageResource -> LocalResLoader.current.getImage(res) else -> throw NotImplementedError() } } - -class ImageBlur( - val blurRadius: Float, - val bitmapScale: Float, -) { - companion object { - val Sensitive = ImageBlur( - blurRadius = 25f, - bitmapScale = 0.4f - ) - } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 995cb4cae..92e96dc89 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import com.twidere.services.http.authorization.EmptyAuthorization import com.twidere.twiderex.MR -import com.twidere.twiderex.component.ImageBlur +import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.kmp.rememberNetworkImagePainter import com.twidere.twiderex.preferences.LocalHttpConfig @@ -41,7 +41,7 @@ fun NetworkImage( data: Any, modifier: Modifier = Modifier, contentScale: ContentScale = ContentScale.Crop, - blur: ImageBlur? = null, + effects: ImageEffects.Builder.() -> Unit = { crossFade(true) }, placeholder: @Composable (() -> Unit)? = null, ) { val state = remember { @@ -68,7 +68,7 @@ fun NetworkImage( data = data, httpConfig = httpConfig, authorization = auth, - blur = blur, + effects = ImageEffects.Builder().apply(effects).build(), onImageStateChanged = { state.value = it } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt new file mode 100644 index 000000000..6ef950f2d --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.image + +class ImageBlur( + val blurRadius: Float, + val bitmapScale: Float, +) { + companion object { + val Sensitive = ImageBlur( + blurRadius = 25f, + bitmapScale = 0.4f + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt new file mode 100644 index 000000000..fee7f675a --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt @@ -0,0 +1,43 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.image + +class ImageEffects private constructor( + val blur: ImageBlur?, + val crossFade: Boolean +) { + class Builder { + private var blur: ImageBlur? = null + private var crossFade: Boolean = true + fun blur(blur: ImageBlur) { + this.blur = blur + } + + fun crossFade(crossFade: Boolean) { + this.crossFade = crossFade + } + + fun build() = ImageEffects( + blur = blur, + crossFade = crossFade + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index a22923784..7dab22736 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -24,14 +24,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig -import com.twidere.twiderex.component.ImageBlur import com.twidere.twiderex.component.foundation.NetworkImageState +import com.twidere.twiderex.component.image.ImageEffects @Composable internal expect fun rememberNetworkImagePainter( data: Any, authorization: Authorization, httpConfig: HttpConfig, - blur: ImageBlur?, + effects: ImageEffects, onImageStateChanged: (NetworkImageState) -> Unit ): Painter diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 2b4008fc4..f2cea261c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.kmp import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -31,8 +30,8 @@ expect class ResLoader { fun getString(res: StringResource, vararg args: Any): String @Composable - fun getSvg(res: FileResource, blur: ImageBlur?): Painter + fun getSvg(res: FileResource): Painter @Composable - fun getImage(res: ImageResource, blur: ImageBlur?): Painter + fun getImage(res: ImageResource): Painter } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt new file mode 100644 index 000000000..9f24a3dc9 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt @@ -0,0 +1,82 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.image + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.IntSize +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import org.jetbrains.skija.Bitmap +import org.jetbrains.skija.Codec + +internal class GifPainter(private val codec: Codec, private val parentScope: CoroutineScope) : Painter() { + override val intrinsicSize: Size + get() = Size(codec.width.toFloat(), codec.height.toFloat()) + private var frameIndex = mutableStateOf(0) + private var rememberScope: CoroutineScope? = null + private var bitmapCache: Bitmap? = null + + fun start() { + if (rememberScope != null && rememberScope?.isActive == true) return + rememberScope?.cancel() + val context = parentScope.coroutineContext + rememberScope = CoroutineScope(context + SupervisorJob(context[Job])) + rememberScope?.launch { + while (true) { + for ((index, frame) in codec.framesInfo.withIndex()) { + frameIndex.value = index + delay(frame.duration.toLong()) + } + } + } + } + + fun stop() { + if (rememberScope?.isActive == true) rememberScope?.cancel() + } + + override fun DrawScope.onDraw() { + val bitmap = recycleBitmap(codec) + codec.readPixels(bitmap, frameIndex.value) + val intSize = IntSize(size.width.toInt(), size.height.toInt()) + drawImage(bitmap.asImageBitmap(), dstSize = intSize) + } + + private fun recycleBitmap(codec: Codec): Bitmap { + return bitmapCache?.let { + if (codec.width == bitmapCache?.width && codec.height == bitmapCache?.height) { + it.apply { allocPixels(codec.imageInfo) } + } else null + } ?: Bitmap().apply { allocPixels(codec.imageInfo) } + .also { + bitmapCache = it + } + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt new file mode 100644 index 000000000..b57ad3447 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -0,0 +1,144 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.image + +import androidx.compose.runtime.RememberObserver +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.asPainter +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.painter.Painter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import org.jetbrains.skija.Codec +import org.jetbrains.skija.Data +import java.io.InputStream +import java.net.HttpURLConnection +import java.net.URL +import javax.imageio.ImageIO + +/** + * current support http/https only + */ +internal class ImagePainter( + private val request: Any, + private val parentScope: CoroutineScope +) : Painter(), RememberObserver { + private var painter = mutableStateOf(null) + + override val intrinsicSize: Size + get() = painter.value?.intrinsicSize ?: Size.Unspecified + private var alpha: Float = 1f + private var colorFilter: ColorFilter? = null + private var rememberScope: CoroutineScope? = null + + override fun applyAlpha(alpha: Float): Boolean { + this.alpha = alpha + return true + } + + override fun applyColorFilter(colorFilter: ColorFilter?): Boolean { + this.colorFilter = colorFilter + return true + } + + override fun DrawScope.onDraw() { + painter.value?.apply { + draw(size, alpha, colorFilter) + if (this is GifPainter) { + start() + } + } + } + + override fun onAbandoned() = onForgotten() + + override fun onForgotten() { + rememberScope?.cancel() + rememberScope = null + painter.value?.let { + if (it is GifPainter) { + it.stop() + } + } + } + + override fun onRemembered() { + rememberScope?.cancel() + val context = parentScope.coroutineContext + rememberScope = CoroutineScope(parentScope.coroutineContext + SupervisorJob(context[Job])) + + rememberScope?.launch { + execute() + } + } + + private fun networkRequest(url: URL) { + var connection: HttpURLConnection? = null + var input: InputStream? = null + try { + connection = url.openConnection() as HttpURLConnection + connection.connectTimeout = 5000 + connection.connect() + input = connection.inputStream + if (connection.contentType == "image/gif") { + painter.value = GifPainter(Codec.makeFromData(Data.makeFromBytes(input.readAllBytes())), parentScope) + } else { + ImageIO.read(input)?.let { + painter = it.asPainter() + } + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + try { + input?.close() + connection?.disconnect() + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + private fun execute() { + val data = when (request) { + is URL -> { + request.toString() + } + is String -> { + request + } + else -> { + throw NotImplementedError() + } + } + checkRequestValid(data) + networkRequest(URL(data)) + } + + private fun checkRequestValid(data: String) { + if (!data.startsWith("http")) throw NotImplementedError() + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt new file mode 100644 index 000000000..af06a8884 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -0,0 +1,47 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.graphics.painter.Painter +import com.twidere.services.http.authorization.Authorization +import com.twidere.services.http.config.HttpConfig +import com.twidere.twiderex.component.foundation.NetworkImageState +import com.twidere.twiderex.component.image.ImageEffects +import com.twidere.twiderex.image.ImagePainter +import kotlinx.coroutines.Dispatchers + +// TODO 1 PROXY +// TODO 2 authorize +// TODO 3 effects +@Composable +internal actual fun rememberNetworkImagePainter( + data: Any, + authorization: Authorization, + httpConfig: HttpConfig, + effects: ImageEffects, + onImageStateChanged: (NetworkImageState) -> Unit +): Painter { + val scope = rememberCoroutineScope { Dispatchers.IO } + return remember(data) { ImagePainter(data, scope) } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 44230ff9a..f24a62a39 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.kmp import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource -import com.twidere.twiderex.component.ImageBlur import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -37,12 +36,12 @@ actual class ResLoader { } @Composable - actual fun getSvg(res: FileResource, blur: ImageBlur?): Painter { + actual fun getSvg(res: FileResource): Painter { return painterResource(res.filePath) } @Composable - actual fun getImage(res: ImageResource, blur: ImageBlur?): Painter { + actual fun getImage(res: ImageResource): Painter { return painterResource(res.filePath) } } From c5d5e467f8d31f1804ecba2e168e1e8266b140aa Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 15 Sep 2021 19:01:55 +0800 Subject: [PATCH 175/615] fix some error code --- .../component/foundation/NetworkImage.kt | 29 +++++++++---------- .../{GifImagePainter.kt => GifPainter.kt} | 0 .../twidere/twiderex/image/ImagePainter.kt | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) rename common/src/desktopMain/kotlin/com/twidere/twiderex/image/{GifImagePainter.kt => GifPainter.kt} (100%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 92e96dc89..909784980 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.component.foundation -import android.net.Uri import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -34,7 +33,6 @@ import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.kmp.rememberNetworkImagePainter import com.twidere.twiderex.preferences.LocalHttpConfig -import com.twidere.twiderex.twitterTonApiHost @Composable fun NetworkImage( @@ -51,19 +49,20 @@ fun NetworkImage( data } else { val httpConfig = LocalHttpConfig.current - val auth = if (data is Uri && twitterTonApiHost == data.host) { - TODO("Waiting for LocalActiveAccount") - // (account.credentials as OAuthCredentials).let { - // OAuth1Authorization( - // consumerKey = it.consumer_key, - // consumerSecret = it.consumer_secret, - // accessToken = it.access_token, - // accessSecret = it.access_token_secret, - // ) - // } - } else { - EmptyAuthorization() - } + val auth = EmptyAuthorization() + // if (data is Uri && twitterTonApiHost == data.host) { + // TODO "Waiting for LocalActiveAccount") + // (account.credentials as OAuthCredentials).let { + // OAuth1Authorization( + // consumerKey = it.consumer_key, + // consumerSecret = it.consumer_secret, + // accessToken = it.access_token, + // accessSecret = it.access_token_secret, + // ) + // } + // } else { + + // } rememberNetworkImagePainter( data = data, httpConfig = httpConfig, diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt similarity index 100% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifImagePainter.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index b57ad3447..fd4e51fb6 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -107,7 +107,7 @@ internal class ImagePainter( painter.value = GifPainter(Codec.makeFromData(Data.makeFromBytes(input.readAllBytes())), parentScope) } else { ImageIO.read(input)?.let { - painter = it.asPainter() + painter.value = it.asPainter() } } } catch (e: Exception) { From 1399da4df5a74a8cc5c920968a9df812c1ece18c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Sep 2021 11:18:36 +0800 Subject: [PATCH 176/615] fixed desktop build error --- .../twiderex/component/lazy/LazyGrid.kt | 0 .../component/lazy/itemsGridIndexed.kt | 0 .../twiderex/component/lazy/itemsPaging.kt | 0 .../placeholder/UiImagePlaceholder.kt | 0 common/build.gradle.kts | 5 --- .../com/twidere/twiderex/kmp/TimeUtils.kt | 32 ++++++++++++++ .../twiderex/extensions/TimestampExtension.kt | 8 +--- .../com/twidere/twiderex/kmp/TimeUtils.kt | 24 +++++++++++ .../com/twidere/twiderex/kmp/TimeUtils.kt | 43 +++++++++++++++++++ 9 files changed, 101 insertions(+), 11 deletions(-) rename {common/src/commonMain => android/src/main}/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt (100%) rename {common/src/commonMain => android/src/main}/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt (100%) rename {common/src/commonMain => android/src/main}/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt (100%) rename {common/src/commonMain => android/src/main}/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt (100%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt similarity index 100% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt similarity index 100% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt similarity index 100% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt b/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt similarity index 100% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt rename to android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 2512980d8..b4b98eaef 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -36,7 +36,6 @@ kotlin { api(compose.material) implementation(projects.services) api("androidx.paging:paging-common:${Versions.paging}") - api("androidx.paging:paging-compose:${Versions.paging_compose}") api("androidx.datastore:datastore-core:${Versions.datastore}") api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") api("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.Kotlin.serialization}") @@ -47,10 +46,6 @@ kotlin { implementation(projects.routeProcessor) ksp(projects.routeProcessor) implementation("dev.icerock.moko:resources:${Versions.moko}") - implementation("com.mxalbert.zoomable:zoomable:${Versions.zoomable}") - implementation("com.github.Tlaster:NestedScrollView:${ Versions.nestedScrollView}") - implementation("com.github.Tlaster:Swiper:${Versions.swiper}") - implementation("com.github.Tlaster:Placeholder:${Versions.placeholder}") } } val commonTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt new file mode 100644 index 000000000..6359bfec1 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import android.text.format.DateUtils +actual object TimeUtils { + actual fun humanizedTimestamp(time: Long): String { + return DateUtils.getRelativeTimeSpanString( + time, System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_ALL + ).toString() + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt index 4676a90b2..a56ac71fa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt @@ -20,17 +20,13 @@ */ package com.twidere.twiderex.extensions -import android.text.format.DateUtils +import com.twidere.twiderex.kmp.TimeUtils import java.text.SimpleDateFormat import java.util.Date import java.util.Locale fun Long.humanizedTimestamp(): String { - return DateUtils.getRelativeTimeSpanString( - this, System.currentTimeMillis(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_ALL - ).toString() + return TimeUtils.humanizedTimestamp(this) } fun Long.formattedTimestamp(): String { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt new file mode 100644 index 000000000..393b27407 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -0,0 +1,24 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp +expect object TimeUtils { + fun humanizedTimestamp(time: Long): String +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt new file mode 100644 index 000000000..3c5cb9c5a --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -0,0 +1,43 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.kmp + +import java.text.SimpleDateFormat +import java.time.Instant +import java.time.temporal.ChronoUnit +import java.util.Date + +actual object TimeUtils { + private const val hour = 60 + private const val day = 24 * 60 + private const val week = 7 * 24 * 60 + actual fun humanizedTimestamp(time: Long): String { + val now: Instant = Instant.now() + val instant: Instant = Instant.ofEpochMilli(time) + val relativeMinute = ChronoUnit.MINUTES.between(instant, now) + return when { + relativeMinute < hour -> "$relativeMinute min.ago" + relativeMinute in (hour + 1) until day -> "${ChronoUnit.HOURS.between(instant, now)} hr.ago" + relativeMinute in (day + 1) until week -> "${ChronoUnit.DAYS.between(instant, now)} days ago" + else -> SimpleDateFormat.getDateTimeInstance().format(Date(time)) + } + } +} From 3aeb4213a4fa514af24d6e753bd601e86d2f6a10 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Sep 2021 15:21:11 +0800 Subject: [PATCH 177/615] add blur fitler to desktop --- .../http/TwidereNetworkImageLoader.kt | 96 ------------------- .../twiderex/image/ImageEffectsFilter.kt | 86 +++++++++++++++++ .../twidere/twiderex/image/ImagePainter.kt | 31 ++++-- .../com/twidere/twiderex/kmp/ImagePainter.kt | 69 ++++++++++++- 4 files changed, 175 insertions(+), 107 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt b/android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt deleted file mode 100644 index b7b885732..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/http/TwidereNetworkImageLoader.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.http - -import android.content.Context -import android.net.Uri -import coil.ImageLoader -import coil.bitmap.BitmapPool -import coil.memory.MemoryCache -import coil.request.DefaultRequestOptions -import coil.request.Disposable -import coil.request.ImageRequest -import coil.request.ImageResult -import com.twidere.services.http.authorization.OAuth1Authorization -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.cred.OAuthCredentials -import com.twidere.twiderex.model.enums.PlatformType -import okhttp3.Headers -import okhttp3.Request -import java.net.URL - -class TwidereNetworkImageLoader( - private val realImageLoader: ImageLoader, - private val context: Context, - private val account: AccountDetails? -) : ImageLoader { - private val twitterTonApiHost = "ton.twitter.com" - override val bitmapPool: BitmapPool - get() = realImageLoader.bitmapPool - override val defaults: DefaultRequestOptions - get() = realImageLoader.defaults - override val memoryCache: MemoryCache - get() = realImageLoader.memoryCache - - override fun enqueue(request: ImageRequest): Disposable { - return realImageLoader.enqueue(handleRequest(request)) - } - - override suspend fun execute(request: ImageRequest): ImageResult { - return realImageLoader.execute(handleRequest(request)) - } - - override fun newBuilder(): ImageLoader.Builder { - return ImageLoader.Builder(context) - } - - override fun shutdown() { - realImageLoader.shutdown() - } - - private fun handleRequest(request: ImageRequest): ImageRequest { - var data = request.data - // ton.twitter.com must be retrieved via an authenticated - if (data is String) data = Uri.parse(data) - return if (data is Uri && twitterTonApiHost == data.host && account?.type == PlatformType.Twitter) { - val auth = (account.credentials as OAuthCredentials).let { - OAuth1Authorization( - consumerKey = it.consumer_key, - consumerSecret = it.consumer_secret, - accessToken = it.access_token, - accessSecret = it.access_token_secret, - ) - } - request.newBuilder( - request.context - ).headers( - headers = Headers.headersOf( - "Authorization", - auth.getAuthorizationHeader(Request.Builder().url(URL(data.toString())).build()) - ) - ).build() - } else { - request.newBuilder(request.context) - .data(data) - .build() - } - } -} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt new file mode 100644 index 000000000..d763dc08f --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt @@ -0,0 +1,86 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.image + +import java.awt.image.BufferedImage +import java.awt.image.ConvolveOp +import java.awt.image.Kernel + +object ImageEffectsFilter { + fun applyBlurFilter(bitmap: BufferedImage, radius: Int, scale: Float): BufferedImage { + val scaledBitmap = applyPixelFilter(bitmap, scale) + var result = BufferedImage(scaledBitmap.getWidth(), scaledBitmap.getHeight(), scaledBitmap.type) + + val graphics = result.getGraphics() + graphics.drawImage(scaledBitmap, 0, 0, null) + graphics.dispose() + + val weight: Float = 1.0f / (radius * radius) + val matrix = FloatArray(radius * radius) + + for (i in matrix.indices) { + matrix[i] = weight + } + + val kernel = Kernel(radius, radius, matrix) + val op = ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null) + result = op.filter(result, null) + + return result.getSubimage( + radius, + radius, + result.width - radius * 2, + result.height - radius * 2 + ) + } + + fun applyPixelFilter(bitmap: BufferedImage, scale: Float): BufferedImage { + val w: Int = bitmap.width + val h: Int = bitmap.height + var result = scaleBitmapAspectRatio(bitmap, (w * scale).toInt(), (h * scale).toInt()) + result = scaleBitmapAspectRatio(result, w, h) + + return result + } + + private fun scaleBitmapAspectRatio( + bitmap: BufferedImage, + width: Int, + height: Int + ): BufferedImage { + val boundW: Float = width.toFloat() + val boundH: Float = height.toFloat() + + val ratioX: Float = boundW / bitmap.width + val ratioY: Float = boundH / bitmap.height + val ratio: Float = if (ratioX < ratioY) ratioX else ratioY + + val resultH = (bitmap.height * ratio).toInt() + val resultW = (bitmap.width * ratio).toInt() + + val result = BufferedImage(resultW, resultH, BufferedImage.TYPE_INT_ARGB) + val graphics = result.createGraphics() + graphics.drawImage(bitmap, 0, 0, resultW, resultH, null) + graphics.dispose() + + return result + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index fd4e51fb6..f0e9e7747 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -27,6 +27,8 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.asPainter import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter +import com.twidere.twiderex.component.foundation.NetworkImageState +import com.twidere.twiderex.component.image.ImageEffects import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob @@ -44,7 +46,10 @@ import javax.imageio.ImageIO */ internal class ImagePainter( private val request: Any, - private val parentScope: CoroutineScope + private val parentScope: CoroutineScope, + private val imageEffects: ImageEffects, + private val httpConnection: (URL) -> HttpURLConnection = { it.openConnection() as HttpURLConnection }, + private val onImageStateChanged: (NetworkImageState) -> Unit ) : Painter(), RememberObserver { private var painter = mutableStateOf(null) @@ -89,29 +94,38 @@ internal class ImagePainter( rememberScope?.cancel() val context = parentScope.coroutineContext rememberScope = CoroutineScope(parentScope.coroutineContext + SupervisorJob(context[Job])) - + onImageStateChanged(NetworkImageState.LOADING) rememberScope?.launch { - execute() + try { + execute() + onImageStateChanged(NetworkImageState.SUCCESS) + } catch (e: Throwable) { + onImageStateChanged(NetworkImageState.ERROR) + } } } private fun networkRequest(url: URL) { var connection: HttpURLConnection? = null var input: InputStream? = null + var error: Throwable? = null try { - connection = url.openConnection() as HttpURLConnection - connection.connectTimeout = 5000 + connection = httpConnection(url) connection.connect() input = connection.inputStream if (connection.contentType == "image/gif") { painter.value = GifPainter(Codec.makeFromData(Data.makeFromBytes(input.readAllBytes())), parentScope) } else { - ImageIO.read(input)?.let { + ImageIO.read(input)?.let { image -> + imageEffects.blur?.let { + ImageEffectsFilter.applyBlurFilter(image, it.blurRadius.toInt(), it.bitmapScale) + } ?: image + }?.let { painter.value = it.asPainter() } } - } catch (e: Exception) { - e.printStackTrace() + } catch (e: Throwable) { + error = e } finally { try { input?.close() @@ -120,6 +134,7 @@ internal class ImagePainter( e.printStackTrace() } } + if (error != null) throw error } private fun execute() { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index af06a8884..fd57197e3 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -26,13 +26,21 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.graphics.painter.Painter import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig +import com.twidere.services.proxy.ProxyConfig +import com.twidere.services.proxy.ReverseProxyHandler import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.image.ImagePainter import kotlinx.coroutines.Dispatchers +import okhttp3.Credentials +import okhttp3.HttpUrl +import okhttp3.Request +import java.io.IOException +import java.net.HttpURLConnection +import java.net.InetSocketAddress +import java.net.Proxy +import java.net.URL -// TODO 1 PROXY -// TODO 2 authorize // TODO 3 effects @Composable internal actual fun rememberNetworkImagePainter( @@ -43,5 +51,60 @@ internal actual fun rememberNetworkImagePainter( onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val scope = rememberCoroutineScope { Dispatchers.IO } - return remember(data) { ImagePainter(data, scope) } + return remember(data) { + ImagePainter( + data, + scope, + imageEffects = effects, + httpConnection = { + generateConnection( + httpConfig = httpConfig, + authorization = authorization, + url = it + ) + }, + onImageStateChanged = onImageStateChanged + ) + } +} + +private fun generateConnection(httpConfig: HttpConfig, authorization: Authorization, url: URL): HttpURLConnection { + val proxyConfig = httpConfig.proxyConfig + val connection = if (proxyConfig.enable) { + when (proxyConfig.type) { + ProxyConfig.Type.HTTP -> { + val proxy = if (proxyConfig.port !in (0..65535)) { + Proxy.NO_PROXY + } else { + val address = InetSocketAddress.createUnresolved( + proxyConfig.server, + proxyConfig.port + ) + Proxy(Proxy.Type.HTTP, address) + } + url.openConnection(proxy) + } + ProxyConfig.Type.REVERSE -> { + val con = HttpUrl.get(url)?.let { + try { + URL(ReverseProxyHandler.replaceUrl(it, proxyConfig.server)) + } catch (e: Throwable) { + throw IOException("Invalid reverse proxy format") + } + }?.openConnection() ?: url.openConnection() + if (proxyConfig.userName.isNotEmpty() && proxyConfig.password.isNotEmpty()) { + val credential = Credentials.basic( + proxyConfig.userName, + proxyConfig.password + ) + con.setRequestProperty("Proxy-Authorization", credential) + } + con + } + } + } else url.openConnection() + if (authorization.hasAuthorization) { + connection.setRequestProperty("Authorization", authorization.getAuthorizationHeader(Request.Builder().url(url).build())) + } + return connection as HttpURLConnection } From 9d94d20354a64a38e164181070c2e257d92e6a5c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Sep 2021 16:17:27 +0800 Subject: [PATCH 178/615] fixed NetworkImage keep recompose problem --- .../component/status/StatusMediaComponent.kt | 48 +++++---------- .../twiderex/extensions/TimestampExtension.kt | 60 ------------------- .../component/foundation/NetworkImage.kt | 4 +- 3 files changed, 17 insertions(+), 95 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 02579fe2f..9fbde5051 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -271,40 +271,20 @@ fun StatusMediaPreviewItem( when (media.type) { MediaType.photo -> media.previewUrl?.let { - AnimatedVisibility( - visible = sensitive, - enter = fadeIn(), - exit = fadeOut() - ) { - NetworkImage( - data = it, - effects = { blur(ImageBlur.Sensitive) }, - modifier = Modifier - .fillMaxSize(), - placeholder = { - Placeholder(modifier = Modifier.fillMaxSize()) - }, - ) - } - AnimatedVisibility( - visible = !sensitive, - enter = fadeIn(), - exit = fadeOut() - ) { - NetworkImage( - data = it, - modifier = Modifier - .fillMaxSize() - .clickable( - onClick = { - onClick(media) - } - ), - placeholder = { - Placeholder(modifier = Modifier.fillMaxSize()) - }, - ) - } + NetworkImage( + data = it, + modifier = Modifier + .fillMaxSize() + .clickable( + onClick = { + onClick(media) + } + ), + effects = { if (sensitive) blur(ImageBlur.Sensitive) }, + placeholder = { + Placeholder(modifier = Modifier.fillMaxSize()) + }, + ) } MediaType.video, MediaType.animated_gif -> media.mediaUrl?.let { val previewUrl = media.previewUrl diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt deleted file mode 100644 index 4676a90b2..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.extensions - -import android.text.format.DateUtils -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - -fun Long.humanizedTimestamp(): String { - return DateUtils.getRelativeTimeSpanString( - this, System.currentTimeMillis(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_ALL - ).toString() -} - -fun Long.formattedTimestamp(): String { - return SimpleDateFormat.getDateTimeInstance().format(Date(this)) -} - -val countUnits = arrayOf(null, "K", "M", "B") - -fun Long.humanizedCount(): String { - if (this < 1000) { - return this.toString() - } - var value = this.toDouble() - var index = 0 - while (index < countUnits.size) { - if (value < 1000) { - break - } - value /= 1000.0 - index++ - } - return if (value < 10 && value % 1.0 >= 0.049 && value % 1.0 < 0.5) { - String.format(Locale.getDefault(), "%.1f %s", value, countUnits[index]) - } else { - String.format(Locale.getDefault(), "%.0f %s", value, countUnits[index]) - } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 909784980..c1b6870a2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.component.foundation +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -69,7 +70,8 @@ fun NetworkImage( authorization = auth, effects = ImageEffects.Builder().apply(effects).build(), onImageStateChanged = { - state.value = it + Log.d("Image", "onStateChange:$it") + if (state.value == NetworkImageState.LOADING) state.value = it } ) } From 5b72c4ffad9c1751026e4973e9a8cfcb400e4edc Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Sep 2021 16:28:30 +0800 Subject: [PATCH 179/615] add auth todo to NetworkImage --- .../component/foundation/NetworkImage.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index c1b6870a2..937305525 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.component.foundation -import android.util.Log import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -34,6 +33,9 @@ import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.kmp.rememberNetworkImagePainter import com.twidere.twiderex.preferences.LocalHttpConfig +import com.twidere.twiderex.twitterTonApiHost +import java.net.MalformedURLException +import java.net.URL @Composable fun NetworkImage( @@ -50,27 +52,30 @@ fun NetworkImage( data } else { val httpConfig = LocalHttpConfig.current - val auth = EmptyAuthorization() - // if (data is Uri && twitterTonApiHost == data.host) { - // TODO "Waiting for LocalActiveAccount") - // (account.credentials as OAuthCredentials).let { - // OAuth1Authorization( - // consumerKey = it.consumer_key, - // consumerSecret = it.consumer_secret, - // accessToken = it.access_token, - // accessSecret = it.access_token_secret, - // ) - // } - // } else { - - // } + val auth = try { + val url = URL(data.toString()) + if (url.host == twitterTonApiHost) { + // (account.credentials as OAuthCredentials).let { + // OAuth1Authorization( + // consumerKey = it.consumer_key, + // consumerSecret = it.consumer_secret, + // accessToken = it.access_token, + // accessSecret = it.access_token_secret, + // ) + // } + TODO("Waiting for LocalActiveAccount") + } else { + EmptyAuthorization() + } + } catch (e: MalformedURLException) { + EmptyAuthorization() + } rememberNetworkImagePainter( data = data, httpConfig = httpConfig, authorization = auth, effects = ImageEffects.Builder().apply(effects).build(), onImageStateChanged = { - Log.d("Image", "onStateChange:$it") if (state.value == NetworkImageState.LOADING) state.value = it } ) From 0c32a45dd0face02e7f874ffa0aae87541edfb16 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 16 Sep 2021 19:31:45 +0800 Subject: [PATCH 180/615] update account usage --- .../twiderex/viewmodel/StatusViewModel.kt | 29 ++--- .../viewmodel/compose/ComposeViewModel.kt | 107 ++++++++---------- .../MastodonComposeSearchHashtagViewModel.kt | 35 +++--- .../viewmodel/dm/DMConversationViewModel.kt | 16 ++- .../dm/DMNewConversationViewModel.kt | 39 +++---- .../viewmodel/lists/ListsViewModel.kt | 37 +++--- .../timeline/HomeTimelineViewModel.kt | 21 ++-- .../mastodon/LocalTimelineViewModel.kt | 26 ++--- 8 files changed, 139 insertions(+), 171 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index afe91491a..466f740d2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -26,8 +26,8 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -39,30 +39,23 @@ class StatusViewModel( private val account by lazy { accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { - account.flatMapLatest { - if (it != null) { - statusRepository.loadStatus(statusKey = statusKey, accountKey = it.accountKey) - } else { - emptyFlow() - } + account.mapNotNull { it }.flatMapLatest { + statusRepository.loadStatus(statusKey = statusKey, accountKey = it.accountKey) }.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - if (it != null) { - statusRepository.conversation( - statusKey = statusKey, - accountKey = it.accountKey, - platformType = it.type, - service = it.service - ) - } else { - emptyFlow() - } + account.mapNotNull { it }.flatMapLatest { + statusRepository.conversation( + statusKey = statusKey, + accountKey = it.accountKey, + platformType = it.type, + service = it.service + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 95f7d51a7..951fd7260 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -55,6 +55,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -192,15 +193,11 @@ open class ComposeViewModel( } val excludedReplyUserIds = MutableStateFlow>(emptyList()) val replyToUserName by lazy { - combine(account, status) { account, status -> - if (account != null && status != null) { - if (account.type == PlatformType.Twitter && composeType == ComposeType.Reply && statusKey != null) { - Extractor().extractMentionedScreennames( - status.htmlText - ).filter { it != account.user.screenName && it != status.user.screenName } - } else { - emptyList() - } + combine(account.mapNotNull { it }, status.mapNotNull { it }) { account, status -> + if (account.type == PlatformType.Twitter && composeType == ComposeType.Reply && statusKey != null) { + Extractor().extractMentionedScreennames( + status.htmlText + ).filter { it != account.user.screenName && it != status.user.screenName } } else { emptyList() } @@ -210,24 +207,20 @@ open class ComposeViewModel( val loadingReplyUser = MutableStateFlow(false) val replyToUser by lazy { - combine(account, replyToUserName) { account, list -> - if (account != null) { - if (list.isNotEmpty()) { - loadingReplyUser.value = true - try { - userRepository.lookupUsersByName( - list, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - } catch (e: Throwable) { - inAppNotification.notifyError(e) - emptyList() - } finally { - loadingReplyUser.value = false - } - } else { + combine(account.mapNotNull { it }, replyToUserName) { account, list -> + if (list.isNotEmpty()) { + loadingReplyUser.value = true + try { + userRepository.lookupUsersByName( + list, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + } catch (e: Throwable) { + inAppNotification.notifyError(e) emptyList() + } finally { + loadingReplyUser.value = false } } else { emptyList() @@ -254,43 +247,41 @@ open class ComposeViewModel( @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { - account.flatMapLatest { + account.mapNotNull { it }.flatMapLatest { account -> if (statusKey != null) { - it?.let { account -> - repository.loadStatus(statusKey, accountKey = account.accountKey) - .map { status -> - if (status != null && - textFieldValue.value.text.isEmpty() && - status.platformType == PlatformType.Mastodon && - status.mastodonExtra?.mentions != null && - composeType == ComposeType.Reply - ) { - val mentions = - status.mastodonExtra.mentions.mapNotNull { it.acct } - .filter { it != account.user.screenName } - .map { "@$it" } - .let { - if (status.user.userKey != account.user.userKey) { - listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it - } else { - it - } + repository.loadStatus(statusKey, accountKey = account.accountKey) + .map { status -> + if (status != null && + textFieldValue.value.text.isEmpty() && + status.platformType == PlatformType.Mastodon && + status.mastodonExtra?.mentions != null && + composeType == ComposeType.Reply + ) { + val mentions = + status.mastodonExtra.mentions.mapNotNull { it.acct } + .filter { it != account.user.screenName } + .map { "@$it" } + .let { + if (status.user.userKey != account.user.userKey) { + listOf(status.user.getDisplayScreenName(account.accountKey.host)) + it + } else { + it } - .distinctBy { it } - .takeIf { it.any() } - ?.joinToString(" ", postfix = " ") { it } - if (mentions != null) { - setText( - TextFieldValue( - mentions, - selection = TextRange(mentions.length) - ) + } + .distinctBy { it } + .takeIf { it.any() } + ?.joinToString(" ", postfix = " ") { it } + if (mentions != null) { + setText( + TextFieldValue( + mentions, + selection = TextRange(mentions.length) ) - } + ) } - status } - } ?: flowOf(null) + status + } } else { flowOf(null) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 60b7682e0..847a5a7e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -32,8 +32,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -47,23 +48,19 @@ class MastodonComposeSearchHashtagViewModel( val text = MutableStateFlow("") @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - val source = text.debounce(666L).flatMapLatest { - it.takeIf { it.isNotEmpty() }?.let { str -> - account.flatMapLatest { - it?.let { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - MastodonSearchHashtagPagingSource( - str, - account.service as MastodonService - ) - }.flow - } ?: emptyFlow() - } - } ?: emptyFlow() + val source = text.debounce(666L).filterNot { it.isEmpty() }.flatMapLatest { str -> + account.mapNotNull { it }.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + MastodonSearchHashtagPagingSource( + str, + account.service as MastodonService + ) + }.flow + } }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index bf55a8913..b4b066485 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -27,8 +27,8 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -42,14 +42,12 @@ class DMConversationViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - repository.dmConversationListSource( - accountKey = account.accountKey, - service = account.service as DirectMessageService, - lookupService = account.service as LookupService - ) - } ?: emptyFlow() + account.mapNotNull { it }.flatMapLatest { account -> + repository.dmConversationListSource( + accountKey = account.accountKey, + service = account.service as DirectMessageService, + lookupService = account.service as LookupService + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 64d6ef4e7..1a19622bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -35,9 +35,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -53,30 +54,26 @@ class DMNewConversationViewModel( val input = MutableStateFlow("") @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - val sourceFlow = input.debounce(666L).flatMapLatest { - it.takeIf { it.isNotEmpty() }?.let { str -> - account.flatMapLatest { - it?.let { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - str, - account.service as SearchService, - ) - }.flow - } ?: emptyFlow() - } - } ?: emptyFlow() + val sourceFlow = input.debounce(666L).filterNot { it.isEmpty() }.flatMapLatest { str -> + account.mapNotNull { it }.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + str, + account.service as SearchService, + ) + }.flow + } }.cachedIn(viewModelScope) fun createNewConversation(receiver: UiUser, onResult: (key: MicroBlogKey?) -> Unit) { viewModelScope.launch { - kotlin.runCatching { + runCatching { account.firstOrNull()?.let { account -> dmRepository.createNewConversation( receiver = receiver, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index 920a54e5d..b2eeaba3c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -51,35 +52,29 @@ class ListsViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - listsRepository.fetchLists( - accountKey = account.accountKey, - service = account.service as ListsService - ) - } ?: emptyFlow() + account.mapNotNull { it }.flatMapLatest { account -> + listsRepository.fetchLists( + accountKey = account.accountKey, + service = account.service as ListsService + ) }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) val ownerSource by lazy { - account.flatMapLatest { - it?.let { account -> - source.map { - it.filter { it.isOwner(account.user.userId) } - } - } ?: emptyFlow() + account.mapNotNull { it }.flatMapLatest { account -> + source.map { + it.filter { it.isOwner(account.user.userId) } + } }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) val subscribedSource by lazy { - account.flatMapLatest { - it?.let { account -> - source.map { pagingData -> - pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } - } - } ?: emptyFlow() + account.mapNotNull { it }.flatMapLatest { account -> + source.map { pagingData -> + pagingData.filter { !it.isOwner(account.user.userId) && it.isFollowed } + } }.cachedIn(viewModelScope) } } @@ -134,7 +129,7 @@ class ListsCreateViewModel( service = account.service as ListsService, title = title, description = description, - mode = if (private)ListsMode.PRIVATE.value else ListsMode.PUBLIC.value + mode = if (private) ListsMode.PRIVATE.value else ListsMode.PUBLIC.value ) } } @@ -192,7 +187,7 @@ class ListsModifyViewModel( listId = listId, title = title, description = description, - mode = if (private)ListsMode.PRIVATE.value else ListsMode.PUBLIC.value + mode = if (private) ListsMode.PRIVATE.value else ListsMode.PUBLIC.value ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 4c031e7d7..6d12d5241 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class HomeTimelineViewModel( @@ -42,23 +43,19 @@ class HomeTimelineViewModel( @OptIn(ExperimentalCoroutinesApi::class) override val pagingMediator by lazy { - account.mapLatest { - it?.let { - HomeTimelineMediator( - it.service as TimelineService, - it.accountKey, - database, - ) - } + account.mapNotNull { it }.mapLatest { + HomeTimelineMediator( + it.service as TimelineService, + it.accountKey, + database, + ) }.asStateIn(viewModelScope, null) } @OptIn(ExperimentalCoroutinesApi::class) override val savedStateKey by lazy { - account.mapLatest { - it?.let { - "${it.accountKey}_home" - } + account.mapNotNull { it }.mapLatest { + "${it.accountKey}_home" }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 79d15e4a8..8c58dd556 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -28,7 +28,9 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.timeline.mastodon.LocalTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class LocalTimelineViewModel( @@ -40,23 +42,21 @@ class LocalTimelineViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) override val pagingMediator by lazy { - account.map { - it?.let { - LocalTimelineMediator( - it.service as MastodonService, - it.accountKey, - database, - ) - } + account.mapNotNull { it }.mapLatest { + LocalTimelineMediator( + it.service as MastodonService, + it.accountKey, + database, + ) }.asStateIn(viewModelScope, null) } + @OptIn(ExperimentalCoroutinesApi::class) override val savedStateKey by lazy { - account.map { - it?.let { - "${it.accountKey}_local" - } + account.mapNotNull { it }.mapLatest { + "${it.accountKey}_local" }.asStateIn(viewModelScope, null) } } From efb880a738dd310cd3335f2159c5cf015f233e0b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 16 Sep 2021 19:32:00 +0800 Subject: [PATCH 181/615] add lists view model test --- common/build.gradle.kts | 4 + .../twidere/twiderex/MainThreadTestBase.kt | 1 + .../viewmodel/lists/ListsViewModelTest.kt | 123 ++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index d289f69db..f5208be44 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -53,6 +53,9 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") + implementation("io.mockk:mockk-common:1.12.0") + implementation("io.mockk:mockk:1.12.0") + implementation("org.jetbrains.kotlin:kotlin-reflect:${Versions.Kotlin.lang}") } } val androidMain by getting { @@ -84,6 +87,7 @@ kotlin { implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") implementation("androidx.room:room-testing:${Versions.room}") + implementation("io.mockk:mockk-android:1.12.0") } } val desktopMain by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt index d2ec2719f..51afdeaa3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt @@ -41,5 +41,6 @@ internal open class MainThreadTestBase { @After open fun tearDown() { Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher + mainThreadSurrogate.close() } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt new file mode 100644 index 000000000..4083c6355 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt @@ -0,0 +1,123 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.lists + +import androidx.paging.PagingData +import com.twidere.twiderex.MainThreadTestBase +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.AmUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.ListsRepository +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class ListsViewModelTest : MainThreadTestBase() { + + @MockK + private lateinit var mockRepository: ListsRepository + + @MockK + private lateinit var mockAccountRepository: AccountRepository + + @MockK + private lateinit var mockAccount: AccountDetails + + @MockK + private lateinit var mockUser: AmUser + + @MockK + private lateinit var ownerList: UiList + + @MockK + private lateinit var subscribeList: UiList + + private lateinit var viewModel: ListsViewModel + + override fun setUp() { + super.setUp() + MockKAnnotations.init(this, relaxUnitFun = true) + every { mockRepository.fetchLists(any(), any()) }.returns( + flowOf( + PagingData.from( + listOf( + ownerList, + subscribeList + ) + ) + ) + ) + every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) + every { ownerList.isOwner(any()) }.returns(true) + every { subscribeList.isOwner(any()) }.returns(false) + every { ownerList.title }.returns("owner") + every { subscribeList.title }.returns("subscribe") + every { subscribeList.isFollowed }.returns(true) + every { mockAccount.user }.returns(mockUser) + every { mockAccount.accountKey }.returns(MicroBlogKey.twitter("123")) + every { mockAccount.service }.returns(MockListsService()) + every { mockUser.userId }.returns("123") + viewModel = ListsViewModel(mockRepository, mockAccountRepository) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun source_containsAllLists(): Unit = runBlocking(Dispatchers.Main) { + // check the source + viewModel.source.first().let { + val sourceItems = it.collectDataForTest() + assertEquals(2, sourceItems.size) + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun ownerSource_containsOwnedLists(): Unit = runBlocking(Dispatchers.Main) { + // make sure ownerSource only emit data which isOwner() returns true + viewModel.ownerSource.first().let { + val ownerItems = it.collectDataForTest() + assertEquals(1, ownerItems.size) + assertEquals("owner", ownerItems[0].title) + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun subscribeSource_containsSubscribedLists(): Unit = runBlocking(Dispatchers.Main) { + // make sure ownerSource only emit data which isOwner() returns true + viewModel.subscribedSource.first().let { + val subscribeItems = it.collectDataForTest() + assertEquals(1, subscribeItems.size) + assertEquals("subscribe", subscribeItems[0].title) + } + } +} \ No newline at end of file From d0d4c6609264f7a8ec63de94a439563d9b2abd14 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 13:53:26 +0800 Subject: [PATCH 182/615] fix build --- gradle.properties | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c60902fc7..a97721aa0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,6 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -20,4 +19,14 @@ android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.injected.testOnly=false -kotlin.mpp.enableGranularSourceSetsMetadata=true \ No newline at end of file +kotlin.mpp.enableGranularSourceSetsMetadata=true +kapt.use.worker.api=true +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC \ + --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ + --add-opens jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ No newline at end of file From 590c92e54f903abc3c684ae3f9026e4570da543e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Sep 2021 14:26:21 +0800 Subject: [PATCH 183/615] add lists view model test --- .../com/twidere/twiderex/mock/Observer.kt | 25 ++ .../twiderex/mock/service/MockListsService.kt | 2 + .../twiderex/viewmodel/ViewModelTestBase.kt | 31 +++ .../lists/ListsCreateViewModelTest.kt | 135 +++++++++++ .../lists/ListsModifyViewModelTest.kt | 226 ++++++++++++++++++ .../viewmodel/lists/ListsViewModelTest.kt | 8 +- 6 files changed, 422 insertions(+), 5 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt new file mode 100644 index 000000000..91198c36d --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.mock + +interface Observer { + fun onChanged(value: T) +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index 1cea3100b..062b90086 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -25,6 +25,7 @@ import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.model.IListModel import com.twidere.services.microblog.model.IUser import com.twidere.services.twitter.model.TwitterList +import com.twidere.services.twitter.model.exceptions.TwitterApiException import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.mock.model.mockIListModel import com.twidere.twiderex.mock.model.mockIUser @@ -81,6 +82,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() override suspend fun destroyList(listId: String) { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") // do nothing } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt new file mode 100644 index 000000000..382c39ab0 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import com.twidere.twiderex.MainThreadTestBase +import io.mockk.MockKAnnotations + +internal abstract class ViewModelTestBase : MainThreadTestBase() { + override fun setUp() { + super.setUp() + MockKAnnotations.init(this, relaxUnitFun = true) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt new file mode 100644 index 000000000..3c4d07209 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt @@ -0,0 +1,135 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.lists + +import com.twidere.twiderex.mock.Observer +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.notification.NotificationEvent +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.ListsRepository +import com.twidere.twiderex.viewmodel.ViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class ListsCreateViewModelTest : ViewModelTestBase() { + + private lateinit var mockRepository: ListsRepository + + @MockK + private lateinit var mockAppNotification: InAppNotification + + @MockK + private lateinit var mockAccountRepository: AccountRepository + + private val mockAccount: AccountDetails = mockk { + every { service }.returns(MockListsService()) + every { accountKey }.returns(MicroBlogKey.twitter("123")) + } + + @MockK + private lateinit var mockSuccessObserver: Observer + + @MockK + private lateinit var mockLoadingObserver: Observer + + private var errorNotification: NotificationEvent? = null + + private lateinit var createViewModel: ListsCreateViewModel + + private val scope = CoroutineScope(Dispatchers.Main) + + override fun setUp() { + super.setUp() + every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) + mockRepository = ListsRepository(MockCacheDatabase()) + createViewModel = ListsCreateViewModel( + mockAppNotification, + mockRepository, + mockAccountRepository + ) { success, _ -> + mockSuccessObserver.onChanged(success) + } + every { mockAppNotification.show(any()) }.answers { + errorNotification = arg(0) + } + errorNotification = null + mockSuccessObserver.onChanged(false) + scope.launch { + createViewModel.loading.collect { + mockLoadingObserver.onChanged(it) + } + } + } + + @Test + fun createList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + async { + createViewModel.createList(title = "title", private = false) + }.await() + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + } + + @Test + fun createList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + assertNull(errorNotification) + async { + createViewModel.createList(title = "error", private = false) + }.await() + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + assertNotNull(errorNotification) + } + + private fun verifySuccessAndLoadingBefore( + loadingObserver: Observer, + successObserver: Observer + ) { + verify(exactly = 1) { loadingObserver.onChanged(false) } + verify { successObserver.onChanged(false) } + } + + private fun verifySuccessAndLoadingAfter( + loadingObserver: Observer, + successObserver: Observer, + success: Boolean + ) { + verify(exactly = 1) { loadingObserver.onChanged(true) } + verify(exactly = 1) { loadingObserver.onChanged(false) } + verify(exactly = if (success) 1 else 2) { successObserver.onChanged(success) } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt new file mode 100644 index 000000000..73e461da0 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt @@ -0,0 +1,226 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.lists + +import com.twidere.twiderex.mock.Observer +import com.twidere.twiderex.mock.db.MockCacheDatabase +import com.twidere.twiderex.mock.service.MockListsService +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.notification.NotificationEvent +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.ListsRepository +import com.twidere.twiderex.viewmodel.ViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +internal class ListsModifyViewModelTest : ViewModelTestBase() { + + private var mockRepository: ListsRepository = ListsRepository(MockCacheDatabase()) + + @MockK + private lateinit var mockAppNotification: InAppNotification + + @MockK + private lateinit var mockAccountRepository: AccountRepository + + private val mockAccount: AccountDetails = mockk { + every { service }.returns(MockListsService()) + every { accountKey }.returns(MicroBlogKey.twitter("123")) + } + + @MockK + private lateinit var mockSuccessObserver: Observer + + @MockK + private lateinit var mockLoadingObserver: Observer + + private var errorNotification: NotificationEvent? = null + + private lateinit var modifyViewModel: ListsModifyViewModel + + private val scope = CoroutineScope(Dispatchers.Main) + + @Test + fun updateList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + suspendCoroutine { + modifyViewModel.editList( + listId = "123", + title = "title", + private = false + ) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + } + + @Test + fun updateList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + Assert.assertNull(errorNotification) + suspendCoroutine { + modifyViewModel.editList( + listId = "error", + title = "name", + private = false + ) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + Assert.assertNotNull(errorNotification) + } + + @Test + fun deleteList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + suspendCoroutine { + modifyViewModel.deleteList(listId = "123", MicroBlogKey.Empty) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + } + + @Test + fun deleteList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + Assert.assertNull(errorNotification) + suspendCoroutine { + modifyViewModel.deleteList(listId = "error", MicroBlogKey.Empty) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + Assert.assertNotNull(errorNotification) + } + + @Test + fun subscribeList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + suspendCoroutine { + modifyViewModel.subscribeList(MicroBlogKey.twitter("123")) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + } + + @Test + fun subscribeList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + Assert.assertNull(errorNotification) + suspendCoroutine { + modifyViewModel.subscribeList(MicroBlogKey.twitter("error")) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + Assert.assertNotNull(errorNotification) + } + + @Test + fun unsubscribeList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + suspendCoroutine { + modifyViewModel.unsubscribeList(MicroBlogKey.twitter("123")) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + } + + @Test + fun unsubscribeList_failedExpectFalseAndShowNotification(): Unit = + runBlocking(Dispatchers.Main) { + verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + Assert.assertNull(errorNotification) + suspendCoroutine { + modifyViewModel.unsubscribeList(MicroBlogKey.twitter("error")) { success, _ -> + mockSuccessObserver.onChanged(success) + it.resume(success) + } + } + verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + Assert.assertNotNull(errorNotification) + } + + override fun setUp() { + super.setUp() + every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) + modifyViewModel = ListsModifyViewModel( + mockRepository, + mockAppNotification, + mockAccountRepository, + listKey = MicroBlogKey.Empty, + ) + every { mockAppNotification.show(any()) }.answers { + errorNotification = arg(0) + } + errorNotification = null + mockSuccessObserver.onChanged(false) + scope.launch { + modifyViewModel.loading.collect { + mockLoadingObserver.onChanged(it) + } + } + } + + private fun verifySuccessAndLoadingBefore( + loadingObserver: Observer, + successObserver: Observer + ) { + verify(exactly = 1) { loadingObserver.onChanged(false) } + verify { successObserver.onChanged(false) } + } + + private fun verifySuccessAndLoadingAfter( + loadingObserver: Observer, + successObserver: Observer, + success: Boolean + ) { + verify(exactly = 1) { loadingObserver.onChanged(true) } + verify(exactly = 1) { loadingObserver.onChanged(false) } + verify(exactly = if (success) 1 else 2) { successObserver.onChanged(success) } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt index 4083c6355..82a983ddc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.PagingData -import com.twidere.twiderex.MainThreadTestBase import com.twidere.twiderex.mock.paging.collectDataForTest import com.twidere.twiderex.mock.service.MockListsService import com.twidere.twiderex.model.AccountDetails @@ -30,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository -import io.mockk.MockKAnnotations +import com.twidere.twiderex.viewmodel.ViewModelTestBase import io.mockk.every import io.mockk.impl.annotations.MockK import kotlinx.coroutines.Dispatchers @@ -41,7 +40,7 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals -internal class ListsViewModelTest : MainThreadTestBase() { +internal class ListsViewModelTest : ViewModelTestBase() { @MockK private lateinit var mockRepository: ListsRepository @@ -65,7 +64,6 @@ internal class ListsViewModelTest : MainThreadTestBase() { override fun setUp() { super.setUp() - MockKAnnotations.init(this, relaxUnitFun = true) every { mockRepository.fetchLists(any(), any()) }.returns( flowOf( PagingData.from( @@ -120,4 +118,4 @@ internal class ListsViewModelTest : MainThreadTestBase() { assertEquals("subscribe", subscribeItems[0].title) } } -} \ No newline at end of file +} From d3570fa31ebff3c194b4baa9964023b0151c7d50 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Sep 2021 14:49:00 +0800 Subject: [PATCH 184/615] init sqldelight for desktop and add search.sq --- .github/workflows/common.yml | 7 +++- build.gradle.kts | 1 + buildSrc/src/main/kotlin/Dependencies.kt | 2 + buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 21 ++++++++-- .../twiderex/sqldelight/table/search.sq | 31 +++++++++++++++ .../twiderex/db/SearchQueriesImplTest.kt | 38 +++++++++++++++++++ .../twiderex/db/base/BaseAppDatabaseTest.kt | 35 +++++++++++++++++ 8 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 744331329..47ad201c5 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -22,7 +22,7 @@ jobs: java-version: 11 - name: Build with Gradle - run: ./gradlew :common:spotlessCheck :desktop:build + run: ./gradlew :common:spotlessCheck :common:build - name: Upload build reports uses: actions/upload-artifact@v2 @@ -45,9 +45,12 @@ jobs: - name: Set up Android SDK License run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses - - name: Build with Gradle + - name: Run test in commonTest and androidTest run: ./gradlew :common:test + - name: Run test in desktopTest + run: ./gradlew :common:desktopTest + - name: Upload test results if: always() uses: actions/upload-artifact@v2 diff --git a/build.gradle.kts b/build.gradle.kts index 8ee741f0a..0eedd0e34 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,6 +9,7 @@ buildscript { dependencies { classpath(kotlin("gradle-plugin", version = Versions.Kotlin.lang)) classpath("com.android.tools.build:gradle:${Versions.agp}") + classpath("com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}") } } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 3d228226b..d5e9163e4 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -9,6 +9,8 @@ fun Project.configRepository() { mavenCentral() maven("https://jitpack.io") maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://www.jetbrains.com/intellij-repository/releases") + maven("https://cache-redirector.jetbrains.com/intellij-dependencies") } } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b076107c9..d93cd3c45 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -48,4 +48,5 @@ object Versions { const val espressoVersion = "3.4.0-rc01" const val koin = "3.1.2" const val moko = "0.17.2" + const val sqlDelight = "1.5.1" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b4b98eaef..7bd2dca39 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -9,6 +9,18 @@ plugins { kotlin("kapt") id("com.google.devtools.ksp").version(Versions.ksp) id("dev.icerock.mobile.multiplatform-resources") version Versions.moko + id("com.squareup.sqldelight") +} + +sqldelight { + database("SqlDelightAppDatabase") { + packageName = "${Package.id}.sqldelight" + sourceFolders = listOf("sqldelight/app") + } + database("SqlDelightCacheDatabase") { + packageName = "${Package.id}.sqldelight" + sourceFolders = listOf("sqldelight/cache") + } } group = Package.group @@ -24,9 +36,6 @@ kotlin { compilations.all { kotlinOptions.jvmTarget = Versions.Java.jvmTarget } - testRuns["test"].executionTask.configure { - useJUnitPlatform() - } } sourceSets { val commonMain by getting { @@ -80,7 +89,11 @@ kotlin { implementation("androidx.room:room-testing:${Versions.room}") } } - val desktopMain by getting + val desktopMain by getting { + dependencies { + implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") + } + } val desktopTest by getting } } diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq new file mode 100644 index 000000000..b7866cbb8 --- /dev/null +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq @@ -0,0 +1,31 @@ +CREATE TABLE search ( + content TEXT NOT NULL, + lastActive INTEGER NOT NULL, + saved INTEGER DEFAULT 0 NOT NULL, + accountKey TEXT NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_search_content_accountKey ON search (content, accountKey); + +insert: +INSERT INTO search(content, lastActive, saved, accountKey) +VALUES(?, ?, ?, ?); + +get: +SELECT * FROM search WHERE content == :content AND accountKey == :accountKey; + +getAll: +SELECT * FROM search WHERE accountKey == :accountKey ORDER BY lastActive DESC; + +getHistories: +SELECT * FROM search WHERE saved == 0 AND accountKey == :accountKey ORDER BY lastActive DESC; + +getSaved: +SELECT * FROM search WHERE saved == 1 AND accountKey == :accountKey ORDER BY lastActive DESC; + +remove: +DELETE FROM search WHERE content == :content AND accountKey == :accountKey; + +clear: +DELETE FROM search WHERE saved == 0; + diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt new file mode 100644 index 000000000..5282252c9 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.base.BaseAppDatabaseTest +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +// Todo complete test +internal class SearchQueriesImplTest : BaseAppDatabaseTest() { + @Test + fun insertSearch(): Unit = runBlocking { + val query = database.searchQueries + val accountKey = MicroBlogKey.twitter("test") + query.insert("test", System.currentTimeMillis(), 0, accountKey = accountKey.toString()) + assertNotNull(query.get("test", accountKey.toString())) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt new file mode 100644 index 000000000..9eb527a5f --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.base + +import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver +import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import org.junit.Before + +internal open class BaseAppDatabaseTest { + protected lateinit var database: SqlDelightAppDatabase + @Before + fun setUp() { + val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + SqlDelightAppDatabase.Schema.create(driver) + database = SqlDelightAppDatabase(driver) + } +} From 2cefcc5c8c9df30ad74de1a09d2dfd03ceae5d9c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Sep 2021 15:12:28 +0800 Subject: [PATCH 185/615] opt common ci config --- .github/workflows/common.yml | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 47ad201c5..be2d5ef5b 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -10,29 +10,8 @@ on: jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK - uses: actions/setup-java@v1 - with: - java-version: 11 - - - name: Build with Gradle - run: ./gradlew :common:spotlessCheck :common:build - - - name: Upload build reports - uses: actions/upload-artifact@v2 - with: - name: build-reports - path: '**/build/reports' - unit-test: runs-on: ubuntu-latest - needs: build timeout-minutes: 30 steps: - uses: actions/checkout@v2 @@ -60,7 +39,7 @@ jobs: connected-android-test: runs-on: macOS-11 - needs: build + needs: unit-test timeout-minutes: 60 strategy: From c475a9ec834f993ba91f6def26b30f98f57075e5 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 16:05:35 +0800 Subject: [PATCH 186/615] init DesktopVideoPlayer.kt --- common/build.gradle.kts | 6 +- .../component/video/AndroidVideoPlayer.kt | 32 +++ .../kotlin/com/twidere/twiderex/App.kt | 26 ++- .../twiderex/component/video/VideoPlayer.kt | 70 +++++++ .../twiderex/component/video/VideoPool.kt | 81 ++++++++ .../component/video/DesktopVideoPlayer.kt | 190 ++++++++++++++++++ 6 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a4537f0db..1ad1b59bf 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -82,7 +82,11 @@ kotlin { implementation("androidx.room:room-testing:${Versions.room}") } } - val desktopMain by getting + val desktopMain by getting { + dependencies { + implementation("uk.co.caprica:vlcj:4.7.1") + } + } val desktopTest by getting } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt new file mode 100644 index 000000000..a5c52094a --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.video + +import androidx.compose.runtime.Composable + +@Composable +actual fun VideoPlayerImpl( + url: String, + width: Int, + height: Int, + isPlaying: Boolean +) { +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 15aa68d62..432a6f7b4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -20,11 +20,20 @@ */ package com.twidere.twiderex +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.video.VideoPlayer import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.ext.get @@ -35,7 +44,22 @@ fun App() { ) { MaterialTheme { Scaffold { - Text("Twidere X!") + Column { + Text("Twidere X!") + LazyColumn { + for (i in 0..5) { + item { + Box(modifier = Modifier.background(Color.DarkGray).padding(100.dp)) { + VideoPlayer( + url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + width = 640, + height = 480 + ) + } + } + } + } + } } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt new file mode 100644 index 000000000..d01d8f052 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.video + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue + +expect fun VideoPlayerImpl( + url: String, + width: Int, + height: Int, + isPlaying: Boolean +) + +@Composable +fun VideoPlayer(url: String, width: Int, height: Int) { + Column { + var isPlaying by remember { + mutableStateOf(true) + } + VideoPlayerImpl( + url, + width, + height, + isPlaying + ) + Row { + Button( + onClick = { + isPlaying = true + } + ) { + Text("play") + } + + Button( + onClick = { + isPlaying = false + } + ) { + Text("pause") + } + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt new file mode 100644 index 000000000..b094452e6 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt @@ -0,0 +1,81 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.video + +import androidx.compose.ui.geometry.Rect +import java.util.concurrent.ConcurrentHashMap +import kotlin.math.abs + +object VideoPool { + + const val DEBOUNCE_DELAY = 500L + + private val pool = ConcurrentHashMap() + + fun get(url: String): Long { + return pool[url] ?: 1L + } + + fun set(url: String, position: Long) { + pool[url] = position + } + + private val positionPool = ConcurrentHashMap() + + fun setRect(videoKey: String, rect: Rect) { + if (rect.top <= 0.0f && rect.bottom <= 0.0f) { + removeRect(videoKey) + } else { + positionPool[videoKey] = rect + } + } + + fun removeRect(url: String) { + positionPool.remove(url) + } + + fun containsMiddleLine(videoKey: String, middle: Float): Boolean { + positionPool[videoKey]?.let { + return it.top <= middle && it.bottom >= middle + } + return false + } + + fun isMostCenter(videoKey: String, middle: Float): Boolean { + if (positionPool.size == 0) { + return false + } + if (positionPool.size == 1) { + return true + } + var centerUrl = videoKey + var minGap = Float.MAX_VALUE + positionPool.forEach { + abs((it.value.top + it.value.bottom) / 2 - middle).let { curGap -> + if (curGap < minGap) { + minGap = curGap + centerUrl = it.key + } + } + } + return videoKey == centerUrl + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt new file mode 100644 index 000000000..d2e0dd6b4 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt @@ -0,0 +1,190 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.video + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.awt.SwingPanel +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.rememberWindowState +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import uk.co.caprica.vlcj.factory.discovery.NativeDiscovery +import uk.co.caprica.vlcj.media.MediaEventAdapter +import uk.co.caprica.vlcj.player.base.MediaPlayer +import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter +import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent +import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent +import java.util.Locale + +@Composable +actual fun VideoPlayerImpl( + url: String, + width: Int, + height: Int, + isPlaying: Boolean +) { +// println("Video player for $url") + NativeDiscovery().discover() + // var resumed by remember(url) { + // mutableStateOf(false) + // } + val mediaPlayerComponent = remember(url) { + // see https://github.com/caprica/vlcj/issues/887#issuecomment-503288294 + // for why we're using CallbackMediaPlayerComponent for macOS. + if (isMacOS()) { + CallbackMediaPlayerComponent() + } else { + EmbeddedMediaPlayerComponent() + } + } + var windowInfo = LocalWindowInfo.current + + var windowState = rememberWindowState() + + LaunchedEffect(url) { + while (true) { + delay(1000) + println(windowState.isMinimized) + println(windowInfo.isWindowFocused) + } + } +// rememberWindowState. + +// Local + +// val windowFocus = Listener +// current.isWindowFocused + var middleLine = 0.0f + val composableScope = rememberCoroutineScope() + + val videoKey = remember { + url + System.nanoTime() + } + + var isMostCenter by remember(url) { + mutableStateOf(false) + } + DisposableEffect(url) { + val listener = object : MediaPlayerEventAdapter() { + override fun positionChanged(mediaPlayer: MediaPlayer?, newPosition: Float) { + super.positionChanged(mediaPlayer, newPosition) + } + + override fun playing(mediaPlayer: MediaPlayer?) { + super.playing(mediaPlayer) + } + + override fun mediaPlayerReady(mediaPlayer: MediaPlayer?) { + super.mediaPlayerReady(mediaPlayer) + } + } + mediaPlayerComponent.mediaPlayer().events() + .addMediaEventListener(object : MediaEventAdapter() { + }) + mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(listener) + mediaPlayerComponent.mediaPlayer().media().prepare(url) + onDispose { + mediaPlayerComponent.mediaPlayer().events().removeMediaPlayerEventListener(listener) + mediaPlayerComponent.mediaPlayer().release() + } + } + + var debounceJob: Job? = remember { + null + } + + return Box( + modifier = Modifier.width(500.dp).height(500.dp).onGloballyPositioned { coordinates -> + if (middleLine == 0.0f) { + var rootCoordinates = coordinates + while (rootCoordinates.parentCoordinates != null) { + rootCoordinates = rootCoordinates.parentCoordinates!! + } + rootCoordinates.boundsInWindow().run { + middleLine = (top + bottom) / 2 + } + } + coordinates.boundsInWindow().run { + VideoPool.setRect(videoKey, this) + if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { + debounceJob?.cancel() + debounceJob = composableScope.launch { + delay(VideoPool.DEBOUNCE_DELAY) + if (VideoPool.containsMiddleLine(videoKey, middleLine)) { + isMostCenter = true + } + } + } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { + isMostCenter = false + } + } + }, + ) { + SwingPanel( + background = Color.Transparent, + factory = { + mediaPlayerComponent + } + ) { + val player = it.mediaPlayer() + val controls = player.controls() + if (isPlaying && isMostCenter && !windowState.isMinimized) { + controls.play() + } else { + controls.setPause(true) + } + } + } +} + +/** + * To return mediaPlayer from player components. + * The method names are same, but they don't share the same parent/interface. + * That's why need this method. + */ +private fun Any.mediaPlayer(): MediaPlayer { + return when (this) { + is CallbackMediaPlayerComponent -> mediaPlayer() + is EmbeddedMediaPlayerComponent -> mediaPlayer() + else -> throw IllegalArgumentException("You can only call mediaPlayer() on vlcj player component") + } +} + +private fun isMacOS(): Boolean { + val os = System.getProperty("os.name", "generic").lowercase(Locale.ENGLISH) + return os.indexOf("mac") >= 0 || os.indexOf("darwin") >= 0 +} From 4c7e369cfdd844f82415a417e6c8ac97b6f11b3e Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 16:27:43 +0800 Subject: [PATCH 187/615] add lifecycle --- .../component/video/DesktopVideoPlayer.kt | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt index d2e0dd6b4..23289677b 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -36,14 +35,14 @@ import androidx.compose.ui.awt.SwingPanel import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.rememberWindowState import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LifecycleObserver +import moe.tlaster.precompose.ui.LocalLifecycleOwner import uk.co.caprica.vlcj.factory.discovery.NativeDiscovery -import uk.co.caprica.vlcj.media.MediaEventAdapter import uk.co.caprica.vlcj.player.base.MediaPlayer import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent @@ -57,11 +56,12 @@ actual fun VideoPlayerImpl( height: Int, isPlaying: Boolean ) { -// println("Video player for $url") + NativeDiscovery().discover() - // var resumed by remember(url) { - // mutableStateOf(false) - // } + val lifecycle = LocalLifecycleOwner.current.lifecycle + var active by remember(url) { + mutableStateOf(true) + } val mediaPlayerComponent = remember(url) { // see https://github.com/caprica/vlcj/issues/887#issuecomment-503288294 // for why we're using CallbackMediaPlayerComponent for macOS. @@ -71,23 +71,7 @@ actual fun VideoPlayerImpl( EmbeddedMediaPlayerComponent() } } - var windowInfo = LocalWindowInfo.current - - var windowState = rememberWindowState() - - LaunchedEffect(url) { - while (true) { - delay(1000) - println(windowState.isMinimized) - println(windowInfo.isWindowFocused) - } - } -// rememberWindowState. - -// Local -// val windowFocus = Listener -// current.isWindowFocused var middleLine = 0.0f val composableScope = rememberCoroutineScope() @@ -112,11 +96,24 @@ actual fun VideoPlayerImpl( super.mediaPlayerReady(mediaPlayer) } } - mediaPlayerComponent.mediaPlayer().events() - .addMediaEventListener(object : MediaEventAdapter() { - }) mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(listener) mediaPlayerComponent.mediaPlayer().media().prepare(url) + + val lifecycleObserver = object : LifecycleObserver { + override fun onStateChanged(state: Lifecycle.State) { + when (state) { + Lifecycle.State.Active -> { + active = true + } + Lifecycle.State.InActive -> { + active = false + } + else -> {} + } + } + } + lifecycle.addObserver(lifecycleObserver) + onDispose { mediaPlayerComponent.mediaPlayer().events().removeMediaPlayerEventListener(listener) mediaPlayerComponent.mediaPlayer().release() @@ -162,7 +159,7 @@ actual fun VideoPlayerImpl( ) { val player = it.mediaPlayer() val controls = player.controls() - if (isPlaying && isMostCenter && !windowState.isMinimized) { + if (isPlaying && isMostCenter && active) { controls.play() } else { controls.setPause(true) From ef3dee6689fea6873a5ddeca5ac762dc9d2b0ed6 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 16:42:47 +0800 Subject: [PATCH 188/615] format --- .../kotlin/com/twidere/twiderex/App.kt | 23 ++---------------- .../twiderex/component/video/VideoPlayer.kt | 24 +++++++++++++++++++ .../component/video/DesktopVideoPlayer.kt | 1 + 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 432a6f7b4..1e0875b09 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -20,20 +20,13 @@ */ package com.twidere.twiderex -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.twidere.twiderex.component.video.VideoPlayer +import com.twidere.twiderex.component.video.VideoPreview import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.ext.get @@ -46,19 +39,7 @@ fun App() { Scaffold { Column { Text("Twidere X!") - LazyColumn { - for (i in 0..5) { - item { - Box(modifier = Modifier.background(Color.DarkGray).padding(100.dp)) { - VideoPlayer( - url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", - width = 640, - height = 480 - ) - } - } - } - } + VideoPreview() } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt index d01d8f052..894f35681 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt @@ -20,8 +20,12 @@ */ package com.twidere.twiderex.component.video +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -29,6 +33,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp expect fun VideoPlayerImpl( url: String, @@ -68,3 +75,20 @@ fun VideoPlayer(url: String, width: Int, height: Int) { } } } + +@Composable +fun VideoPreview() { + LazyColumn { + for (i in 0..5) { + item { + Box(modifier = Modifier.background(Color.DarkGray).padding(100.dp)) { + VideoPlayer( + url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + width = 640, + height = 480 + ) + } + } + } + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt index 23289677b..3f2d22ee1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt @@ -116,6 +116,7 @@ actual fun VideoPlayerImpl( onDispose { mediaPlayerComponent.mediaPlayer().events().removeMediaPlayerEventListener(listener) + lifecycle.removeObserver(lifecycleObserver) mediaPlayerComponent.mediaPlayer().release() } } From b9dcc626dc0101e03ed338777d3042da4ba44e25 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Sep 2021 19:16:02 +0800 Subject: [PATCH 189/615] refactor account flow usage --- .../twiderex/viewmodel/MediaViewModel.kt | 16 ++++----- .../twiderex/viewmodel/StatusViewModel.kt | 2 +- .../compose/ComposeSearchUserViewModel.kt | 31 ++++++++-------- .../viewmodel/compose/ComposeViewModel.kt | 19 +++++----- .../MastodonComposeSearchHashtagViewModel.kt | 2 +- .../viewmodel/dm/DMConversationViewModel.kt | 2 +- .../twiderex/viewmodel/dm/DMEventViewModel.kt | 32 ++++++++--------- .../dm/DMNewConversationViewModel.kt | 2 +- .../lists/ListsAddMemberViewModel.kt | 3 +- .../lists/ListsSearchUserViewModel.kt | 31 ++++++++-------- .../viewmodel/lists/ListsTimelineViewModel.kt | 15 ++++---- .../viewmodel/lists/ListsUserViewModel.kt | 32 ++++++++--------- .../viewmodel/lists/ListsViewModel.kt | 19 +++++----- .../mastodon/MastodonHashtagViewModel.kt | 16 ++++----- .../MastodonSearchHashtagViewModel.kt | 28 +++++++-------- .../viewmodel/search/SearchInputViewModel.kt | 12 +++---- .../viewmodel/search/SearchSaveViewModel.kt | 3 +- .../viewmodel/search/SearchTweetsViewModel.kt | 20 +++++------ .../viewmodel/search/SearchUserViewModel.kt | 34 +++++++++--------- .../settings/AccountNotificationViewModel.kt | 7 ++-- .../viewmodel/settings/LayoutViewModel.kt | 5 +-- .../timeline/HomeTimelineViewModel.kt | 2 +- .../timeline/MentionsTimelineViewModel.kt | 35 +++++++++---------- .../timeline/NotificationTimelineViewModel.kt | 35 +++++++++---------- .../mastodon/FederatedTimelineViewModel.kt | 19 +++++----- .../mastodon/LocalTimelineViewModel.kt | 2 +- .../viewmodel/trend/TrendViewModel.kt | 16 ++++----- .../search/TwitterSearchMediaViewModel.kt | 7 ++-- .../twitter/user/TwitterUserViewModel.kt | 25 ++++++------- .../viewmodel/user/FollowersViewModel.kt | 10 +++--- .../viewmodel/user/FollowingViewModel.kt | 9 +++-- .../user/UserFavouriteTimelineViewModel.kt | 2 +- .../viewmodel/user/UserTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/user/UserViewModel.kt | 6 ++-- 34 files changed, 224 insertions(+), 277 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 792a3b709..40b488bcf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -28,9 +28,9 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.StatusRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -42,7 +42,7 @@ class MediaViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { @@ -70,14 +70,10 @@ class MediaViewModel( @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { - if (it != null) { - repository.loadStatus( - statusKey = statusKey, - accountKey = it.accountKey, - ) - } else { - emptyFlow() - } + repository.loadStatus( + statusKey = statusKey, + accountKey = it.accountKey, + ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index 466f740d2..bd01ab90a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -37,7 +37,7 @@ class StatusViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index b498c001c..f1cc40264 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -41,7 +42,7 @@ class ComposeSearchUserViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val text = MutableStateFlow("") @@ -49,21 +50,19 @@ class ComposeSearchUserViewModel( @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { str -> - account.flatMapLatest { - it?.let { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - str, - account.service as SearchService - ) - }.flow - } ?: emptyFlow() + account.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + str, + account.service as SearchService + ) + }.flow } } ?: emptyFlow() }.cachedIn(viewModelScope) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 951fd7260..047a905b4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -162,21 +162,19 @@ open class ComposeViewModel( val composeType: ComposeType, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } open val draftId: String = UUID.randomUUID().toString() @OptIn(ExperimentalCoroutinesApi::class) val emojis by lazy { - account.flatMapLatest { - it?.let { account -> - if (account.type == PlatformType.Mastodon) { - MastodonEmojiCache.get(account) - } else { - emptyFlow() - } - } ?: emptyFlow() + account.flatMapLatest { account -> + if (account.type == PlatformType.Mastodon) { + MastodonEmojiCache.get(account) + } else { + emptyFlow() + } }.asStateIn(viewModelScope, emptyList()) } @@ -364,12 +362,11 @@ open class ComposeViewModel( private val imageLimit by lazy { account.map { - when (it?.type) { + when (it.type) { PlatformType.Twitter -> 4 PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> 4 - else -> 4 } }.asStateIn(viewModelScope, 4) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 847a5a7e7..d12f6b76c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -42,7 +42,7 @@ class MastodonComposeSearchHashtagViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val text = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index b4b066485..7a3aeaed0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -37,7 +37,7 @@ class DMConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index b67d5be10..5f2cc19b0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -34,9 +34,9 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -49,32 +49,28 @@ class DMEventViewModel( private val conversationKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val conversation by lazy { - account.flatMapLatest { - it?.let { account -> - repository.dmConversation( - accountKey = account.accountKey, - conversationKey = conversationKey - ) - } ?: emptyFlow() + account.flatMapLatest { account -> + repository.dmConversation( + accountKey = account.accountKey, + conversationKey = conversationKey + ) } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - repository.dmEventListSource( - accountKey = account.accountKey, - conversationKey = conversationKey, - service = account.service as DirectMessageService, - lookupService = account.service as LookupService - ) - } ?: emptyFlow() + account.flatMapLatest { account -> + repository.dmEventListSource( + accountKey = account.accountKey, + conversationKey = conversationKey, + service = account.service as DirectMessageService, + lookupService = account.service as LookupService + ) }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 1a19622bc..7c1a9513d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -48,7 +48,7 @@ class DMNewConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val input = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt index 6c8a3e073..27a1b83b5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -31,6 +31,7 @@ import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -42,7 +43,7 @@ class ListsAddMemberViewModel( private val listId: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 1ba158d9f..330f5ed5c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -42,7 +43,7 @@ class ListsSearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val text = MutableStateFlow("") @@ -51,21 +52,19 @@ class ListsSearchUserViewModel( val source = text.debounce(666L).flatMapLatest { it.takeIf { it.isNotEmpty() }?.let { account.flatMapLatest { account -> - account?.let { _ -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - it, - account.service as SearchService, - following = following - ) - }.flow - } ?: emptyFlow() + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + it, + account.service as SearchService, + following = following + ) + }.flow } } ?: emptyFlow() }.cachedIn(viewModelScope) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index a2d126d80..b84d28f29 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -38,19 +39,17 @@ class ListsTimelineViewModel( listKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - it?.let { - repository.listTimeline( - listKey = listKey, - accountKey = it.accountKey, - service = it.service as TimelineService - ) - } ?: emptyFlow() + repository.listTimeline( + listKey = listKey, + accountKey = it.accountKey, + service = it.service as TimelineService + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index d16afc6a1..c759c485b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -30,9 +30,9 @@ import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.viewmodel.user.UserListViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.viewModelScope @@ -43,32 +43,28 @@ class ListsUserViewModel( private val viewMembers: Boolean = true, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) private val members by lazy { - account.flatMapLatest { - it?.let { account -> - listsUsersRepository.fetchMembers( - accountKey = account.accountKey, - service = account.service as ListsService, - listId = listId - ) - } ?: emptyFlow() + account.flatMapLatest { account -> + listsUsersRepository.fetchMembers( + accountKey = account.accountKey, + service = account.service as ListsService, + listId = listId + ) }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) private val subscribers by lazy { - account.flatMapLatest { - it?.let { account -> - listsUsersRepository.fetchSubscribers( - accountKey = account.accountKey, - service = account.service as ListsService, - listId = listId - ) - } ?: emptyFlow() + account.flatMapLatest { account -> + listsUsersRepository.fetchSubscribers( + accountKey = account.accountKey, + service = account.service as ListsService, + listId = listId + ) }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index b2eeaba3c..f591754b6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -33,7 +33,6 @@ import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map @@ -47,7 +46,7 @@ class ListsViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) @@ -114,7 +113,7 @@ class ListsCreateViewModel( private val onResult: (success: Boolean, list: UiList?) -> Unit ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } fun createList( @@ -143,7 +142,7 @@ class ListsModifyViewModel( private val listKey: MicroBlogKey, ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val editName = MutableStateFlow("") @@ -152,13 +151,11 @@ class ListsModifyViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - listsRepository.findListWithListKey( - accountKey = account.accountKey, - listKey = listKey - ) - } ?: emptyFlow() + account.flatMapLatest { account -> + listsRepository.findListWithListKey( + accountKey = account.accountKey, + listKey = listKey + ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 2f60d096f..6f7b63d10 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -26,8 +26,8 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -37,19 +37,17 @@ class MastodonHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - it?.let { - repository.mastodonHashtagTimeline( - keyword = keyword, - accountKey = it.accountKey, - service = it.service as MastodonService - ) - } ?: emptyFlow() + repository.mastodonHashtagTimeline( + keyword = keyword, + accountKey = it.accountKey, + service = it.service as MastodonService + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index f86888eb0..2057fe977 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -29,8 +29,8 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -39,25 +39,23 @@ class MastodonSearchHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - it?.let { - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - MastodonSearchHashtagPagingSource( - keyword, - it.service as MastodonService - ) - }.flow - } ?: emptyFlow() + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + MastodonSearchHashtagPagingSource( + keyword, + it.service as MastodonService + ) + }.flow }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index d9b482027..f76adc3c2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -38,24 +38,20 @@ class SearchInputViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - it?.let { - repository.searchHistory(it.accountKey) - } ?: flowOf(emptyList()) + repository.searchHistory(it.accountKey) } } @OptIn(ExperimentalCoroutinesApi::class) val savedSource by lazy { account.flatMapLatest { - it?.let { - repository.savedSearch(it.accountKey) - } ?: flowOf(emptyList()) + repository.savedSearch(it.accountKey) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 047a239cc..304aab5b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -35,7 +36,7 @@ class SearchSaveViewModel( private val content: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 39929427f..13fe7530e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -29,9 +29,9 @@ import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -41,20 +41,18 @@ class SearchTweetsViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - SearchStatusMediator( - keyword, - database, - account.accountKey, - account.service as SearchService - ).pager().flow.map { it.map { it.status } }.cachedIn(viewModelScope) - } ?: emptyFlow() + account.flatMapLatest { account -> + SearchStatusMediator( + keyword, + database, + account.accountKey, + account.service as SearchService + ).pager().flow.map { it.map { it.status } }.cachedIn(viewModelScope) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index cc146c0e7..b085b7b48 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -29,8 +29,8 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -40,27 +40,25 @@ class SearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.flatMapLatest { - it?.let { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - keyword, - account.service as SearchService, - following = following - ) - }.flow - } ?: emptyFlow() + account.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + keyword, + account.service as SearchService, + following = following + ) + }.flow }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 13db48818..70db85f95 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -35,14 +36,12 @@ class AccountNotificationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val preferences by lazy { account.map { - it?.let { - accountRepository.getAccountPreferences(it.accountKey) - } + accountRepository.getAccountPreferences(it.accountKey) }.asStateIn(viewModelScope, null) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index b210710b7..75445baf0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -25,6 +25,7 @@ import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -36,7 +37,7 @@ class LayoutViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } fun updateHomeMenu(oldIndex: Int, newIndex: Int, menus: List) = viewModelScope.launch { @@ -79,7 +80,7 @@ class LayoutViewModel( val user by lazy { account.map { - it?.toUi() + it.toUi() } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 6d12d5241..0e72112d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -38,7 +38,7 @@ class HomeTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 7e0ab938d..0295e345c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -30,6 +30,7 @@ import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class MentionsTimelineViewModel( @@ -39,33 +40,29 @@ class MentionsTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } override val pagingMediator by lazy { account.map { - it?.let { - MentionTimelineMediator( - service = it.service as TimelineService, - accountKey = it.accountKey, - database = database, - addCursorIfNeed = { data, accountKey -> - notificationRepository.addCursorIfNeeded( - accountKey, - NotificationCursorType.Mentions, - data.status.statusId, - data.status.timestamp, - ) - } - ) - } + MentionTimelineMediator( + service = it.service as TimelineService, + accountKey = it.accountKey, + database = database, + addCursorIfNeed = { data, accountKey -> + notificationRepository.addCursorIfNeeded( + accountKey, + NotificationCursorType.Mentions, + data.status.statusId, + data.status.timestamp, + ) + } + ) }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { account.map { - it?.let { - "${it.accountKey}_mentions" - } + "${it.accountKey}_mentions" }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 7dca6c3dd..9df3bf0ad 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -30,6 +30,7 @@ import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediato import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class NotificationTimelineViewModel( @@ -39,33 +40,29 @@ class NotificationTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } override val pagingMediator by lazy { account.map { - it?.let { - NotificationTimelineMediator( - service = it.service as NotificationService, - accountKey = it.accountKey, - database = database, - addCursorIfNeed = { data, accountKey -> - notificationRepository.addCursorIfNeeded( - accountKey, - NotificationCursorType.General, - data.status.statusId, - data.status.timestamp - ) - } - ) - } + NotificationTimelineMediator( + service = it.service as NotificationService, + accountKey = it.accountKey, + database = database, + addCursorIfNeed = { data, accountKey -> + notificationRepository.addCursorIfNeeded( + accountKey, + NotificationCursorType.General, + data.status.statusId, + data.status.timestamp + ) + } + ) }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { account.map { - it?.let { - "${it.accountKey}_notification" - } + "${it.accountKey}_notification" }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 9aa551784..766c05074 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.paging.mediator.timeline.mastodon.FederatedTimelineM import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class FederatedTimelineViewModel( @@ -37,26 +38,22 @@ class FederatedTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } override val pagingMediator by lazy { account.map { - it?.let { - FederatedTimelineMediator( - it.service as MastodonService, - it.accountKey, - database, - ) - } + FederatedTimelineMediator( + it.service as MastodonService, + it.accountKey, + database, + ) }.asStateIn(viewModelScope, null) } override val savedStateKey by lazy { account.map { - it?.let { - "${it.accountKey}_federated" - } + "${it.accountKey}_federated" }.asStateIn(viewModelScope, null) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 8c58dd556..016efb101 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -39,7 +39,7 @@ class LocalTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index a1a23ea2d..bb43a4e49 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -26,8 +26,8 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -36,20 +36,16 @@ class TrendViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - if (it != null) { - repository.trendsSource( - accountKey = it.accountKey, - service = it.service as TrendService - ) - } else { - emptyFlow() - } + repository.trendsSource( + accountKey = it.accountKey, + service = it.service as TrendService + ) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 0a9fe7317..517f96d7f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -37,15 +38,13 @@ class TwitterSearchMediaViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { account.flatMapLatest { - it?.let { - repository.media(keyword, it.accountKey, it.service as SearchService) - } ?: emptyFlow() + repository.media(keyword, it.accountKey, it.service as SearchService) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 9e68292d8..290ec5108 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.notifyError import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -39,26 +40,22 @@ class TwitterUserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val error = MutableStateFlow(null) val user by lazy { account.map { - if (it != null) { - try { - repository.lookupUserByName( - screenName, - accountKey = it.accountKey, - lookupService = it.service as LookupService, - ) - } catch (e: Throwable) { - inAppNotification.notifyError(e) - error.value = e - null - } - } else { + try { + repository.lookupUserByName( + screenName, + accountKey = it.accountKey, + lookupService = it.service as LookupService, + ) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + error.value = e null } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index 0e5badc5e..ac935158e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -27,8 +27,8 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( @@ -37,15 +37,13 @@ class FollowersViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) override val source by lazy { - account.flatMapLatest { - it?.let { account -> - repository.followers(userKey, account.service as RelationshipService) - } ?: emptyFlow() + account.flatMapLatest { account -> + repository.followers(userKey, account.service as RelationshipService) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 54005cd84..326f4f1a1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( @@ -37,15 +38,13 @@ class FollowingViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) override val source by lazy { - account.flatMapLatest { - it?.let { account -> - repository.following(userKey, account.service as RelationshipService) - } ?: emptyFlow() + account.flatMapLatest { account -> + repository.following(userKey, account.service as RelationshipService) }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 7a3512aa8..54aa89692 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -38,7 +38,7 @@ class UserFavouriteTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 9cd2fc895..8f7e20e60 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -42,7 +42,7 @@ class UserTimelineViewModel( private val _excludeReplies = MutableStateFlow(false) val excludeReplies = _excludeReplies.asStateIn(viewModelScope, false) private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } fun setExcludeReplies(value: Boolean) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 18f454b90..ed1471663 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -47,7 +47,7 @@ class UserViewModel( ) : ViewModel() { private val refreshFlow = MutableStateFlow(UUID.randomUUID()) private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } val refreshing = MutableStateFlow(false) @@ -68,9 +68,7 @@ class UserViewModel( @OptIn(ExperimentalCoroutinesApi::class) val isMe by lazy { account.transformLatest { - it?.let { - emit(it.accountKey == userKey) - } + emit(it.accountKey == userKey) }.asStateIn(viewModelScope, false) } From e4d79933805f405c5f7c6daab8c3acf6d35271a4 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Sep 2021 19:17:17 +0800 Subject: [PATCH 190/615] clean up --- .../twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt | 1 - .../viewmodel/twitter/search/TwitterSearchMediaViewModel.kt | 1 - .../com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt | 1 - 3 files changed, 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index b84d28f29..ab7472575 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -27,7 +27,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 517f96d7f..a5cf2c54e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -26,7 +26,6 @@ import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 326f4f1a1..a6d5c1b8b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -27,7 +27,6 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope From 5e339ac003cef06b459f060836632b37c4cbf736 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Sep 2021 19:17:42 +0800 Subject: [PATCH 191/615] [WIP] add testing --- .../twiderex/mock/service/MockListsService.kt | 8 ++ .../viewmodel/AccountViewModelTestBase.kt | 57 +++++++++++ .../twiderex/viewmodel/MediaViewModelTest.kt | 95 +++++++++++++++++++ .../viewmodel/PureMediaViewModelTest.kt | 67 +++++++++++++ .../twiderex/viewmodel/StatusViewModelTest.kt | 79 +++++++++++++++ .../lists/ListsCreateViewModelTest.kt | 24 ++--- .../lists/ListsModifyViewModelTest.kt | 20 +--- .../viewmodel/lists/ListsViewModelTest.kt | 25 +---- 8 files changed, 322 insertions(+), 53 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index 062b90086..bb8ec405a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -56,6 +56,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() repliesPolicy: String? ): IListModel { checkError() + if (name == "error") throw TwitterApiException(error = "throw exception intentional") return mockIListModel( name = name, description = description, @@ -71,6 +72,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() repliesPolicy: String? ): IListModel { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") return TwitterList( id = listId.toLong(), idStr = listId, @@ -88,6 +90,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() override suspend fun listMembers(listId: String, count: Int, cursor: String?): List { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") val list = mutableListOf() for (i in 0 until count) { list.add( @@ -99,17 +102,20 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() override suspend fun addMember(listId: String, userId: String, screenName: String) { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") // do nothing } override suspend fun removeMember(listId: String, userId: String, screenName: String) { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") // do nothing } private val subscribers = mutableListOf() override suspend fun listSubscribers(listId: String, count: Int, cursor: String?): List { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") val list = mutableListOf() for (i in 0 until count) { list.add( @@ -127,6 +133,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() override suspend fun unsubscribeList(listId: String): IListModel { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") return TwitterList( id = listId.toLong(), idStr = listId, @@ -137,6 +144,7 @@ internal class MockListsService : ListsService, MicroBlogService, ErrorService() override suspend fun subscribeList(listId: String): IListModel { checkError() + if (listId == "error") throw TwitterApiException(error = "throw exception intentional") return TwitterList( id = listId.toLong(), idStr = listId, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt new file mode 100644 index 000000000..1791e638d --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt @@ -0,0 +1,57 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.model.AccountDetails +import com.twidere.twiderex.model.AmUser +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.repository.AccountRepository +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.flow.flowOf + +internal abstract class AccountViewModelTestBase : ViewModelTestBase() { + + @MockK + protected lateinit var mockUser: AmUser + @MockK + protected lateinit var mockAccount: AccountDetails + @MockK + protected lateinit var mockAccountRepository: AccountRepository + + abstract val mockService: MicroBlogService + + override fun setUp() { + super.setUp() + with(mockUser) { + every { userId }.returns("123") + } + with(mockAccount) { + every { service }.returns(mockService) + every { accountKey }.returns(MicroBlogKey.twitter("123")) + every { type }.returns(PlatformType.Twitter) + every { user }.returns(mockUser) + } + every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt new file mode 100644 index 000000000..2426b0d84 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt @@ -0,0 +1,95 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.action.MediaAction +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.StatusRepository +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +internal class MediaViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService = mockk() + private lateinit var viewModel: MediaViewModel + + @MockK + private lateinit var repository: StatusRepository + + @MockK + private lateinit var mediaAction: MediaAction + + override fun setUp() { + super.setUp() + viewModel = MediaViewModel( + repository, + mockAccountRepository, + mediaAction, + MicroBlogKey.twitter("123") + ) + coEvery { repository.loadStatus(any(), any()) }.returns( + flowOf( + mockk { + every { statusKey }.returns(MicroBlogKey.twitter("123")) + } + ) + ) + } + + @Test + fun load_status(): Unit = runBlocking(Dispatchers.Main) { + viewModel.status.firstOrNull().let { + assertNotNull(it) + assertEquals(MicroBlogKey.twitter("123"), it.statusKey) + } + } + + @Test + fun saveFile_success(): Unit = runBlocking(Dispatchers.Main) { + viewModel.saveFile( + mockk { + every { mediaUrl }.returns("123") + }, + "target", + ) + verify(exactly = 1) { mediaAction.download("123", "target", MicroBlogKey.twitter("123")) } + } + + @Test + fun shareMedia_success(): Unit = runBlocking(Dispatchers.Main) { + viewModel.shareMedia( + mockk { + every { mediaUrl }.returns("123") + } + ) + verify(exactly = 1) { mediaAction.share("123", MicroBlogKey.twitter("123")) } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt new file mode 100644 index 000000000..1500ca7c0 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt @@ -0,0 +1,67 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.MediaRepository +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +internal class PureMediaViewModelTest : ViewModelTestBase() { + + @MockK + lateinit var repository: MediaRepository + + lateinit var viewModel: PureMediaViewModel + + override fun setUp() { + super.setUp() + viewModel = PureMediaViewModel(repository, MicroBlogKey.twitter("123")) + coEvery { repository.findMediaByBelongToKey(any()) }.returns( + (0..3).map { + mockk { + every { belongToKey }.returns(MicroBlogKey.twitter("123")) + every { mediaUrl }.returns(it.toString()) + } + } + ) + } + + @Test + fun loadSource_success(): Unit = runBlocking(Dispatchers.Main) { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assertEquals(4, it.size) + assertTrue { + it.all { it.belongToKey == MicroBlogKey.twitter("123") } + } + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt new file mode 100644 index 000000000..0fd3ff17f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt @@ -0,0 +1,79 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.StatusRepository +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +internal class StatusViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService = mockk() + + @MockK + private lateinit var repository: StatusRepository + private lateinit var viewModel: StatusViewModel + override fun setUp() { + super.setUp() + every { repository.conversation(any(), any(), any(), any()) }.returns( + flowOf( + PagingData.from( + (0..4).map { + mockk { + every { statusKey }.returns(MicroBlogKey.twitter(it.toString())) + every { statusId }.returns(it.toString()) + } + } + ) + ) + ) + viewModel = StatusViewModel(repository, mockAccountRepository, MicroBlogKey.twitter("2")) + } + + @Test + fun source_loadConversation(): Unit = runBlocking(Dispatchers.Main) { + viewModel.source.first().let { + val data = it.collectDataForTest() + assertEquals(5, data.size) + assertTrue { + data.any { + it.statusId == "2" + } + } + assertTrue { + data.indexOfFirst { + it.statusId == "2" + } == 2 + } + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt index 3c4d07209..90567f0f8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt @@ -20,46 +20,36 @@ */ package com.twidere.twiderex.viewmodel.lists +import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.mock.Observer import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.service.MockListsService -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.notification.NotificationEvent -import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertNotNull import kotlin.test.assertNull -internal class ListsCreateViewModelTest : ViewModelTestBase() { +internal class ListsCreateViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockListsService() - private lateinit var mockRepository: ListsRepository + private val mockRepository: ListsRepository = ListsRepository(MockCacheDatabase()) @MockK private lateinit var mockAppNotification: InAppNotification - @MockK - private lateinit var mockAccountRepository: AccountRepository - - private val mockAccount: AccountDetails = mockk { - every { service }.returns(MockListsService()) - every { accountKey }.returns(MicroBlogKey.twitter("123")) - } - @MockK private lateinit var mockSuccessObserver: Observer @@ -74,8 +64,6 @@ internal class ListsCreateViewModelTest : ViewModelTestBase() { override fun setUp() { super.setUp() - every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) - mockRepository = ListsRepository(MockCacheDatabase()) createViewModel = ListsCreateViewModel( mockAppNotification, mockRepository, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt index 73e461da0..25ef55ca1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt @@ -20,24 +20,21 @@ */ package com.twidere.twiderex.viewmodel.lists +import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.mock.Observer import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.service.MockListsService -import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.notification.NotificationEvent -import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert @@ -45,21 +42,15 @@ import org.junit.Test import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -internal class ListsModifyViewModelTest : ViewModelTestBase() { +internal class ListsModifyViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockListsService() private var mockRepository: ListsRepository = ListsRepository(MockCacheDatabase()) @MockK private lateinit var mockAppNotification: InAppNotification - @MockK - private lateinit var mockAccountRepository: AccountRepository - - private val mockAccount: AccountDetails = mockk { - every { service }.returns(MockListsService()) - every { accountKey }.returns(MicroBlogKey.twitter("123")) - } - @MockK private lateinit var mockSuccessObserver: Observer @@ -187,7 +178,6 @@ internal class ListsModifyViewModelTest : ViewModelTestBase() { override fun setUp() { super.setUp() - every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) modifyViewModel = ListsModifyViewModel( mockRepository, mockAppNotification, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt index 82a983ddc..0eb811647 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt @@ -21,15 +21,12 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.mock.paging.collectDataForTest import com.twidere.twiderex.mock.service.MockListsService -import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.model.AmUser -import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList -import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsRepository -import com.twidere.twiderex.viewmodel.ViewModelTestBase +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase import io.mockk.every import io.mockk.impl.annotations.MockK import kotlinx.coroutines.Dispatchers @@ -40,20 +37,13 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals -internal class ListsViewModelTest : ViewModelTestBase() { +internal class ListsViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockListsService() @MockK private lateinit var mockRepository: ListsRepository - @MockK - private lateinit var mockAccountRepository: AccountRepository - - @MockK - private lateinit var mockAccount: AccountDetails - - @MockK - private lateinit var mockUser: AmUser - @MockK private lateinit var ownerList: UiList @@ -74,16 +64,11 @@ internal class ListsViewModelTest : ViewModelTestBase() { ) ) ) - every { mockAccountRepository.activeAccount }.returns(flowOf(mockAccount)) every { ownerList.isOwner(any()) }.returns(true) every { subscribeList.isOwner(any()) }.returns(false) every { ownerList.title }.returns("owner") every { subscribeList.title }.returns("subscribe") every { subscribeList.isFollowed }.returns(true) - every { mockAccount.user }.returns(mockUser) - every { mockAccount.accountKey }.returns(MicroBlogKey.twitter("123")) - every { mockAccount.service }.returns(MockListsService()) - every { mockUser.userId }.returns("123") viewModel = ListsViewModel(mockRepository, mockAccountRepository) } From ed99a32a3dbd350d1a40ce6b692c0daf5be77f37 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 21:41:09 +0800 Subject: [PATCH 192/615] refactor function --- .../component/video/AndroidVideoPlayer.kt | 12 ++- .../twiderex/component/video/VideoPlayer.kt | 78 +++++++++---------- .../component/video/DesktopVideoPlayer.kt | 16 ++-- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt index a5c52094a..6913ecbb7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt @@ -21,12 +21,18 @@ package com.twidere.twiderex.component.video import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier @Composable actual fun VideoPlayerImpl( + modifier: Modifier, url: String, - width: Int, - height: Int, - isPlaying: Boolean + volume: Float, + customControl: Any?, + showControls: Boolean, + zOrderMediaOverlay: Boolean, + keepScreenOn: Boolean, + isListItem: Boolean, + thumb: @Composable (() -> Unit)? ) { } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt index 894f35681..f294ad802 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt @@ -22,58 +22,48 @@ package com.twidere.twiderex.component.video import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp expect fun VideoPlayerImpl( + modifier: Modifier = Modifier, url: String, - width: Int, - height: Int, - isPlaying: Boolean + volume: Float = 1f, + customControl: Any? = null, + showControls: Boolean = customControl == null, + zOrderMediaOverlay: Boolean = false, + keepScreenOn: Boolean = false, + isListItem: Boolean = true, + thumb: @Composable (() -> Unit)? = null, ) @Composable -fun VideoPlayer(url: String, width: Int, height: Int) { - Column { - var isPlaying by remember { - mutableStateOf(true) - } - VideoPlayerImpl( - url, - width, - height, - isPlaying - ) - Row { - Button( - onClick = { - isPlaying = true - } - ) { - Text("play") - } - - Button( - onClick = { - isPlaying = false - } - ) { - Text("pause") - } - } - } +fun VideoPlayer( + modifier: Modifier = Modifier, + url: String, + volume: Float = 1f, + customControl: Any? = null, + showControls: Boolean = customControl == null, + zOrderMediaOverlay: Boolean = false, + keepScreenOn: Boolean = false, + isListItem: Boolean = true, + thumb: @Composable (() -> Unit)? = null, +) { + VideoPlayerImpl( + modifier = modifier, + url = url, + volume = volume, + customControl = customControl, + showControls = showControls, + zOrderMediaOverlay = zOrderMediaOverlay, + keepScreenOn = keepScreenOn, + isListItem = isListItem, + thumb = thumb + ) } @Composable @@ -81,11 +71,13 @@ fun VideoPreview() { LazyColumn { for (i in 0..5) { item { - Box(modifier = Modifier.background(Color.DarkGray).padding(100.dp)) { + Box( + modifier = Modifier + .background(Color.DarkGray) + .padding(100.dp) + ) { VideoPlayer( url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", - width = 640, - height = 480 ) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt index 3f2d22ee1..73e4ca81a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt @@ -51,10 +51,15 @@ import java.util.Locale @Composable actual fun VideoPlayerImpl( + modifier: Modifier, url: String, - width: Int, - height: Int, - isPlaying: Boolean + volume: Float, + customControl: Any?, + showControls: Boolean, + zOrderMediaOverlay: Boolean, + keepScreenOn: Boolean, + isListItem: Boolean, + thumb: @Composable (() -> Unit)? ) { NativeDiscovery().discover() @@ -158,9 +163,8 @@ actual fun VideoPlayerImpl( mediaPlayerComponent } ) { - val player = it.mediaPlayer() - val controls = player.controls() - if (isPlaying && isMostCenter && active) { + val controls = it.mediaPlayer().controls() + if (isMostCenter && active) { controls.play() } else { controls.setPause(true) From 3f309c61d1aaa6406702385e7acb69f3ea7f071a Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 21:45:25 +0800 Subject: [PATCH 193/615] repackage --- .../component/{video => foundation}/AndroidVideoPlayer.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/App.kt | 2 +- .../twiderex/component/{video => foundation}/VideoPlayer.kt | 2 +- .../twidere/twiderex/{component => utils}/video/VideoPool.kt | 2 +- .../component/{video => foundation}/DesktopVideoPlayer.kt | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) rename common/src/androidMain/kotlin/com/twidere/twiderex/component/{video => foundation}/AndroidVideoPlayer.kt (95%) rename common/src/commonMain/kotlin/com/twidere/twiderex/component/{video => foundation}/VideoPlayer.kt (98%) rename common/src/commonMain/kotlin/com/twidere/twiderex/{component => utils}/video/VideoPool.kt (98%) rename common/src/desktopMain/kotlin/com/twidere/twiderex/component/{video => foundation}/DesktopVideoPlayer.kt (98%) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt similarity index 95% rename from common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt rename to common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 6913ecbb7..0c42646b4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/video/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.component.video +package com.twidere.twiderex.component.foundation import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 1e0875b09..9a5f1711e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -26,7 +26,7 @@ import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import com.twidere.twiderex.component.video.VideoPreview +import com.twidere.twiderex.component.foundation.VideoPreview import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.ext.get diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt similarity index 98% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index f294ad802..860362e94 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.component.video +package com.twidere.twiderex.component.foundation import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt similarity index 98% rename from common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt index b094452e6..b7f2018e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/video/VideoPool.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.component.video +package com.twidere.twiderex.utils.video import androidx.compose.ui.geometry.Rect import java.util.concurrent.ConcurrentHashMap diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt similarity index 98% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt rename to common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 73e4ca81a..aae202e1b 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/video/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.component.video +package com.twidere.twiderex.component.foundation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height @@ -36,6 +36,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.unit.dp +import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch From 372ba3677a1e728fec55c50efe9137500e5ad6b2 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 17 Sep 2021 23:39:01 +0800 Subject: [PATCH 194/615] [wip] --- .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 3 - common/build.gradle.kts | 2 + .../foundation/AndroidVideoPlayer.kt | 227 ++++++++++++++++++ .../foundation/RemainingTimeVideo.kt | 99 ++++++++ .../utils/video/CacheDataSourceFactory.kt | 66 +++++ .../twiderex/utils/video/VideoCache.kt | 38 +++ .../component/foundation/VideoPlayer.kt | 3 + .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 27 +++ .../foundation/DesktopVideoPlayer.kt | 20 +- 9 files changed, 463 insertions(+), 22 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt index 565a1dc44..632cf7e38 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf import androidx.core.view.WindowInsetsControllerCompat import com.twidere.twiderex.model.AccountDetails -import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import moe.tlaster.precompose.navigation.NavController @@ -40,5 +39,3 @@ val LocalActiveAccountViewModel = compositionLocalOf { error("No ActiveAccountViewModel") } val LocalApplication = staticCompositionLocalOf { error("No Application") } val LocalActivity = staticCompositionLocalOf { error("NoActivity") } -val LocalVideoPlayback = compositionLocalOf { DisplayPreferences.AutoPlayback.Auto } -val LocalIsActiveNetworkMetered = compositionLocalOf { false } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 1ad1b59bf..97dd91132 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -69,6 +69,8 @@ kotlin { implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") implementation("io.coil-kt:coil-svg:${Versions.coil}") + implementation("com.google.android.exoplayer:exoplayer:${Versions.exoplayer}") + implementation("com.google.android.exoplayer:extension-okhttp:${Versions.exoplayer}") } } val androidAndroidTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 0c42646b4..d6f8124c3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -20,8 +20,58 @@ */ package com.twidere.twiderex.component.foundation +import android.view.SurfaceView +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent +import com.google.android.exoplayer2.MediaItem +import com.google.android.exoplayer2.Player +import com.google.android.exoplayer2.SimpleExoPlayer +import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource +import com.google.android.exoplayer2.source.DefaultMediaSourceFactory +import com.google.android.exoplayer2.source.ProgressiveMediaSource +import com.google.android.exoplayer2.ui.PlayerControlView +import com.google.android.exoplayer2.ui.StyledPlayerView +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.twidere.twiderex.MR.strings.accessibility_common_video_play +import com.twidere.twiderex.compose.LocalResLoader +import com.twidere.twiderex.http.TwidereServiceFactory +import com.twidere.twiderex.preferences.LocalHttpConfig +import com.twidere.twiderex.preferences.model.DisplayPreferences +import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered +import com.twidere.twiderex.ui.LocalVideoPlayback +import com.twidere.twiderex.utils.video.CacheDataSourceFactory +import com.twidere.twiderex.utils.video.VideoPool +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable actual fun VideoPlayerImpl( @@ -35,4 +85,181 @@ actual fun VideoPlayerImpl( isListItem: Boolean, thumb: @Composable (() -> Unit)? ) { + var playing by remember { mutableStateOf(false) } + val playBackMode = LocalVideoPlayback.current + val isActiveNetworkMetered = LocalIsActiveNetworkMetered.current + var shouldShowThumb by remember { mutableStateOf(false) } + val playInitial = when (playBackMode) { + DisplayPreferences.AutoPlayback.Auto -> !isActiveNetworkMetered + DisplayPreferences.AutoPlayback.Always -> true + DisplayPreferences.AutoPlayback.Off -> false + } + var autoPlay by remember(url) { mutableStateOf(playInitial) } + val context = LocalContext.current + val lifecycle = LocalLifecycleOwner.current.lifecycle + val httpConfig = LocalHttpConfig.current + val resLoder = LocalResLoader.current + Box { + if (playInitial) { + val player = remember(url) { + RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context) + .apply { + if (httpConfig.proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } + } + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + shouldShowThumb = state != Player.STATE_READY + } + + override fun onIsPlayingChanged(isPlaying: Boolean) { + playing = isPlaying + } + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) + } + prepare() + seekTo(VideoPool.get(url)) + } + } + player.volume = volume + + fun updateState() { + autoPlay = player.playWhenReady + VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition)) + } + + LaunchedEffect(customControl) { + (customControl as? PlayerControlView)?.player = player + } + var isResume by remember { + mutableStateOf(true) + } + val videoKey by remember { + mutableStateOf(url + System.currentTimeMillis()) + } + DisposableEffect(Unit) { + val observer = object : LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun onResume() { + isResume = true + player.playWhenReady = autoPlay + } + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + fun onPause() { + isResume = false + updateState() + player.playWhenReady = false + } + } + lifecycle.addObserver(observer) + onDispose { + updateState() + player.release() + VideoPool.removeRect(videoKey) + lifecycle.removeObserver(observer) + } + } + + var middleLine = 0.0f + val composableScope = rememberCoroutineScope() + + var isMostCenter by remember(url) { + mutableStateOf(false) + } + var debounceJob: Job? = null + AndroidView( + modifier = modifier.onGloballyPositioned { coordinates -> + if (middleLine == 0.0f) { + var rootCoordinates = coordinates + while (rootCoordinates.parentCoordinates != null) { + rootCoordinates = rootCoordinates.parentCoordinates!! + } + rootCoordinates.boundsInWindow().run { + middleLine = (top + bottom) / 2 + } + } + coordinates.boundsInWindow().run { + VideoPool.setRect(videoKey, this) + if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { + debounceJob?.cancel() + debounceJob = composableScope.launch { + delay(VideoPool.DEBOUNCE_DELAY) + if (VideoPool.containsMiddleLine(videoKey, middleLine)) { + isMostCenter = true + } + } + } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { + isMostCenter = false + } + } + }, + factory = { context -> + StyledPlayerView(context).also { playerView -> + (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) + playerView.useController = showControls + playerView.keepScreenOn = keepScreenOn + } + } + ) { + it.player = player + if (isResume && isMostCenter) { + if (isListItem) { + player.playWhenReady = autoPlay + } + it.onResume() + } else { + if (isListItem) { + player.playWhenReady = false + } + it.onPause() + } + } + } + if ((shouldShowThumb || !playing) && thumb != null) { + thumb() + Box( + modifier = Modifier + .fillMaxSize() + ) { + Icon( + imageVector = Icons.Default.PlayArrow, + tint = Color.White.copy(alpha = LocalContentAlpha.current), + modifier = Modifier + .align(Alignment.Center) + .size(UserAvatarDefaults.AvatarSize) + .background(MaterialTheme.colors.primary, CircleShape), + contentDescription = resLoder.getString(accessibility_common_video_play) + ) + } + } + } +} +object UserAvatarDefaults { + val AvatarSize = 44.dp } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt new file mode 100644 index 000000000..c462a0525 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt @@ -0,0 +1,99 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation + +import android.content.Context +import android.util.AttributeSet +import com.google.android.exoplayer2.SimpleExoPlayer +import com.google.android.exoplayer2.ui.DefaultTimeBar +import com.google.android.exoplayer2.ui.TimeBar + +class RemainingTimeExoPlayer(builder: Builder) : SimpleExoPlayer(builder) { + override fun getContentPosition(): Long { + return super.getContentPosition() - contentDuration + } +} + +class RemainingTimeBar : DefaultTimeBar { + constructor(context: Context) : this(context, null) + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, attrs) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + timebarAttrs: AttributeSet? + ) : this(context, attrs, defStyleAttr, timebarAttrs, 0) + + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int, + timebarAttrs: AttributeSet?, + defStyleRes: Int + ) : super(context, attrs, defStyleAttr, timebarAttrs, defStyleRes) + + private var duration: Long = 0 + private val listeners = mutableListOf() + override fun setDuration(duration: Long) { + this.duration = duration + super.setDuration(duration) + } + override fun setPosition(position: Long) { + super.setPosition(if (position < 0) duration + position else position) + } + + override fun addListener(listener: TimeBar.OnScrubListener) { + val wrapper = RemainingScrubListenerWrapper(listener) { + if (it < 0) it else it - duration + }.also { + listeners.add(it) + } + super.addListener(wrapper) + } + + override fun removeListener(listener: TimeBar.OnScrubListener) { + listeners.removeAll { it.listener == listener } + super.removeListener(listener) + } + + private class RemainingScrubListenerWrapper( + val listener: TimeBar.OnScrubListener, + private val transform: (position: Long) -> Long + ) : TimeBar.OnScrubListener { + + override fun onScrubStart(timeBar: TimeBar, position: Long) { + listener.onScrubStart(timeBar, transform(position)) + } + + override fun onScrubMove(timeBar: TimeBar, position: Long) { + listener.onScrubMove(timeBar, transform(position)) + } + + override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { + // do not transform position here + listener.onScrubStop(timeBar, position, canceled) + } + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt new file mode 100644 index 000000000..259aebdc2 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt @@ -0,0 +1,66 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils.video + +import android.content.Context +import com.google.android.exoplayer2.upstream.DataSource +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.google.android.exoplayer2.upstream.DefaultHttpDataSource +import com.google.android.exoplayer2.upstream.FileDataSource +import com.google.android.exoplayer2.upstream.cache.CacheDataSink +import com.google.android.exoplayer2.upstream.cache.CacheDataSource +import com.google.android.exoplayer2.upstream.cache.SimpleCache +import com.google.android.exoplayer2.util.Util + +class CacheDataSourceFactory( + private val context: Context, + private val maxFileSize: Long, +) : DataSource.Factory { + private val simpleCache: SimpleCache by lazy { + VideoCache.getInstance(context) + } + + private val defaultDatasourceFactory: DefaultDataSourceFactory + override fun createDataSource(): DataSource { + return CacheDataSource( + simpleCache, defaultDatasourceFactory.createDataSource(), + FileDataSource(), CacheDataSink(simpleCache, maxFileSize), + CacheDataSource.FLAG_BLOCK_ON_CACHE or CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR, null + ) + } + + init { + // todo application name + val userAgent = Util.getUserAgent( + context, + "" + ) + val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build() + defaultDatasourceFactory = DefaultDataSourceFactory( + this.context, + bandwidthMeter, + DefaultHttpDataSource.Factory() + .setUserAgent(userAgent) + .setTransferListener(bandwidthMeter) + ) + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt new file mode 100644 index 000000000..3917e7f87 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils.video + +import android.content.Context +import com.google.android.exoplayer2.database.ExoDatabaseProvider +import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor +import com.google.android.exoplayer2.upstream.cache.SimpleCache +import java.io.File + +object VideoCache { + private var simpleCache: SimpleCache? = null + private const val maxCacheSize: Long = 100 * 1024 * 1024 + fun getInstance(context: Context): SimpleCache { + val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize) + if (simpleCache == null) simpleCache = + SimpleCache(File(context.cacheDir, "media"), evictor, ExoDatabaseProvider(context)) + return simpleCache as SimpleCache + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 860362e94..63b7cf2aa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -22,7 +22,9 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -78,6 +80,7 @@ fun VideoPreview() { ) { VideoPlayer( url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + modifier = Modifier.width(500.dp).height(500.dp) ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt new file mode 100644 index 000000000..2c55bf82d --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.ui + +import androidx.compose.runtime.compositionLocalOf +import com.twidere.twiderex.preferences.model.DisplayPreferences + +val LocalVideoPlayback = compositionLocalOf { DisplayPreferences.AutoPlayback.Auto } +val LocalIsActiveNetworkMetered = compositionLocalOf { false } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index aae202e1b..4aaa6d61f 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -21,8 +21,6 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue @@ -35,7 +33,6 @@ import androidx.compose.ui.awt.SwingPanel import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.unit.dp import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -45,7 +42,6 @@ import moe.tlaster.precompose.lifecycle.LifecycleObserver import moe.tlaster.precompose.ui.LocalLifecycleOwner import uk.co.caprica.vlcj.factory.discovery.NativeDiscovery import uk.co.caprica.vlcj.player.base.MediaPlayer -import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import java.util.Locale @@ -62,7 +58,6 @@ actual fun VideoPlayerImpl( isListItem: Boolean, thumb: @Composable (() -> Unit)? ) { - NativeDiscovery().discover() val lifecycle = LocalLifecycleOwner.current.lifecycle var active by remember(url) { @@ -89,20 +84,7 @@ actual fun VideoPlayerImpl( mutableStateOf(false) } DisposableEffect(url) { - val listener = object : MediaPlayerEventAdapter() { - override fun positionChanged(mediaPlayer: MediaPlayer?, newPosition: Float) { - super.positionChanged(mediaPlayer, newPosition) - } - - override fun playing(mediaPlayer: MediaPlayer?) { - super.playing(mediaPlayer) - } - override fun mediaPlayerReady(mediaPlayer: MediaPlayer?) { - super.mediaPlayerReady(mediaPlayer) - } - } - mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(listener) mediaPlayerComponent.mediaPlayer().media().prepare(url) val lifecycleObserver = object : LifecycleObserver { @@ -132,7 +114,7 @@ actual fun VideoPlayerImpl( } return Box( - modifier = Modifier.width(500.dp).height(500.dp).onGloballyPositioned { coordinates -> + modifier = modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates while (rootCoordinates.parentCoordinates != null) { From b4ec361e4f4bed9c2dad0835766bcad4231ee8cb Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 00:01:42 +0800 Subject: [PATCH 195/615] remove listener --- .../twidere/twiderex/component/foundation/DesktopVideoPlayer.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 4aaa6d61f..a9316e8e1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -103,7 +103,6 @@ actual fun VideoPlayerImpl( lifecycle.addObserver(lifecycleObserver) onDispose { - mediaPlayerComponent.mediaPlayer().events().removeMediaPlayerEventListener(listener) lifecycle.removeObserver(lifecycleObserver) mediaPlayerComponent.mediaPlayer().release() } From 8fd469565c4be5a4457c8415162e90c69857a4c1 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 09:20:40 +0800 Subject: [PATCH 196/615] split some code --- .../foundation/AndroidVideoPlayer.kt | 117 ++++++++++-------- .../component/foundation/VideoPlayer.kt | 9 ++ 2 files changed, 74 insertions(+), 52 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index d6f8124c3..1eec95cc4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -86,66 +86,24 @@ actual fun VideoPlayerImpl( thumb: @Composable (() -> Unit)? ) { var playing by remember { mutableStateOf(false) } - val playBackMode = LocalVideoPlayback.current - val isActiveNetworkMetered = LocalIsActiveNetworkMetered.current var shouldShowThumb by remember { mutableStateOf(false) } - val playInitial = when (playBackMode) { - DisplayPreferences.AutoPlayback.Auto -> !isActiveNetworkMetered - DisplayPreferences.AutoPlayback.Always -> true - DisplayPreferences.AutoPlayback.Off -> false - } + val playInitial = getPlayInitial() var autoPlay by remember(url) { mutableStateOf(playInitial) } - val context = LocalContext.current val lifecycle = LocalLifecycleOwner.current.lifecycle - val httpConfig = LocalHttpConfig.current val resLoder = LocalResLoader.current Box { if (playInitial) { val player = remember(url) { - RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context) - .apply { - if (httpConfig.proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } - } - } - ).apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - shouldShowThumb = state != Player.STATE_READY - } - - override fun onIsPlayingChanged(isPlaying: Boolean) { - playing = isPlaying - } - }) - - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) + getPlater( + url = url, + autoPlay = autoPlay, + setShowThumb = { + shouldShowThumb = it + }, + setPLaying = { + playing = it } - prepare() - seekTo(VideoPool.get(url)) - } + ) } player.volume = volume @@ -260,6 +218,61 @@ actual fun VideoPlayerImpl( } } } + +fun getPlater( + url: String, + autoPlay: Boolean, + setShowThumb: (Boolean) -> Unit, + setPLaying: (Boolean) -> Unit, +): RemainingTimeExoPlayer { + val context = LocalContext.current + val httpConfig = LocalHttpConfig.current + return RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context) + .apply { + if (httpConfig.proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } + } + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + setShowThumb(state != Player.STATE_READY) + } + + override fun onIsPlayingChanged(isPlaying: Boolean) { + setPLaying(isPlaying) + } + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) + } + prepare() + seekTo(VideoPool.get(url)) + } +} + object UserAvatarDefaults { val AvatarSize = 44.dp } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 63b7cf2aa..8c5a91632 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -30,6 +30,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import com.twidere.twiderex.preferences.model.DisplayPreferences +import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered +import com.twidere.twiderex.ui.LocalVideoPlayback expect fun VideoPlayerImpl( modifier: Modifier = Modifier, @@ -68,6 +71,12 @@ fun VideoPlayer( ) } +internal fun getPlayInitial() = when(LocalVideoPlayback.current) { + DisplayPreferences.AutoPlayback.Auto -> !LocalIsActiveNetworkMetered.current + DisplayPreferences.AutoPlayback.Always -> true + DisplayPreferences.AutoPlayback.Off -> false +} + @Composable fun VideoPreview() { LazyColumn { From 9640bef0a19c731fb5cb23481651cd2b2c144682 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 11:20:38 +0800 Subject: [PATCH 197/615] [wip] --- .../foundation/AndroidVideoPlayer.kt | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 1eec95cc4..6d139d5f3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -20,7 +20,9 @@ */ package com.twidere.twiderex.component.foundation +import android.content.Context import android.view.SurfaceView +import android.view.View import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -48,6 +50,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.viewinterop.NoOpUpdate import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent @@ -151,7 +154,10 @@ actual fun VideoPlayerImpl( mutableStateOf(false) } var debounceJob: Job? = null - AndroidView( + PlatformView( + zOrderMediaOverlay = zOrderMediaOverlay, + showControls = showControls, + keepScreenOn = keepScreenOn, modifier = modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates @@ -177,25 +183,18 @@ actual fun VideoPlayerImpl( } } }, - factory = { context -> - StyledPlayerView(context).also { playerView -> - (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) - playerView.useController = showControls - playerView.keepScreenOn = keepScreenOn - } - } ) { it.player = player if (isResume && isMostCenter) { if (isListItem) { player.playWhenReady = autoPlay } - it.onResume() + it.resume() } else { if (isListItem) { player.playWhenReady = false } - it.onPause() + it.pause() } } } @@ -219,6 +218,66 @@ actual fun VideoPlayerImpl( } } +@Composable +fun PlatformView( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + modifier: Modifier = Modifier, + update: (NativePlayer) -> Unit = {} +) { + val nativePlayer = remember { + nativeViewFactory( + zOrderMediaOverlay, + showControls, + keepScreenOn + ) + } + AndroidView( + factory = { + nativePlayer.player as View + }, + modifier = modifier, + update = { + update.invoke(nativePlayer) + } + ) +} + +class NativePlayer { + var player: Any?= null + + fun setP(p: Any) { + + } + + fun resume() { + + } + fun pause() { + + } + + fun update() { + + } +} + +fun nativeViewFactory( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean +): NativePlayer { + val context = LocalContext.current + return NativePlayer().apply { + player = StyledPlayerView(context).also { playerView -> + (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) + playerView.useController = showControls + playerView.keepScreenOn = keepScreenOn + } + } +} + fun getPlater( url: String, autoPlay: Boolean, From 5edb85b575157cb96d88834c625edd88816d4aa8 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 12:08:30 +0800 Subject: [PATCH 198/615] split all platform things --- .../foundation/AndroidVideoPlayer.kt | 141 +++++++++++------- 1 file changed, 85 insertions(+), 56 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 6d139d5f3..82622924e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.component.foundation -import android.content.Context import android.view.SurfaceView import android.view.View import androidx.compose.foundation.background @@ -50,7 +49,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import androidx.compose.ui.viewinterop.NoOpUpdate import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent @@ -67,9 +65,6 @@ import com.twidere.twiderex.MR.strings.accessibility_common_video_play import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig -import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered -import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.utils.video.CacheDataSourceFactory import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.Job @@ -97,7 +92,7 @@ actual fun VideoPlayerImpl( Box { if (playInitial) { val player = remember(url) { - getPlater( + getNativePlater( url = url, autoPlay = autoPlay, setShowThumb = { @@ -108,11 +103,11 @@ actual fun VideoPlayerImpl( } ) } - player.volume = volume + player.setVolume(volume) fun updateState() { autoPlay = player.playWhenReady - VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition)) + VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition())) } LaunchedEffect(customControl) { @@ -224,7 +219,7 @@ fun PlatformView( showControls: Boolean, keepScreenOn: Boolean, modifier: Modifier = Modifier, - update: (NativePlayer) -> Unit = {} + update: (NativePlayerView) -> Unit = {} ) { val nativePlayer = remember { nativeViewFactory( @@ -235,7 +230,7 @@ fun PlatformView( } AndroidView( factory = { - nativePlayer.player as View + nativePlayer.playerView as View }, modifier = modifier, update = { @@ -244,33 +239,65 @@ fun PlatformView( ) } +class NativePlayerView() { + var playerView: Any?= null + var player: NativePlayer?= null + + private fun realPlayerView() = playerView as? StyledPlayerView + + fun resume() = realPlayerView()?.onResume() + + fun pause() = realPlayerView()?.onPause() +} + class NativePlayer { var player: Any?= null + private fun realPlayer() = player as? RemainingTimeExoPlayer + + // var playWhenReady = realPlayer as + + var playWhenReady: Boolean + get() = realPlayer()?.playWhenReady ?: false + set(value) { + realPlayer()?.playWhenReady = value + } + + fun contentPosition(): Long = realPlayer()?.contentPosition?:0L + fun setP(p: Any) { } fun resume() { - + // getRealPlayer()?.res } - fun pause() { + fun pause() { + realPlayer()?.pause() } fun update() { } + + fun setVolume(volume: Float) { + realPlayer()?.volume = volume + } + + fun release() { + realPlayer()?.release() + } } fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean -): NativePlayer { +): NativePlayerView { val context = LocalContext.current - return NativePlayer().apply { - player = StyledPlayerView(context).also { playerView -> + return NativePlayerView().apply { + playerView = StyledPlayerView(context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) playerView.useController = showControls playerView.keepScreenOn = keepScreenOn @@ -278,57 +305,59 @@ fun nativeViewFactory( } } -fun getPlater( +fun getNativePlater( url: String, autoPlay: Boolean, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit, -): RemainingTimeExoPlayer { +): NativePlayer { val context = LocalContext.current val httpConfig = LocalHttpConfig.current - return RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context) - .apply { - if (httpConfig.proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } + return NativePlayer().apply { + player = RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context) + .apply { + if (httpConfig.proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } + } + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + setShowThumb(state != Player.STATE_READY) } - } - ).apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - setShowThumb(state != Player.STATE_READY) - } - override fun onIsPlayingChanged(isPlaying: Boolean) { - setPLaying(isPlaying) - } - }) + override fun onIsPlayingChanged(isPlaying: Boolean) { + setPLaying(isPlaying) + } + }) - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) + } + prepare() + seekTo(VideoPool.get(url)) } - prepare() - seekTo(VideoPool.get(url)) } } From 8c368396280589d15d9d502f47ce02944a8d2d83 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 12:33:01 +0800 Subject: [PATCH 199/615] split out custom control --- .../twiderex/component/foundation/AndroidVideoPlayer.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 82622924e..64a7f1774 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -111,7 +111,7 @@ actual fun VideoPlayerImpl( } LaunchedEffect(customControl) { - (customControl as? PlayerControlView)?.player = player + player.setCustomControl(customControl) } var isResume by remember { mutableStateOf(true) @@ -239,6 +239,7 @@ fun PlatformView( ) } + class NativePlayerView() { var playerView: Any?= null var player: NativePlayer?= null @@ -265,8 +266,8 @@ class NativePlayer { fun contentPosition(): Long = realPlayer()?.contentPosition?:0L - fun setP(p: Any) { - + fun setCustomControl(customControl: Any?) { + (customControl as? PlayerControlView)?.player = realPlayer() } fun resume() { From a41a567032a5a063c72b3cc4962addb1e4f151b2 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 12:44:29 +0800 Subject: [PATCH 200/615] use common lifecycle in AndroidVideoPlayer.kt --- .../foundation/AndroidVideoPlayer.kt | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 64a7f1774..3672e66e2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -46,12 +46,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.SimpleExoPlayer @@ -70,6 +66,9 @@ import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LifecycleObserver +import moe.tlaster.precompose.ui.LocalLifecycleOwner @Composable actual fun VideoPlayerImpl( @@ -121,16 +120,19 @@ actual fun VideoPlayerImpl( } DisposableEffect(Unit) { val observer = object : LifecycleObserver { - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onResume() { - isResume = true - player.playWhenReady = autoPlay - } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun onPause() { - isResume = false - updateState() - player.playWhenReady = false + override fun onStateChanged(state: Lifecycle.State) { + when(state) { + Lifecycle.State.Active -> { + isResume = true + player.playWhenReady = autoPlay + } + Lifecycle.State.InActive -> { + isResume = false + updateState() + player.playWhenReady = false + } + else -> {} + } } } lifecycle.addObserver(observer) From ea7bd1bcace09938c05ea3cd55ecbc9a3a31c9be Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sat, 18 Sep 2021 13:29:18 +0800 Subject: [PATCH 201/615] add search and draft table and add unit test for them --- common/build.gradle.kts | 1 + .../twiderex/sqldelight/table/draft.sq | 30 ++++++ .../twiderex/sqldelight/table/search.sq | 11 ++- .../sqldelight/adapter/ColumnAdapters.kt | 48 +++++++++ .../sqldelight/adapter/DraftAdapterFactory.kt | 32 ++++++ .../adapter/SearchAdapterFactory.kt | 29 ++++++ .../twiderex/db/DraftQueriesImplTest.kt | 98 +++++++++++++++++++ .../twiderex/db/SearchQueriesImplTest.kt | 98 +++++++++++++++++-- .../twiderex/db/base/BaseAppDatabaseTest.kt | 8 +- 9 files changed, 344 insertions(+), 11 deletions(-) create mode 100644 common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7bd2dca39..98b7d2710 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -92,6 +92,7 @@ kotlin { val desktopMain by getting { dependencies { implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") + implementation("com.squareup.sqldelight:coroutines-extensions-jvm:${Versions.sqlDelight}") } } val desktopTest by getting diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq new file mode 100644 index 000000000..eaabd4f07 --- /dev/null +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq @@ -0,0 +1,30 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.enums.ComposeType; +import kotlin.collections.List; + +CREATE TABLE draft ( + id TEXT NOT NULL PRIMARY KEY, + content TEXT NOT NULL, + media TEXT AS List, + createAt INTEGER NOT NULL, + composeType TEXT AS ComposeType, + statusKey TEXT AS MicroBlogKey DEFAULT NULL, + excludedReplyUserIds TEXT AS List DEFAULT NULL +); + +insert: +INSERT OR REPLACE INTO draft(id, content, media, createAt, composeType, statusKey, excludedReplyUserIds) +VALUES ?; + +getAll: +SELECT * FROM draft; + +get: +SELECT * FROM draft WHERE id == :id; + +remove: +DELETE FROM draft WHERE id == :id; + +getDraftCount: +SELECT COUNT(*) FROM draft; + diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq index b7866cbb8..6b95719d9 100644 --- a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq @@ -1,15 +1,18 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import java.lang.Boolean; + CREATE TABLE search ( content TEXT NOT NULL, lastActive INTEGER NOT NULL, - saved INTEGER DEFAULT 0 NOT NULL, - accountKey TEXT NOT NULL + saved INTEGER AS Boolean DEFAULT 0 NOT NULL, + accountKey TEXT AS MicroBlogKey NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS index_search_content_accountKey ON search (content, accountKey); insert: -INSERT INTO search(content, lastActive, saved, accountKey) -VALUES(?, ?, ?, ?); +INSERT OR REPLACE INTO search(content, lastActive, saved, accountKey) +VALUES ?; get: SELECT * FROM search WHERE content == :content AND accountKey == :accountKey; diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt new file mode 100644 index 000000000..9a788070a --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt @@ -0,0 +1,48 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.squareup.sqldelight.ColumnAdapter +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType + +internal class StringListColumnAdapter : ColumnAdapter, String> { + override fun decode(databaseValue: String) = + if (databaseValue.isEmpty()) { + listOf() + } else { + databaseValue.split(",") + } + + override fun encode(value: List) = value.joinToString { "," } +} + +internal class ComposeTypeColumnAdapter : ColumnAdapter { + override fun decode(databaseValue: String) = ComposeType.valueOf(databaseValue) + + override fun encode(value: ComposeType) = value.name +} + +internal class MicroBlogKeyColumnAdapter : ColumnAdapter { + override fun decode(databaseValue: String) = MicroBlogKey.valueOf(databaseValue) + + override fun encode(value: MicroBlogKey) = value.toString() +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt new file mode 100644 index 000000000..eb230e8f4 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.Draft + +internal object DraftAdapterFactory { + fun create() = Draft.Adapter( + mediaAdapter = StringListColumnAdapter(), + composeTypeAdapter = ComposeTypeColumnAdapter(), + statusKeyAdapter = MicroBlogKeyColumnAdapter(), + excludedReplyUserIdsAdapter = StringListColumnAdapter() + ) +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt new file mode 100644 index 000000000..34ee50b4c --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.Search + +internal object SearchAdapterFactory { + fun create() = Search.Adapter( + accountKeyAdapter = MicroBlogKeyColumnAdapter() + ) +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt new file mode 100644 index 000000000..fbb65a71c --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt @@ -0,0 +1,98 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.db.base.BaseAppDatabaseTest +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.sqldelight.table.Draft +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class DraftQueriesImplTest : BaseAppDatabaseTest() { + @Test + fun insertDraft_InsertOrReplaceWhenIdEquals() = runBlocking { + val query = database.draftQueries + val insert = createDraft(UUID.randomUUID().toString()) + query.insert(insert) + var result = query.getAll().executeAsList() + assertEquals(1, result.size) + assertEquals("test", result.firstOrNull()?.content) + query.insert(insert.copy(content = "replace")) + result = query.getAll().executeAsList() + assertEquals(1, result.size) + assertEquals("replace", result.firstOrNull()?.content) + } + + @Test + fun get_getAllReturnsAllDraftAndGetReturnsDraftWithGivenId() = runBlocking { + val query = database.draftQueries + val specifiedId = UUID.randomUUID().toString() + query.transaction { + for (i in 0 until 10) { + query.insert(createDraft(UUID.randomUUID().toString())) + } + query.insert(createDraft(specifiedId)) + } + assertEquals(11, query.getAll().executeAsList().size) + assertEquals(specifiedId, query.get(specifiedId).executeAsOneOrNull()?.id) + } + + @Test + fun remove_removeDraftWithGivenId() = runBlocking { + val query = database.draftQueries + val specifiedId = UUID.randomUUID().toString() + query.transaction { + for (i in 0 until 10) { + query.insert(createDraft(UUID.randomUUID().toString())) + } + query.insert(createDraft(specifiedId)) + } + assertEquals(11, query.getAll().executeAsList().size) + assertEquals(specifiedId, query.get(specifiedId).executeAsOneOrNull()?.id) + query.remove(specifiedId) + assertNull(query.get(specifiedId).executeAsOneOrNull()) + assertEquals(10, query.getAll().executeAsList().size) + } + + @Test + fun getDraftCount_ReturnsCountOfAllDrafts() = runBlocking { + val query = database.draftQueries + query.transaction { + for (i in 0 until 10) { + query.insert(createDraft(UUID.randomUUID().toString())) + } + } + assertEquals(10, query.getDraftCount().executeAsOneOrNull()) + } + + private fun createDraft(id: String) = Draft( + id = id, + content = "test", + media = listOf(), + createAt = System.currentTimeMillis(), + composeType = ComposeType.New, + statusKey = null, + excludedReplyUserIds = null + ) +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt index 5282252c9..8492cf2ce 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt @@ -22,17 +22,103 @@ package com.twidere.twiderex.db import com.twidere.twiderex.db.base.BaseAppDatabaseTest import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.sqldelight.table.Search +import com.twidere.twiderex.sqldelight.table.SearchQueries import kotlinx.coroutines.runBlocking import org.junit.Test -import kotlin.test.assertNotNull +import kotlin.test.assertEquals +import kotlin.test.assertNull -// Todo complete test internal class SearchQueriesImplTest : BaseAppDatabaseTest() { + private val twitterAccountKey = MicroBlogKey.twitter("123") + private val mastodonAccountKey = MicroBlogKey("456", "mastodon.com") @Test - fun insertSearch(): Unit = runBlocking { + fun insertSearch_InsertOrReplaceWhenContentAndAccountKeyEquals(): Unit = runBlocking { val query = database.searchQueries - val accountKey = MicroBlogKey.twitter("test") - query.insert("test", System.currentTimeMillis(), 0, accountKey = accountKey.toString()) - assertNotNull(query.get("test", accountKey.toString())) + val insert = Search("test", System.currentTimeMillis(), false, accountKey = twitterAccountKey) + query.insert(insert) + var result = query.getAll(twitterAccountKey).executeAsList() + assertEquals(1, result.size) + assertEquals(false, result.firstOrNull()?.saved) + query.insert(insert.copy(saved = true)) + result = query.getAll(twitterAccountKey).executeAsList() + assertEquals(1, result.size) + assertEquals(true, result.firstOrNull()?.saved) + } + + @Test + fun getAll_ReturnResultByAccountKey() = runBlocking { + val query = database.searchQueries + query.insertList(5, twitterAccountKey) + query.insertList(5, mastodonAccountKey) + val twitterResult = query.getAll(twitterAccountKey).executeAsList() + assert(twitterResult.isNotEmpty()) + twitterResult.forEach { + assertEquals(twitterAccountKey, it.accountKey) + } + + val mastodonResult = query.getAll(mastodonAccountKey).executeAsList() + assert(mastodonResult.isNotEmpty()) + mastodonResult.forEach { + assertEquals(mastodonAccountKey, it.accountKey) + } + } + + @Test + fun getHistory_ReturnResultWhichSavedIs0() = runBlocking { + val query = database.searchQueries + query.insertList(5, twitterAccountKey, saved = false) + query.insertList(5, mastodonAccountKey, saved = true) + val history = query.getHistories(twitterAccountKey).executeAsList() + assert(history.isNotEmpty()) + history.forEach { + assertEquals(false, it.saved) + } + } + + @Test + fun getSaved_ReturnResultWhichSavedIs1() = runBlocking { + val query = database.searchQueries + query.insertList(5, twitterAccountKey, saved = false) + query.insertList(5, mastodonAccountKey, saved = true) + val saved = query.getSaved(mastodonAccountKey).executeAsList() + assert(saved.isNotEmpty()) + saved.forEach { + assertEquals(true, it.saved) + } + } + + @Test + fun removeSearch() = runBlocking { + val query = database.searchQueries + query.insertList(5, twitterAccountKey, saved = false) + query.insert(Search(content = "remove", lastActive = System.currentTimeMillis(), saved = false, accountKey = twitterAccountKey)) + assertEquals("remove", query.get(content = "remove", accountKey = twitterAccountKey).executeAsOneOrNull()?.content) + query.remove(content = "remove", accountKey = twitterAccountKey) + assertNull(query.get(content = "remove", accountKey = twitterAccountKey).executeAsOneOrNull()) + } + + @Test + fun clear_clearAllHistories() = runBlocking { + val query = database.searchQueries + query.insertList(5, twitterAccountKey, saved = false) + query.insertList(5, mastodonAccountKey, saved = true) + assertEquals(5, query.getAll(mastodonAccountKey).executeAsList().size) + assertEquals(5, query.getAll(twitterAccountKey).executeAsList().size) + query.clear() + assertEquals(0, query.getAll(twitterAccountKey).executeAsList().size) + val result = query.getAll(mastodonAccountKey).executeAsList() + assertEquals(5, result.size) + result.forEach { + assertEquals(true, it.saved) + } + } + + private fun SearchQueries.insertList(count: Int, accountKey: MicroBlogKey, saved: Boolean = false) { + transaction { + for (i in 0 until count) { + insert(Search("test $i", lastActive = System.currentTimeMillis(), saved = saved, accountKey = accountKey)) + } + } } } diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt index 9eb527a5f..2e6eccc27 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt @@ -22,6 +22,8 @@ package com.twidere.twiderex.db.base import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import com.twidere.twiderex.sqldelight.adapter.DraftAdapterFactory +import com.twidere.twiderex.sqldelight.adapter.SearchAdapterFactory import org.junit.Before internal open class BaseAppDatabaseTest { @@ -30,6 +32,10 @@ internal open class BaseAppDatabaseTest { fun setUp() { val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) SqlDelightAppDatabase.Schema.create(driver) - database = SqlDelightAppDatabase(driver) + database = SqlDelightAppDatabase( + driver = driver, + draftAdapter = DraftAdapterFactory.create(), + searchAdapter = SearchAdapterFactory.create() + ) } } From 24f38b440c88e3b484709838f39637c4567e3cdf Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 13:32:37 +0800 Subject: [PATCH 202/615] add expect and actual for video widget --- .../foundation/AndroidVideoPlayer.kt | 44 ++++++------ .../kotlin/com/twidere/twiderex/App.kt | 7 +- .../component/foundation/VideoPlayer.kt | 67 ++++++++++++------- 3 files changed, 63 insertions(+), 55 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 3672e66e2..55e5a7727 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -91,7 +91,7 @@ actual fun VideoPlayerImpl( Box { if (playInitial) { val player = remember(url) { - getNativePlater( + getNativePlayer( url = url, autoPlay = autoPlay, setShowThumb = { @@ -216,12 +216,12 @@ actual fun VideoPlayerImpl( } @Composable -fun PlatformView( +actual fun PlatformView( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - modifier: Modifier = Modifier, - update: (NativePlayerView) -> Unit = {} + modifier: Modifier, + update: (NativePlayerView) -> Unit ) { val nativePlayer = remember { nativeViewFactory( @@ -242,58 +242,56 @@ fun PlatformView( } -class NativePlayerView() { - var playerView: Any?= null +actual class NativePlayerView actual constructor() { + actual var playerView: Any?= null var player: NativePlayer?= null private fun realPlayerView() = playerView as? StyledPlayerView - fun resume() = realPlayerView()?.onResume() + actual fun resume() = realPlayerView()?.onResume() - fun pause() = realPlayerView()?.onPause() + actual fun pause() = realPlayerView()?.onPause() } -class NativePlayer { - var player: Any?= null - - private fun realPlayer() = player as? RemainingTimeExoPlayer +actual class NativePlayer { + actual var player: Any?= null - // var playWhenReady = realPlayer as + fun realPlayer() = player as? RemainingTimeExoPlayer - var playWhenReady: Boolean + actual var playWhenReady: Boolean get() = realPlayer()?.playWhenReady ?: false set(value) { realPlayer()?.playWhenReady = value } - fun contentPosition(): Long = realPlayer()?.contentPosition?:0L + actual fun contentPosition(): Long = realPlayer()?.contentPosition?:0L - fun setCustomControl(customControl: Any?) { + actual fun setCustomControl(customControl: Any?) { (customControl as? PlayerControlView)?.player = realPlayer() } - fun resume() { + actual fun resume() { // getRealPlayer()?.res } - fun pause() { + actual fun pause() { realPlayer()?.pause() } - fun update() { + actual fun update() { } - fun setVolume(volume: Float) { + actual fun setVolume(volume: Float) { realPlayer()?.volume = volume } - fun release() { + actual fun release() { realPlayer()?.release() } } -fun nativeViewFactory( +actual fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean @@ -308,7 +306,7 @@ fun nativeViewFactory( } } -fun getNativePlater( +actual fun getNativePlayer( url: String, autoPlay: Boolean, setShowThumb: (Boolean) -> Unit, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 9a5f1711e..15aa68d62 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -20,13 +20,11 @@ */ package com.twidere.twiderex -import androidx.compose.foundation.layout.Column import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import com.twidere.twiderex.component.foundation.VideoPreview import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.ext.get @@ -37,10 +35,7 @@ fun App() { ) { MaterialTheme { Scaffold { - Column { - Text("Twidere X!") - VideoPreview() - } + Text("Twidere X!") } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 8c5a91632..13f11f5f4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -20,16 +20,8 @@ */ package com.twidere.twiderex.component.foundation -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalVideoPlayback @@ -77,22 +69,45 @@ internal fun getPlayInitial() = when(LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Off -> false } -@Composable -fun VideoPreview() { - LazyColumn { - for (i in 0..5) { - item { - Box( - modifier = Modifier - .background(Color.DarkGray) - .padding(100.dp) - ) { - VideoPlayer( - url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", - modifier = Modifier.width(500.dp).height(500.dp) - ) - } - } - } - } +expect class NativePlayerView() { + var playerView: Any? + var player: NativePlayer? + fun resume(): Unit? + fun pause(): Unit? } + +expect class NativePlayer { + + // var playWhenReady = realPlayer as + var player: Any? + var playWhenReady: Boolean + fun contentPosition(): Long + fun setCustomControl(customControl: Any?) + fun resume() + fun pause() + fun update() + fun setVolume(volume: Float) + fun release() +} + +expect fun nativeViewFactory( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean +): NativePlayerView + +expect fun getNativePlayer( + url: String, + autoPlay: Boolean, + setShowThumb: (Boolean) -> Unit, + setPLaying: (Boolean) -> Unit +): NativePlayer + +@Composable +expect fun PlatformView( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + modifier: Modifier, + update: (NativePlayerView) -> Unit +) \ No newline at end of file From 5f43844de67008d4e928a808bd7b261018ed4e09 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 14:14:01 +0800 Subject: [PATCH 203/615] [wip] --- .../foundation/AndroidVideoPlayer.kt | 13 +- .../component/foundation/VideoPlayer.kt | 175 ++++++++++++++++-- 2 files changed, 171 insertions(+), 17 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 55e5a7727..d0d7b5c36 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.component.foundation +import android.content.Context import android.view.SurfaceView import android.view.View import androidx.compose.foundation.background @@ -223,11 +224,13 @@ actual fun PlatformView( modifier: Modifier, update: (NativePlayerView) -> Unit ) { + val context = LocalContext.current val nativePlayer = remember { nativeViewFactory( zOrderMediaOverlay, showControls, - keepScreenOn + keepScreenOn, + context ) } AndroidView( @@ -244,7 +247,7 @@ actual fun PlatformView( actual class NativePlayerView actual constructor() { actual var playerView: Any?= null - var player: NativePlayer?= null + actual var player: NativePlayer?= null private fun realPlayerView() = playerView as? StyledPlayerView @@ -294,11 +297,11 @@ actual class NativePlayer { actual fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, - keepScreenOn: Boolean + keepScreenOn: Boolean, + context: Any ): NativePlayerView { - val context = LocalContext.current return NativePlayerView().apply { - playerView = StyledPlayerView(context).also { playerView -> + playerView = StyledPlayerView(context as Context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) playerView.useController = showControls playerView.keepScreenOn = keepScreenOn diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 13f11f5f4..78c91f88e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -20,11 +20,41 @@ */ package com.twidere.twiderex.component.foundation +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Icon +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.boundsInWindow +import androidx.compose.ui.layout.onGloballyPositioned +import com.twidere.twiderex.MR +import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalVideoPlayback +import com.twidere.twiderex.utils.video.VideoPool +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import moe.tlaster.precompose.lifecycle.Lifecycle +import moe.tlaster.precompose.lifecycle.LifecycleObserver +import moe.tlaster.precompose.ui.LocalLifecycleOwner expect fun VideoPlayerImpl( modifier: Modifier = Modifier, @@ -50,17 +80,137 @@ fun VideoPlayer( isListItem: Boolean = true, thumb: @Composable (() -> Unit)? = null, ) { - VideoPlayerImpl( - modifier = modifier, - url = url, - volume = volume, - customControl = customControl, - showControls = showControls, - zOrderMediaOverlay = zOrderMediaOverlay, - keepScreenOn = keepScreenOn, - isListItem = isListItem, - thumb = thumb - ) + var playing by remember { mutableStateOf(false) } + var shouldShowThumb by remember { mutableStateOf(false) } + val playInitial = getPlayInitial() + var autoPlay by remember(url) { mutableStateOf(playInitial) } + val lifecycle = LocalLifecycleOwner.current.lifecycle + val resLoder = LocalResLoader.current + Box { + if (playInitial) { + val player = remember(url) { + getNativePlayer( + url = url, + autoPlay = autoPlay, + setShowThumb = { + shouldShowThumb = it + }, + setPLaying = { + playing = it + } + ) + } + player.setVolume(volume) + + fun updateState() { + autoPlay = player.playWhenReady + VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition())) + } + + LaunchedEffect(customControl) { + player.setCustomControl(customControl) + } + var isResume by remember { + mutableStateOf(true) + } + val videoKey by remember { + mutableStateOf(url + System.currentTimeMillis()) + } + DisposableEffect(Unit) { + val observer = object : LifecycleObserver { + override fun onStateChanged(state: Lifecycle.State) { + when(state) { + Lifecycle.State.Active -> { + isResume = true + player.playWhenReady = autoPlay + } + Lifecycle.State.InActive -> { + isResume = false + updateState() + player.playWhenReady = false + } + else -> {} + } + } + } + lifecycle.addObserver(observer) + onDispose { + updateState() + player.release() + VideoPool.removeRect(videoKey) + lifecycle.removeObserver(observer) + } + } + + var middleLine = 0.0f + val composableScope = rememberCoroutineScope() + + var isMostCenter by remember(url) { + mutableStateOf(false) + } + var debounceJob: Job? = null + PlatformView( + zOrderMediaOverlay = zOrderMediaOverlay, + showControls = showControls, + keepScreenOn = keepScreenOn, + modifier = modifier.onGloballyPositioned { coordinates -> + if (middleLine == 0.0f) { + var rootCoordinates = coordinates + while (rootCoordinates.parentCoordinates != null) { + rootCoordinates = rootCoordinates.parentCoordinates!! + } + rootCoordinates.boundsInWindow().run { + middleLine = (top + bottom) / 2 + } + } + coordinates.boundsInWindow().run { + VideoPool.setRect(videoKey, this) + if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { + debounceJob?.cancel() + debounceJob = composableScope.launch { + delay(VideoPool.DEBOUNCE_DELAY) + if (VideoPool.containsMiddleLine(videoKey, middleLine)) { + isMostCenter = true + } + } + } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { + isMostCenter = false + } + } + }, + ) { + it.player = player + if (isResume && isMostCenter) { + if (isListItem) { + player.playWhenReady = autoPlay + } + it.resume() + } else { + if (isListItem) { + player.playWhenReady = false + } + it.pause() + } + } + } + if ((shouldShowThumb || !playing) && thumb != null) { + thumb() + Box( + modifier = Modifier + .fillMaxSize() + ) { + Icon( + imageVector = Icons.Default.PlayArrow, + tint = Color.White.copy(alpha = LocalContentAlpha.current), + modifier = Modifier + .align(Alignment.Center) + .size(UserAvatarDefaults.AvatarSize) + .background(MaterialTheme.colors.primary, CircleShape), + contentDescription = resLoder.getString(MR.strings.accessibility_common_video_play) + ) + } + } + } } internal fun getPlayInitial() = when(LocalVideoPlayback.current) { @@ -93,7 +243,8 @@ expect class NativePlayer { expect fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, - keepScreenOn: Boolean + keepScreenOn: Boolean, + context: Any ): NativePlayerView expect fun getNativePlayer( From 7afadb5d8afe03b7d66ae9ebe3e45ec93634ffe6 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 16:33:01 +0800 Subject: [PATCH 204/615] fix build --- .../foundation/AndroidVideoPlayer.kt | 23 +++++++++++++++---- .../component/foundation/VideoPlayer.kt | 15 +++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index d0d7b5c36..62ee11e9d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -58,6 +58,7 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.twidere.services.http.config.HttpConfig import com.twidere.twiderex.MR.strings.accessibility_common_video_play import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.http.TwidereServiceFactory @@ -89,12 +90,16 @@ actual fun VideoPlayerImpl( var autoPlay by remember(url) { mutableStateOf(playInitial) } val lifecycle = LocalLifecycleOwner.current.lifecycle val resLoder = LocalResLoader.current + val context = getContext() + val httpConfig = httpConfig() Box { if (playInitial) { val player = remember(url) { getNativePlayer( url = url, autoPlay = autoPlay, + context = context, + httpConfig = httpConfig, setShowThumb = { shouldShowThumb = it }, @@ -309,19 +314,29 @@ actual fun nativeViewFactory( } } +@Composable +actual fun getContext(): Any { + return LocalContext.current +} + +@Composable +actual fun httpConfig(): Any { + return LocalHttpConfig.current +} + actual fun getNativePlayer( url: String, autoPlay: Boolean, + context: Any, + httpConfig: Any, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit, ): NativePlayer { - val context = LocalContext.current - val httpConfig = LocalHttpConfig.current return NativePlayer().apply { player = RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context) + SimpleExoPlayer.Builder(context as Context) .apply { - if (httpConfig.proxyConfig.enable) { + if ((httpConfig as HttpConfig).proxyConfig.enable) { // replace DataSource OkHttpDataSource.Factory( TwidereServiceFactory diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 78c91f88e..314dd3e0b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -86,12 +86,16 @@ fun VideoPlayer( var autoPlay by remember(url) { mutableStateOf(playInitial) } val lifecycle = LocalLifecycleOwner.current.lifecycle val resLoder = LocalResLoader.current + val context = getContext() + val httpConfig = httpConfig() Box { if (playInitial) { val player = remember(url) { getNativePlayer( url = url, autoPlay = autoPlay, + httpConfig = httpConfig, + context = context, setShowThumb = { shouldShowThumb = it }, @@ -213,6 +217,7 @@ fun VideoPlayer( } } +@Composable internal fun getPlayInitial() = when(LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Auto -> !LocalIsActiveNetworkMetered.current DisplayPreferences.AutoPlayback.Always -> true @@ -250,6 +255,8 @@ expect fun nativeViewFactory( expect fun getNativePlayer( url: String, autoPlay: Boolean, + context: Any, + httpConfig: Any, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit ): NativePlayer @@ -261,4 +268,10 @@ expect fun PlatformView( keepScreenOn: Boolean, modifier: Modifier, update: (NativePlayerView) -> Unit -) \ No newline at end of file +) + +@Composable +expect fun getContext(): Any + +@Composable +expect fun httpConfig(): Any \ No newline at end of file From d1d7796f55a0dcf44fc68f2cf8566b8f370f4c22 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sat, 18 Sep 2021 16:36:41 +0800 Subject: [PATCH 205/615] Implement AppDatabase and daos for desktop --- .../twiderex/sqldelight/table/draft.sq | 4 +- .../twidere/twiderex/sqldelight/table/drop.sq | 4 + .../twiderex/dataprovider/dao/DraftDaoImpl.kt | 49 ++++++++++++ .../dataprovider/dao/SearchDaoImpl.kt | 75 +++++++++++++++++++ .../dataprovider/db/AppDatabaseImpl.kt | 44 +++++++++++ .../sqldelight/transform/DraftTransform.kt | 44 +++++++++++ .../sqldelight/transform/SearchTransform.kt | 38 ++++++++++ 7 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq index eaabd4f07..61d3ed354 100644 --- a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq @@ -5,9 +5,9 @@ import kotlin.collections.List; CREATE TABLE draft ( id TEXT NOT NULL PRIMARY KEY, content TEXT NOT NULL, - media TEXT AS List, + media TEXT AS List NOT NULL, createAt INTEGER NOT NULL, - composeType TEXT AS ComposeType, + composeType TEXT AS ComposeType NOT NULL, statusKey TEXT AS MicroBlogKey DEFAULT NULL, excludedReplyUserIds TEXT AS List DEFAULT NULL ); diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq new file mode 100644 index 000000000..668e45000 --- /dev/null +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq @@ -0,0 +1,4 @@ +clearAllTables{ + DELETE FROM search; + DELETE FROM draft; +} \ No newline at end of file diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt new file mode 100644 index 000000000..c2894f46b --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.dao + +import com.squareup.sqldelight.Query +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToList +import com.squareup.sqldelight.runtime.coroutines.mapToOneNotNull +import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.sqldelight.table.Draft +import com.twidere.twiderex.sqldelight.table.DraftQueries +import com.twidere.twiderex.sqldelight.transform.toDbDraft +import com.twidere.twiderex.sqldelight.transform.toUi +import kotlinx.coroutines.flow.map + +internal class DraftDaoImpl(private val queries: DraftQueries) : DraftDao { + override fun getAll() = queries.getAll().asUiFlow() + + override fun getDraftCount() = queries.getDraftCount().asFlow().mapToOneNotNull() + + override suspend fun insert(it: UiDraft) = queries.insert(it.toDbDraft()) + + override suspend fun get(draftId: String) = queries.get(draftId).executeAsOneOrNull()?.toUi() + + override suspend fun remove(draft: UiDraft) = queries.remove(draft.draftId) + + private fun Query.asUiFlow() = asFlow() + .mapToList() + .map { it.map { draft -> draft.toUi() } } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt new file mode 100644 index 000000000..0dde28121 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt @@ -0,0 +1,75 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.dao + +import com.squareup.sqldelight.Query +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToList +import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.sqldelight.table.Search +import com.twidere.twiderex.sqldelight.table.SearchQueries +import com.twidere.twiderex.sqldelight.transform.toDbSearch +import com.twidere.twiderex.sqldelight.transform.toUi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class SearchDaoImpl(private val queries: SearchQueries) : SearchDao { + override suspend fun insertAll(search: List) { + queries.transaction { + search.forEach { + queries.insert(search = it.toDbSearch()) + } + } + } + + override fun getAll(accountKey: MicroBlogKey): Flow> { + return queries.getAll(accountKey = accountKey) + .asUiFlow() + } + + override fun getAllHistory(accountKey: MicroBlogKey): Flow> { + return queries.getHistories(accountKey = accountKey) + .asUiFlow() + } + + override fun getAllSaved(accountKey: MicroBlogKey): Flow> { + return queries.getSaved(accountKey = accountKey) + .asUiFlow() + } + + override suspend fun get(content: String, accountKey: MicroBlogKey): UiSearch? { + return queries.get(content = content, accountKey = accountKey).executeAsOneOrNull()?.toUi() + } + + override suspend fun remove(search: UiSearch) { + queries.remove(content = search.content, accountKey = search.accountKey) + } + + override suspend fun clear() { + queries.clear() + } + + private fun Query.asUiFlow() = asFlow() + .mapToList() + .map { it.map { search -> search.toUi() } } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt new file mode 100644 index 000000000..71391c1e0 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.db + +import com.twidere.twiderex.dataprovider.dao.DraftDaoImpl +import com.twidere.twiderex.dataprovider.dao.SearchDaoImpl +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import kotlinx.coroutines.runBlocking + +internal class AppDatabaseImpl(private val database: SqlDelightAppDatabase) : AppDatabase { + private val draftDao = DraftDaoImpl(database.draftQueries) + override fun draftDao() = draftDao + + private val searchDao = SearchDaoImpl(database.searchQueries) + override fun searchDao() = searchDao + + override suspend fun clearAllTables() { + database.dropQueries.clearAllTables() + } + + override suspend fun withTransaction(block: suspend () -> R): R { + // TODO find a way to handle transaction + return database.transactionWithResult { runBlocking { block.invoke() } } + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt new file mode 100644 index 000000000..cb9ab169a --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.transform + +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.sqldelight.table.Draft + +fun Draft.toUi() = UiDraft( + draftId = id, + content = content, + media = media, + createdAt = createAt, + composeType = composeType, + statusKey = statusKey, + excludedReplyUserIds = excludedReplyUserIds +) + +fun UiDraft.toDbDraft() = Draft( + id = draftId, + content = content, + media = media, + createAt = createdAt, + composeType = composeType, + statusKey = statusKey, + excludedReplyUserIds = excludedReplyUserIds +) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt new file mode 100644 index 000000000..a12d63683 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.transform + +import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.sqldelight.table.Search + +internal fun Search.toUi() = UiSearch( + content = content, + lastActive = lastActive, + saved = saved, + accountKey = accountKey +) + +internal fun UiSearch.toDbSearch() = Search( + content = content, + lastActive = lastActive, + saved = saved, + accountKey = accountKey +) From 027e9ec0dee6ee50c5c2ef0a6aa2f96ad9d931a6 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 16:38:20 +0800 Subject: [PATCH 206/615] remove useless code --- .../foundation/AndroidVideoPlayer.kt | 177 ------------------ 1 file changed, 177 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 62ee11e9d..590d71a26 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -23,29 +23,9 @@ package com.twidere.twiderex.component.foundation import android.content.Context import android.view.SurfaceView import android.view.View -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.boundsInWindow -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -59,167 +39,10 @@ import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.twidere.services.http.config.HttpConfig -import com.twidere.twiderex.MR.strings.accessibility_common_video_play -import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig import com.twidere.twiderex.utils.video.CacheDataSourceFactory import com.twidere.twiderex.utils.video.VideoPool -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import moe.tlaster.precompose.lifecycle.Lifecycle -import moe.tlaster.precompose.lifecycle.LifecycleObserver -import moe.tlaster.precompose.ui.LocalLifecycleOwner - -@Composable -actual fun VideoPlayerImpl( - modifier: Modifier, - url: String, - volume: Float, - customControl: Any?, - showControls: Boolean, - zOrderMediaOverlay: Boolean, - keepScreenOn: Boolean, - isListItem: Boolean, - thumb: @Composable (() -> Unit)? -) { - var playing by remember { mutableStateOf(false) } - var shouldShowThumb by remember { mutableStateOf(false) } - val playInitial = getPlayInitial() - var autoPlay by remember(url) { mutableStateOf(playInitial) } - val lifecycle = LocalLifecycleOwner.current.lifecycle - val resLoder = LocalResLoader.current - val context = getContext() - val httpConfig = httpConfig() - Box { - if (playInitial) { - val player = remember(url) { - getNativePlayer( - url = url, - autoPlay = autoPlay, - context = context, - httpConfig = httpConfig, - setShowThumb = { - shouldShowThumb = it - }, - setPLaying = { - playing = it - } - ) - } - player.setVolume(volume) - - fun updateState() { - autoPlay = player.playWhenReady - VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition())) - } - - LaunchedEffect(customControl) { - player.setCustomControl(customControl) - } - var isResume by remember { - mutableStateOf(true) - } - val videoKey by remember { - mutableStateOf(url + System.currentTimeMillis()) - } - DisposableEffect(Unit) { - val observer = object : LifecycleObserver { - override fun onStateChanged(state: Lifecycle.State) { - when(state) { - Lifecycle.State.Active -> { - isResume = true - player.playWhenReady = autoPlay - } - Lifecycle.State.InActive -> { - isResume = false - updateState() - player.playWhenReady = false - } - else -> {} - } - } - } - lifecycle.addObserver(observer) - onDispose { - updateState() - player.release() - VideoPool.removeRect(videoKey) - lifecycle.removeObserver(observer) - } - } - - var middleLine = 0.0f - val composableScope = rememberCoroutineScope() - - var isMostCenter by remember(url) { - mutableStateOf(false) - } - var debounceJob: Job? = null - PlatformView( - zOrderMediaOverlay = zOrderMediaOverlay, - showControls = showControls, - keepScreenOn = keepScreenOn, - modifier = modifier.onGloballyPositioned { coordinates -> - if (middleLine == 0.0f) { - var rootCoordinates = coordinates - while (rootCoordinates.parentCoordinates != null) { - rootCoordinates = rootCoordinates.parentCoordinates!! - } - rootCoordinates.boundsInWindow().run { - middleLine = (top + bottom) / 2 - } - } - coordinates.boundsInWindow().run { - VideoPool.setRect(videoKey, this) - if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { - debounceJob?.cancel() - debounceJob = composableScope.launch { - delay(VideoPool.DEBOUNCE_DELAY) - if (VideoPool.containsMiddleLine(videoKey, middleLine)) { - isMostCenter = true - } - } - } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { - isMostCenter = false - } - } - }, - ) { - it.player = player - if (isResume && isMostCenter) { - if (isListItem) { - player.playWhenReady = autoPlay - } - it.resume() - } else { - if (isListItem) { - player.playWhenReady = false - } - it.pause() - } - } - } - if ((shouldShowThumb || !playing) && thumb != null) { - thumb() - Box( - modifier = Modifier - .fillMaxSize() - ) { - Icon( - imageVector = Icons.Default.PlayArrow, - tint = Color.White.copy(alpha = LocalContentAlpha.current), - modifier = Modifier - .align(Alignment.Center) - .size(UserAvatarDefaults.AvatarSize) - .background(MaterialTheme.colors.primary, CircleShape), - contentDescription = resLoder.getString(accessibility_common_video_play) - ) - } - } - } -} @Composable actual fun PlatformView( From e2acfc5d9b1aeeb9d424ce7f902a5bf3bdb2d448 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 17:26:24 +0800 Subject: [PATCH 207/615] initial migrate desktop player --- .../foundation/AndroidVideoPlayer.kt | 4 - .../component/foundation/VideoPlayer.kt | 7 +- .../foundation/DesktopVideoPlayer.kt | 85 +++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 590d71a26..d64df4eb9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -202,7 +202,3 @@ actual fun getNativePlayer( } } } - -object UserAvatarDefaults { - val AvatarSize = 44.dp -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 314dd3e0b..b4da089d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -43,6 +43,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.unit.dp import com.twidere.twiderex.MR import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.preferences.model.DisplayPreferences @@ -274,4 +275,8 @@ expect fun PlatformView( expect fun getContext(): Any @Composable -expect fun httpConfig(): Any \ No newline at end of file +expect fun httpConfig(): Any + +object UserAvatarDefaults { + val AvatarSize = 44.dp +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index a9316e8e1..1660b3f4d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -155,6 +155,91 @@ actual fun VideoPlayerImpl( } } +actual class NativePlayer { + actual var player: Any? = null + get() = TODO("Not yet implemented") + actual var playWhenReady: Boolean = false + get() = TODO("Not yet implemented") + + actual fun contentPosition(): Long { + TODO("Not yet implemented") + } + + actual fun setCustomControl(customControl: Any?) { + } + + actual fun resume() { + } + + actual fun pause() { + } + + actual fun update() { + } + + actual fun setVolume(volume: Float) { + } + + actual fun release() { + } + +} + +actual class NativePlayerView actual constructor() { + actual var playerView: Any? = null + get() = TODO("Not yet implemented") + actual var player: NativePlayer? = null + get() = TODO("Not yet implemented") + + actual fun resume(): Unit? { + TODO("Not yet implemented") + } + + actual fun pause(): Unit? { + TODO("Not yet implemented") + } +} + +actual fun nativeViewFactory( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + context: Any +): NativePlayerView { + TODO("Not yet implemented") +} + +actual fun getNativePlayer( + url: String, + autoPlay: Boolean, + context: Any, + httpConfig: Any, + setShowThumb: (Boolean) -> Unit, + setPLaying: (Boolean) -> Unit +): NativePlayer { + TODO("Not yet implemented") +} + +@Composable +actual fun PlatformView( + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + modifier: Modifier, + update: (NativePlayerView) -> Unit +) { +} + +@Composable +actual fun getContext(): Any { + TODO("Not yet implemented") +} + +@Composable +actual fun httpConfig(): Any { + TODO("Not yet implemented") +} + /** * To return mediaPlayer from player components. * The method names are same, but they don't share the same parent/interface. From 102e9be21eed8c7838087198d44fe159465e0fae Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sat, 18 Sep 2021 17:40:27 +0800 Subject: [PATCH 208/615] add unit test for sqldelight transform/adpater and dao implements --- .../sqldelight/adapter/ColumnAdapters.kt | 6 +- .../{db => }/base/BaseAppDatabaseTest.kt | 2 +- .../twiderex/db/AppDatabaseImplTest.kt | 46 ++++++++ .../twiderex/db/dao/DraftDaoImplTest.kt | 70 ++++++++++++ .../twiderex/db/dao/SearchDaoImplTest.kt | 108 ++++++++++++++++++ .../DraftQueriesImplTest.kt | 4 +- .../SearchQueriesImplTest.kt | 4 +- .../adapter/ComposeTypeColumnAdapterTest.kt | 44 +++++++ .../adapter/MicroBlogKeyColumnAdapterTest.kt | 41 +++++++ .../adapter/StringListColumnAdapterTest.kt | 56 +++++++++ .../transform/DraftTransformTest.kt | 74 ++++++++++++ .../transform/SearchTransformTest.kt | 59 ++++++++++ 12 files changed, 506 insertions(+), 8 deletions(-) rename common/src/desktopTest/kotlin/com/twidere/twiderex/{db => }/base/BaseAppDatabaseTest.kt (97%) create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt rename common/src/desktopTest/kotlin/com/twidere/twiderex/{db => sqldelight}/DraftQueriesImplTest.kt (97%) rename common/src/desktopTest/kotlin/com/twidere/twiderex/{db => sqldelight}/SearchQueriesImplTest.kt (98%) create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt index 9a788070a..ecf84a68f 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt @@ -24,15 +24,15 @@ import com.squareup.sqldelight.ColumnAdapter import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType -internal class StringListColumnAdapter : ColumnAdapter, String> { +internal class StringListColumnAdapter(private val separator: String = ",") : ColumnAdapter, String> { override fun decode(databaseValue: String) = if (databaseValue.isEmpty()) { listOf() } else { - databaseValue.split(",") + databaseValue.split(separator) } - override fun encode(value: List) = value.joinToString { "," } + override fun encode(value: List) = value.joinToString(separator = separator) { it } } internal class ComposeTypeColumnAdapter : ColumnAdapter { diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt similarity index 97% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt rename to common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index 2e6eccc27..3c14ab2ac 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/base/BaseAppDatabaseTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.base +package com.twidere.twiderex.base import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt new file mode 100644 index 000000000..fe932cb98 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db + +import com.twidere.twiderex.base.BaseAppDatabaseTest +import com.twidere.twiderex.dataprovider.db.AppDatabaseImpl +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.sqldelight.table.Draft +import com.twidere.twiderex.sqldelight.table.Search +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID + +internal class AppDatabaseImplTest : BaseAppDatabaseTest() { + @Test + fun clearAllTables() = runBlocking { + val appDatabase = AppDatabaseImpl(database) + val accountKey = MicroBlogKey.twitter("test") + database.searchQueries.insert(Search(content = "test", lastActive = System.currentTimeMillis(), saved = false, accountKey = accountKey)) + database.draftQueries.insert(Draft(content = "test", id = UUID.randomUUID().toString(), media = emptyList(), createAt = System.currentTimeMillis(), composeType = ComposeType.New, statusKey = null, excludedReplyUserIds = emptyList())) + assert(database.draftQueries.getAll().executeAsList().isNotEmpty()) + assert(database.searchQueries.getAll(accountKey).executeAsList().isNotEmpty()) + appDatabase.clearAllTables() + assert(database.draftQueries.getAll().executeAsList().isEmpty()) + assert(database.searchQueries.getAll(accountKey).executeAsList().isEmpty()) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt new file mode 100644 index 000000000..ce2c06ad0 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.base.BaseAppDatabaseTest +import com.twidere.twiderex.dataprovider.dao.DraftDaoImpl +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.model.ui.UiDraft +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class DraftDaoImplTest : BaseAppDatabaseTest() { + @Test + fun getAll_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { + val draftDao = DraftDaoImpl(database.draftQueries) + val flow = draftDao.getAll() + assert(flow.firstOrNull()?.isEmpty() ?: false) + val draft = createUiDraft() + draftDao.insert(draft) + assert(flow.firstOrNull()?.isNotEmpty() ?: false) + draftDao.remove(draft) + assert(flow.firstOrNull()?.isEmpty() ?: true) + } + + @Test + fun getDraftCount_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { + val draftDao = DraftDaoImpl(database.draftQueries) + val flow = draftDao.getDraftCount() + assertEquals(0L, flow.firstOrNull() ?: false) + val draft = createUiDraft() + draftDao.insert(draft) + assertEquals(1L, flow.firstOrNull() ?: false) + draftDao.remove(draft) + assertEquals(0L, flow.firstOrNull() ?: false) + } + + private fun createUiDraft( + id: String = UUID.randomUUID().toString(), + content: String = UUID.randomUUID().toString(), + ) = UiDraft( + draftId = id, + content = content, + media = emptyList(), + createdAt = System.currentTimeMillis(), + composeType = ComposeType.New, + statusKey = null, + excludedReplyUserIds = null + ) +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt new file mode 100644 index 000000000..40e00f781 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt @@ -0,0 +1,108 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.base.BaseAppDatabaseTest +import com.twidere.twiderex.dataprovider.dao.SearchDaoImpl +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class SearchDaoImplTest : BaseAppDatabaseTest() { + @Test + fun getAll_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val searchDao = SearchDaoImpl(database.searchQueries) + val flow = searchDao.getAll(accountKey) + assert(flow.firstOrNull()?.isEmpty() ?: false) + searchDao.insertAll(createSearchList(accountKey = accountKey, count = 1)) + assert(flow.firstOrNull()?.isNotEmpty() ?: false) + } + + @Test + fun insertAll_InsertAllDataInGivenList() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val searchDao = SearchDaoImpl(database.searchQueries) + val flow = searchDao.getAll(accountKey) + assert(flow.firstOrNull()?.isEmpty() ?: false) + val count = 10 + searchDao.insertAll(createSearchList(count = count, accountKey = accountKey)) + assertEquals(count, flow.firstOrNull()?.size) + } + + @Test + fun getHistories_ReturnsFlowWithNotSavedSearchAndUpdateAfterDbUpdated() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val searchDao = SearchDaoImpl(database.searchQueries) + val flow = searchDao.getAllHistory(accountKey) + assert(flow.firstOrNull()?.isEmpty() ?: false) + searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey)) + searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey, saved = true)) + assertEquals(5, flow.firstOrNull()?.size) + flow.firstOrNull()?.forEach { + assertEquals(false, it.saved) + } + searchDao.insertAll(flow.firstOrNull()!!.map { it.copy(saved = true) }) + assert(flow.firstOrNull()?.isEmpty() ?: true) + searchDao.clear() + assert(flow.firstOrNull()?.isEmpty() ?: true) + } + + @Test + fun getSaved_ReturnsFlowWithSavedSearchAndUpdateAfterDbUpdated() = runBlocking { + val accountKey = MicroBlogKey.twitter("test") + val searchDao = SearchDaoImpl(database.searchQueries) + val flow = searchDao.getAllSaved(accountKey) + assert(flow.firstOrNull()?.isEmpty() ?: false) + searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey)) + searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey, saved = true)) + assertEquals(5, flow.firstOrNull()?.size) + flow.firstOrNull()?.forEach { + assertEquals(true, it.saved) + } + searchDao.insertAll(flow.firstOrNull()!!.map { it.copy(saved = false) }) + assert(flow.firstOrNull()?.isEmpty() ?: true) + } + + private fun createSearchList( + count: Int, + content: String = UUID.randomUUID().toString(), + accountKey: MicroBlogKey, + saved: Boolean = false + ): MutableList { + val list = mutableListOf() + for (i in 0 until count) { + list.add( + UiSearch( + content = content + i.toString(), + accountKey = accountKey, + lastActive = System.currentTimeMillis(), + saved = saved + ) + ) + } + return list + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt similarity index 97% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt rename to common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt index fbb65a71c..2af1f5221 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/DraftQueriesImplTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt @@ -18,9 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db +package com.twidere.twiderex.sqldelight -import com.twidere.twiderex.db.base.BaseAppDatabaseTest +import com.twidere.twiderex.base.BaseAppDatabaseTest import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.sqldelight.table.Draft import kotlinx.coroutines.runBlocking diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt similarity index 98% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt rename to common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt index 8492cf2ce..8bd8b8c1f 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/SearchQueriesImplTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt @@ -18,9 +18,9 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db +package com.twidere.twiderex.sqldelight -import com.twidere.twiderex.db.base.BaseAppDatabaseTest +import com.twidere.twiderex.base.BaseAppDatabaseTest import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.sqldelight.table.Search import com.twidere.twiderex.sqldelight.table.SearchQueries diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt new file mode 100644 index 000000000..018d2b088 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.twidere.twiderex.model.enums.ComposeType +import org.junit.Test +import kotlin.test.assertEquals + +internal class ComposeTypeColumnAdapterTest { + private val adapter = ComposeTypeColumnAdapter() + @Test + fun decode_TypeMatchesToName() { + assertEquals(ComposeType.New, adapter.decode("New")) + assertEquals(ComposeType.Quote, adapter.decode("Quote")) + assertEquals(ComposeType.Reply, adapter.decode("Reply")) + assertEquals(ComposeType.Thread, adapter.decode("Thread")) + } + + @Test + fun encode_NameMatchesToType() { + assertEquals("New", adapter.encode(ComposeType.New)) + assertEquals("Quote", adapter.encode(ComposeType.Quote)) + assertEquals("Reply", adapter.encode(ComposeType.Reply)) + assertEquals("Thread", adapter.encode(ComposeType.Thread)) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt new file mode 100644 index 000000000..1518478db --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.twidere.twiderex.model.MicroBlogKey +import org.junit.Test +import kotlin.test.assertEquals + +internal class MicroBlogKeyColumnAdapterTest { + private val adapter = MicroBlogKeyColumnAdapter() + @Test + fun decode_GenerateCorrectHostAndId() { + val key = adapter.decode("123@twitter.com") + assertEquals("123", key.id) + assertEquals("twitter.com", key.host) + } + + @Test + fun encode_CombineIdAndHostToString() { + val string = adapter.encode(MicroBlogKey(id = "123", host = "twitter.com")) + assertEquals("123@twitter.com", string) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt new file mode 100644 index 000000000..2dcbd03c6 --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt @@ -0,0 +1,56 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import org.junit.Test +import kotlin.test.assertEquals + +internal class StringListColumnAdapterTest { + @Test + fun decode_splitStringWithGivenSeparator() { + var adapter = StringListColumnAdapter(";") + val originString = "a,b,c,d" + var list = adapter.decode(originString) + assertEquals(1, list.size) + assertEquals(originString, list.first()) + adapter = StringListColumnAdapter(",") + list = adapter.decode(originString) + list.forEachIndexed { index, s -> + when (index) { + 0 -> assertEquals("a", s) + 1 -> assertEquals("b", s) + 2 -> assertEquals("c", s) + 3 -> assertEquals("d", s) + } + } + } + + @Test + fun encode_combineListContentToStringWithGivenSeparator() { + var adapter = StringListColumnAdapter(";") + val originList = listOf("a", "b", "c", "d") + var string = adapter.encode(originList) + assertEquals("a;b;c;d", string) + adapter = StringListColumnAdapter("|") + string = adapter.encode(originList) + assertEquals("a|b|c|d", string) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt new file mode 100644 index 000000000..d64f2311a --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt @@ -0,0 +1,74 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.model.ui.UiDraft +import com.twidere.twiderex.sqldelight.table.Draft +import org.junit.Test +import java.util.UUID +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals + +class DraftTransformTest { + @Test + fun draftToUi() { + val draft = Draft( + id = UUID.randomUUID().toString(), + content = "draft", + media = listOf("media"), + createAt = System.currentTimeMillis(), + composeType = ComposeType.New, + statusKey = MicroBlogKey.valueOf("status"), + excludedReplyUserIds = listOf("userId") + ) + val uiDraft = draft.toUi() + assertEquals(draft.id, uiDraft.draftId) + assertEquals(draft.content, uiDraft.content) + assertEquals(draft.createAt, uiDraft.createdAt) + assertEquals(draft.composeType, uiDraft.composeType) + assertEquals(draft.statusKey, uiDraft.statusKey) + assertContentEquals(draft.media, uiDraft.media) + assertContentEquals(draft.excludedReplyUserIds, uiDraft.excludedReplyUserIds) + } + + @Test + fun uiToDraft() { + val uiDraft = UiDraft( + draftId = UUID.randomUUID().toString(), + content = "draft", + media = listOf("media"), + createdAt = System.currentTimeMillis(), + composeType = ComposeType.New, + statusKey = MicroBlogKey.valueOf("status"), + excludedReplyUserIds = listOf("userId") + ) + val draft = uiDraft.toDbDraft() + assertEquals(draft.id, uiDraft.draftId) + assertEquals(draft.content, uiDraft.content) + assertEquals(draft.createAt, uiDraft.createdAt) + assertEquals(draft.composeType, uiDraft.composeType) + assertEquals(draft.statusKey, uiDraft.statusKey) + assertContentEquals(draft.media, uiDraft.media) + assertContentEquals(draft.excludedReplyUserIds, uiDraft.excludedReplyUserIds) + } +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt new file mode 100644 index 000000000..ec3aac02f --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt @@ -0,0 +1,59 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.sqldelight.table.Search +import org.junit.Test +import kotlin.test.assertEquals + +class SearchTransformTest { + @Test + fun searchToUi() { + val search = Search( + content = "search", + lastActive = System.currentTimeMillis(), + accountKey = MicroBlogKey.valueOf("test"), + saved = true + ) + val uiSearch = search.toUi() + assertEquals(search.content, uiSearch.content) + assertEquals(search.lastActive, uiSearch.lastActive) + assertEquals(search.accountKey, uiSearch.accountKey) + assertEquals(search.saved, uiSearch.saved) + } + + @Test + fun uiTosearch() { + val uiSearch = UiSearch( + content = "search", + lastActive = System.currentTimeMillis(), + accountKey = MicroBlogKey.valueOf("test"), + saved = true + ) + val search = uiSearch.toDbSearch() + assertEquals(search.content, uiSearch.content) + assertEquals(search.lastActive, uiSearch.lastActive) + assertEquals(search.accountKey, uiSearch.accountKey) + assertEquals(search.saved, uiSearch.saved) + } +} From 4657b873c09915a955f3d64e9abe5cda461515a4 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sat, 18 Sep 2021 18:29:53 +0800 Subject: [PATCH 209/615] [wip] add more testing --- .../com/twidere/twiderex/scenes/MediaScene.kt | 15 +++- .../platform/MastodonListsCreateDialog.kt | 32 ++++---- .../lists/platform/TwitterListsCreateScene.kt | 37 +++++---- common/build.gradle.kts | 1 - .../twiderex/di/modules/ViewModelModule.kt | 4 +- .../twiderex/viewmodel/AccountViewModel.kt | 35 +++++++++ .../twiderex/viewmodel/MediaViewModel.kt | 10 +-- .../viewmodel/lists/ListsViewModel.kt | 17 ++++- .../mock/service/MockLookUpService.kt | 4 +- .../mock/service/MockRelationshipService.kt | 4 +- .../viewmodel/ActiveAccountViewModelTest.kt | 52 +++++++++++++ .../twiderex/viewmodel/DraftViewModelTest.kt | 58 ++++++++++++++ .../twiderex/viewmodel/MediaViewModelTest.kt | 17 +++-- .../viewmodel/PureMediaViewModelTest.kt | 3 +- .../twiderex/viewmodel/StatusViewModelTest.kt | 3 +- .../lists/ListsCreateViewModelTest.kt | 32 +++----- .../user/UserViewModelRelationshipTest.kt | 76 +++++++++++++++++++ 17 files changed, 313 insertions(+), 87 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index a49a47cea..02fd7106c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -56,6 +56,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -106,6 +107,7 @@ import com.twidere.twiderex.ui.LocalVideoPlayback import com.twidere.twiderex.ui.LocalWindow import com.twidere.twiderex.ui.TwidereDialog import com.twidere.twiderex.viewmodel.MediaViewModel +import kotlinx.coroutines.launch import moe.tlaster.swiper.Swiper import moe.tlaster.swiper.SwiperState import moe.tlaster.swiper.rememberSwiperState @@ -295,6 +297,7 @@ private fun StatusMediaInfo( viewModel: MediaViewModel, currentMedia: UiMedia ) { + val scope = rememberCoroutineScope() Column( modifier = Modifier .padding(StatusMediaInfoDefaults.ContentPadding), @@ -325,7 +328,9 @@ private fun StatusMediaInfo( contract = ActivityResultContracts.CreateDocument() ) { it?.let { - viewModel.saveFile(currentMedia, it.toString()) + scope.launch { + viewModel.saveFile(currentMedia, it.toString()) + } } } ShareButton(status = status) { callback -> @@ -345,9 +350,11 @@ private fun StatusMediaInfo( onClick = { callback.invoke() currentMedia.fileName?.let { - viewModel.shareMedia( - currentMedia = currentMedia - ) + scope.launch { + viewModel.shareMedia( + currentMedia = currentMedia + ) + } } } ) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index 0db5620a8..d30035ba3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.Dialog @@ -36,11 +37,13 @@ import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel +import kotlinx.coroutines.launch import org.koin.core.parameter.parametersOf @Composable fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { val navController = LocalNavController.current + val scope = rememberCoroutineScope() var showMastodonComponent by remember { mutableStateOf(true) } @@ -51,20 +54,7 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { var name by remember { mutableStateOf("") } - val listsCreateViewModel: ListsCreateViewModel = getViewModel { - parametersOf( - { success: Boolean, list: UiList? -> - dismiss() - if (success) { - list?.apply { - navController.navigate( - RootRoute.Lists.Timeline(listKey), - ) - } - } - } - ) - } + val listsCreateViewModel: ListsCreateViewModel = getViewModel() val loading by listsCreateViewModel.loading.observeAsState(initial = false) if (loading) { @@ -85,9 +75,17 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { name = name, onNameChanged = { name = it } ) { - listsCreateViewModel.createList( - title = it - ) + scope.launch { + val result = listsCreateViewModel.createList( + title = it + ) + dismiss() + if (result != null) { + navController.navigate( + RootRoute.Lists.Timeline(result.listKey), + ) + } + } } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index 54832ea3c..61d55a628 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -34,6 +34,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.compose.ui.window.Dialog @@ -50,6 +51,7 @@ import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel +import kotlinx.coroutines.launch import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.PopUpTo import org.koin.core.parameter.parametersOf @@ -57,20 +59,8 @@ import org.koin.core.parameter.parametersOf @Composable fun TwitterListsCreateScene() { val navController = LocalNavController.current - val listsCreateViewModel: ListsCreateViewModel = getViewModel { - parametersOf( - { success: Boolean, list: UiList? -> - if (success) list?.apply { - navController.navigate( - RootRoute.Lists.Timeline(listKey), - options = NavOptions( - popUpTo = PopUpTo(RootRoute.Lists.Home) - ) - ) - } - } - ) - } + val scope = rememberCoroutineScope() + val listsCreateViewModel: ListsCreateViewModel = getViewModel() val loading by listsCreateViewModel.loading.observeAsState(initial = false) TwidereScene { @@ -94,11 +84,20 @@ fun TwitterListsCreateScene() { IconButton( enabled = name.isNotEmpty(), onClick = { - listsCreateViewModel.createList( - title = name, - description = desc, - private = isPrivate - ) + scope.launch { + listsCreateViewModel.createList( + title = name, + description = desc, + private = isPrivate + )?.let { + navController.navigate( + RootRoute.Lists.Timeline(it.listKey), + options = NavOptions( + popUpTo = PopUpTo(RootRoute.Lists.Home) + ) + ) + } + } } ) { Icon( diff --git a/common/build.gradle.kts b/common/build.gradle.kts index f5208be44..0a7ab612a 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -55,7 +55,6 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") implementation("io.mockk:mockk-common:1.12.0") implementation("io.mockk:mockk:1.12.0") - implementation("org.jetbrains.kotlin:kotlin-reflect:${Versions.Kotlin.lang}") } } val androidMain by getting { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt index 33f45c6df..fc47420e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -24,7 +24,6 @@ import com.twidere.twiderex.extensions.viewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.ui.UiDraft -import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.preferences.PreferencesHolder import com.twidere.twiderex.viewmodel.ActiveAccountViewModel import com.twidere.twiderex.viewmodel.DraftViewModel @@ -156,12 +155,11 @@ private fun Module.lists() { ) } viewModel { ListsViewModel(get(), get()) } - viewModel { (onResult: (success: Boolean, list: UiList?) -> Unit) -> + viewModel { ListsCreateViewModel( get(), get(), get(), - onResult ) } viewModel { (listKey: MicroBlogKey) -> ListsModifyViewModel(get(), get(), get(), listKey) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt new file mode 100644 index 000000000..8cef5b50d --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel + +import com.twidere.twiderex.extensions.asStateIn +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.flow.mapNotNull +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope + +abstract class AccountViewModel( + private val accountRepository: AccountRepository, +) : ViewModel() { + protected val account by lazy { + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 40b488bcf..55f9a7b2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -45,8 +44,8 @@ class MediaViewModel( accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } - fun saveFile(currentMedia: UiMedia, target: String) = viewModelScope.launch { - val account = account.firstOrNull() ?: return@launch + suspend fun saveFile(currentMedia: UiMedia, target: String) { + val account = account.firstOrNull() ?: return currentMedia.mediaUrl?.let { mediaAction.download( accountKey = account.accountKey, @@ -56,8 +55,8 @@ class MediaViewModel( } } - fun shareMedia(currentMedia: UiMedia) = viewModelScope.launch { - val account = account.firstOrNull() ?: return@launch + suspend fun shareMedia(currentMedia: UiMedia) { + val account = account.firstOrNull() ?: return currentMedia.mediaUrl?.let { mediaAction.share( source = it, @@ -67,6 +66,7 @@ class MediaViewModel( } val loading = MutableStateFlow(false) + @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { account.flatMapLatest { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index f591754b6..e2f836d42 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -110,18 +110,18 @@ class ListsCreateViewModel( inAppNotification: InAppNotification, private val listsRepository: ListsRepository, private val accountRepository: AccountRepository, - private val onResult: (success: Boolean, list: UiList?) -> Unit ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } - fun createList( + suspend fun createList( title: String, description: String? = null, private: Boolean = false - ) { - loadingRequest(onResult) { + ): UiList? { + loading.value = true + return try { account.firstOrNull()?.let { account -> listsRepository.createLists( accountKey = account.accountKey, @@ -130,7 +130,16 @@ class ListsCreateViewModel( description = description, mode = if (private) ListsMode.PRIVATE.value else ListsMode.PUBLIC.value ) + }.let { + modifySuccess.value = true + it } + } catch (e: Throwable) { + inAppNotification.notifyError(e) + modifySuccess.value = false + null + } finally { + loading.value = false } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt index c521469d1..21d9e451a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt @@ -21,13 +21,15 @@ package com.twidere.twiderex.mock.service import com.twidere.services.microblog.LookupService +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.model.IStatus import com.twidere.services.microblog.model.IUser import com.twidere.twiderex.mock.model.mockIStatus import com.twidere.twiderex.mock.model.mockIUser import org.jetbrains.annotations.TestOnly -internal class MockLookUpService @TestOnly constructor() : LookupService, ErrorService() { +internal class MockLookUpService @TestOnly constructor() : MicroBlogService, LookupService, + ErrorService() { override suspend fun lookupStatus(id: String): IStatus { return mockIStatus(id = id) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt index dc24cc397..c26365405 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.mock.service +import com.twidere.services.microblog.MicroBlogService import com.twidere.services.microblog.RelationshipService import com.twidere.services.microblog.model.IRelationship import com.twidere.services.microblog.model.IUser @@ -28,7 +29,8 @@ import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.mock.model.toIPaging import org.jetbrains.annotations.TestOnly -internal class MockRelationshipService @TestOnly constructor() : RelationshipService, +internal class MockRelationshipService @TestOnly constructor() : MicroBlogService, + RelationshipService, ErrorService() { private val followings = mutableListOf() private val followers = mutableListOf() diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt new file mode 100644 index 000000000..bfad82f1d --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt @@ -0,0 +1,52 @@ +package com.twidere.twiderex.viewmodel + +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.repository.AccountRepository +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class ActiveAccountViewModelTest : ViewModelTestBase() { + @MockK(relaxed = true) + private lateinit var repository: AccountRepository + private lateinit var viewModel: ActiveAccountViewModel + + override fun setUp() { + super.setUp() + viewModel = ActiveAccountViewModel(repository) + every { repository.getFirstByType(PlatformType.Twitter) }.returns(mockk()) + every { repository.getFirstByType(PlatformType.Fanfou) }.returns(null) + } + + @Test + fun set_activeAccount() { + viewModel.setActiveAccount(mockk()) + verify(exactly = 1) { repository.setCurrentAccount(any()) } + } + + @Test + fun delete_account() { + viewModel.deleteAccount(mockk()) + verify(exactly = 1) { repository.delete(any()) } + } + + @Test + fun get_target_platform_success() { + viewModel.getTargetPlatformDefault(PlatformType.Twitter).let { + assertNotNull(it) + verify(exactly = 1) { repository.getFirstByType(PlatformType.Twitter) } + } + } + + @Test + fun get_target_platform_failed() { + viewModel.getTargetPlatformDefault(PlatformType.Fanfou).let { + assertNull(it) + verify(exactly = 1) { repository.getFirstByType(PlatformType.Fanfou) } + } + } +} \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt new file mode 100644 index 000000000..2595c98a7 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt @@ -0,0 +1,58 @@ +package com.twidere.twiderex.viewmodel + +import com.twidere.twiderex.action.DraftAction +import com.twidere.twiderex.repository.DraftRepository +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +internal class DraftViewModelTest : ViewModelTestBase() { + @MockK + private lateinit var action: DraftAction + + @MockK + private lateinit var repository: DraftRepository + private lateinit var viewModel: DraftViewModel + + override fun setUp() { + super.setUp() + viewModel = DraftViewModel(repository, action) + every { repository.source }.returns( + flowOf( + (0..3).map { + mockk { + every { draftId }.returns(it.toString()) + } + } + ) + ) + } + + @Test + fun draft_list() = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assertEquals(4, it.size) + assertContentEquals( + (0..3).map { it.toString() }.toTypedArray(), + it.map { it.draftId }.toTypedArray() + ) + } + } + + @Test + fun delete_draft() = runBlocking { + viewModel.delete(mockk{ + every { draftId }.returns("123") + }) + verify(exactly = 1) { action.delete("123") } + } +} \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt index 2426b0d84..77935f48c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt @@ -27,9 +27,9 @@ import com.twidere.twiderex.repository.StatusRepository import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.justRun import io.mockk.mockk import io.mockk.verify -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking @@ -62,10 +62,11 @@ internal class MediaViewModelTest : AccountViewModelTestBase() { } ) ) + justRun { mediaAction.download(any(), any(), any()) } } @Test - fun load_status(): Unit = runBlocking(Dispatchers.Main) { + fun load_status(): Unit = runBlocking { viewModel.status.firstOrNull().let { assertNotNull(it) assertEquals(MicroBlogKey.twitter("123"), it.statusKey) @@ -73,18 +74,24 @@ internal class MediaViewModelTest : AccountViewModelTestBase() { } @Test - fun saveFile_success(): Unit = runBlocking(Dispatchers.Main) { + fun saveFile_success(): Unit = runBlocking { viewModel.saveFile( mockk { every { mediaUrl }.returns("123") }, "target", ) - verify(exactly = 1) { mediaAction.download("123", "target", MicroBlogKey.twitter("123")) } + verify(exactly = 1) { + mediaAction.download( + "123", + "target", + MicroBlogKey.twitter("123") + ) + } } @Test - fun shareMedia_success(): Unit = runBlocking(Dispatchers.Main) { + fun shareMedia_success(): Unit = runBlocking { viewModel.shareMedia( mockk { every { mediaUrl }.returns("123") diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt index 1500ca7c0..66fb8260d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt @@ -26,7 +26,6 @@ import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking import org.junit.Test @@ -55,7 +54,7 @@ internal class PureMediaViewModelTest : ViewModelTestBase() { } @Test - fun loadSource_success(): Unit = runBlocking(Dispatchers.Main) { + fun loadSource_success(): Unit = runBlocking { viewModel.source.firstOrNull().let { assertNotNull(it) assertEquals(4, it.size) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt index 0fd3ff17f..195ab5dfe 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt @@ -28,7 +28,6 @@ import com.twidere.twiderex.repository.StatusRepository import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking @@ -60,7 +59,7 @@ internal class StatusViewModelTest : AccountViewModelTestBase() { } @Test - fun source_loadConversation(): Unit = runBlocking(Dispatchers.Main) { + fun source_loadConversation(): Unit = runBlocking { viewModel.source.first().let { val data = it.collectDataForTest() assertEquals(5, data.size) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt index 90567f0f8..c88fc3e33 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt @@ -33,7 +33,6 @@ import io.mockk.impl.annotations.MockK import io.mockk.verify import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -50,9 +49,6 @@ internal class ListsCreateViewModelTest : AccountViewModelTestBase() { @MockK private lateinit var mockAppNotification: InAppNotification - @MockK - private lateinit var mockSuccessObserver: Observer - @MockK private lateinit var mockLoadingObserver: Observer @@ -68,14 +64,11 @@ internal class ListsCreateViewModelTest : AccountViewModelTestBase() { mockAppNotification, mockRepository, mockAccountRepository - ) { success, _ -> - mockSuccessObserver.onChanged(success) - } + ) every { mockAppNotification.show(any()) }.answers { errorNotification = arg(0) } errorNotification = null - mockSuccessObserver.onChanged(false) scope.launch { createViewModel.loading.collect { mockLoadingObserver.onChanged(it) @@ -85,39 +78,32 @@ internal class ListsCreateViewModelTest : AccountViewModelTestBase() { @Test fun createList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) - async { - createViewModel.createList(title = "title", private = false) - }.await() - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, true) + verifySuccessAndLoadingBefore(mockLoadingObserver) + val result = createViewModel.createList(title = "title", private = false) + assertNotNull(result) + verifySuccessAndLoadingAfter(mockLoadingObserver) } @Test fun createList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver, mockSuccessObserver) + verifySuccessAndLoadingBefore(mockLoadingObserver) assertNull(errorNotification) - async { - createViewModel.createList(title = "error", private = false) - }.await() - verifySuccessAndLoadingAfter(mockLoadingObserver, mockSuccessObserver, false) + val result = createViewModel.createList(title = "error", private = false) + assertNull(result) + verifySuccessAndLoadingAfter(mockLoadingObserver) assertNotNull(errorNotification) } private fun verifySuccessAndLoadingBefore( loadingObserver: Observer, - successObserver: Observer ) { verify(exactly = 1) { loadingObserver.onChanged(false) } - verify { successObserver.onChanged(false) } } private fun verifySuccessAndLoadingAfter( loadingObserver: Observer, - successObserver: Observer, - success: Boolean ) { verify(exactly = 1) { loadingObserver.onChanged(true) } verify(exactly = 1) { loadingObserver.onChanged(false) } - verify(exactly = if (success) 1 else 2) { successObserver.onChanged(success) } } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt new file mode 100644 index 000000000..54a0e170f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt @@ -0,0 +1,76 @@ +package com.twidere.twiderex.viewmodel.user + +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.UserRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class UserViewModelRelationshipTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockRelationshipService() + + @MockK(relaxed = true) + private lateinit var repository: UserRepository + + @MockK + private lateinit var inAppNotification: InAppNotification + + override fun setUp() { + super.setUp() + } + + @Test + fun is_me() = runBlocking { + every { repository.getUserFlow(any()) }.returns(flowOf(mockk { + every { userKey }.returns(mockAccount.accountKey) + })) + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + mockAccount.accountKey + ) + viewModel.isMe.firstOrNull().let { + assertNotNull(it) + assert(it) + } + } + + @Test + fun is_not_me() = runBlocking { + every { repository.getUserFlow(any()) }.returns(flowOf(mockk { + every { userKey }.returns(MicroBlogKey.twitter("321")) + })) + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + MicroBlogKey.twitter("321") + ) + viewModel.isMe.firstOrNull().let { + assertNotNull(it) + assert(!it) + } + } + + @Test + fun follow_success() = runBlocking { + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + MicroBlogKey.twitter("321") + ) + viewModel.follow() + } +} \ No newline at end of file From 8a6fa27e103729a62498921aebfce9f14daeae7e Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 19:12:53 +0800 Subject: [PATCH 210/615] desktop two player is same --- .../foundation/AndroidVideoPlayer.kt | 17 +- .../component/foundation/VideoPlayer.kt | 21 +- .../foundation/DesktopVideoPlayer.kt | 184 ++++-------------- 3 files changed, 55 insertions(+), 167 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index d64df4eb9..b73b501af 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player @@ -50,6 +49,7 @@ actual fun PlatformView( showControls: Boolean, keepScreenOn: Boolean, modifier: Modifier, + player: Any?, update: (NativePlayerView) -> Unit ) { val context = LocalContext.current @@ -72,10 +72,9 @@ actual fun PlatformView( ) } - actual class NativePlayerView actual constructor() { - actual var playerView: Any?= null - actual var player: NativePlayer?= null + actual var playerView: Any? = null + actual var player: NativePlayer? = null private fun realPlayerView() = playerView as? StyledPlayerView @@ -84,8 +83,8 @@ actual class NativePlayerView actual constructor() { actual fun pause() = realPlayerView()?.onPause() } -actual class NativePlayer { - actual var player: Any?= null +actual class NativePlayer { + actual var player: Any? = null fun realPlayer() = player as? RemainingTimeExoPlayer @@ -95,7 +94,7 @@ actual class NativePlayer { realPlayer()?.playWhenReady = value } - actual fun contentPosition(): Long = realPlayer()?.contentPosition?:0L + actual fun contentPosition(): Long = realPlayer()?.contentPosition ?: 0L actual fun setCustomControl(customControl: Any?) { (customControl as? PlayerControlView)?.player = realPlayer() @@ -110,7 +109,6 @@ actual class NativePlayer { } actual fun update() { - } actual fun setVolume(volume: Float) { @@ -126,7 +124,8 @@ actual fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - context: Any + context: Any, + player: Any?, ): NativePlayerView { return NativePlayerView().apply { playerView = StyledPlayerView(context as Context).also { playerView -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index b4da089d4..50eea6981 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -57,18 +57,6 @@ import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.LifecycleObserver import moe.tlaster.precompose.ui.LocalLifecycleOwner -expect fun VideoPlayerImpl( - modifier: Modifier = Modifier, - url: String, - volume: Float = 1f, - customControl: Any? = null, - showControls: Boolean = customControl == null, - zOrderMediaOverlay: Boolean = false, - keepScreenOn: Boolean = false, - isListItem: Boolean = true, - thumb: @Composable (() -> Unit)? = null, -) - @Composable fun VideoPlayer( modifier: Modifier = Modifier, @@ -124,7 +112,7 @@ fun VideoPlayer( DisposableEffect(Unit) { val observer = object : LifecycleObserver { override fun onStateChanged(state: Lifecycle.State) { - when(state) { + when (state) { Lifecycle.State.Active -> { isResume = true player.playWhenReady = autoPlay @@ -158,6 +146,7 @@ fun VideoPlayer( zOrderMediaOverlay = zOrderMediaOverlay, showControls = showControls, keepScreenOn = keepScreenOn, + player = player, modifier = modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates @@ -219,7 +208,7 @@ fun VideoPlayer( } @Composable -internal fun getPlayInitial() = when(LocalVideoPlayback.current) { +internal fun getPlayInitial() = when (LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Auto -> !LocalIsActiveNetworkMetered.current DisplayPreferences.AutoPlayback.Always -> true DisplayPreferences.AutoPlayback.Off -> false @@ -250,7 +239,8 @@ expect fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - context: Any + context: Any, + player: Any? = null ): NativePlayerView expect fun getNativePlayer( @@ -268,6 +258,7 @@ expect fun PlatformView( showControls: Boolean, keepScreenOn: Boolean, modifier: Modifier, + player: Any? = null, update: (NativePlayerView) -> Unit ) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 1660b3f4d..af9f104f1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -20,149 +20,24 @@ */ package com.twidere.twiderex.component.foundation -import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.boundsInWindow -import androidx.compose.ui.layout.onGloballyPositioned -import com.twidere.twiderex.utils.video.VideoPool -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import moe.tlaster.precompose.lifecycle.Lifecycle -import moe.tlaster.precompose.lifecycle.LifecycleObserver -import moe.tlaster.precompose.ui.LocalLifecycleOwner -import uk.co.caprica.vlcj.factory.discovery.NativeDiscovery import uk.co.caprica.vlcj.player.base.MediaPlayer import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent +import uk.co.caprica.vlcj.player.component.MediaPlayerComponent +import java.awt.Component import java.util.Locale -@Composable -actual fun VideoPlayerImpl( - modifier: Modifier, - url: String, - volume: Float, - customControl: Any?, - showControls: Boolean, - zOrderMediaOverlay: Boolean, - keepScreenOn: Boolean, - isListItem: Boolean, - thumb: @Composable (() -> Unit)? -) { - NativeDiscovery().discover() - val lifecycle = LocalLifecycleOwner.current.lifecycle - var active by remember(url) { - mutableStateOf(true) - } - val mediaPlayerComponent = remember(url) { - // see https://github.com/caprica/vlcj/issues/887#issuecomment-503288294 - // for why we're using CallbackMediaPlayerComponent for macOS. - if (isMacOS()) { - CallbackMediaPlayerComponent() - } else { - EmbeddedMediaPlayerComponent() - } - } - - var middleLine = 0.0f - val composableScope = rememberCoroutineScope() - - val videoKey = remember { - url + System.nanoTime() - } - - var isMostCenter by remember(url) { - mutableStateOf(false) - } - DisposableEffect(url) { - - mediaPlayerComponent.mediaPlayer().media().prepare(url) - - val lifecycleObserver = object : LifecycleObserver { - override fun onStateChanged(state: Lifecycle.State) { - when (state) { - Lifecycle.State.Active -> { - active = true - } - Lifecycle.State.InActive -> { - active = false - } - else -> {} - } - } - } - lifecycle.addObserver(lifecycleObserver) - - onDispose { - lifecycle.removeObserver(lifecycleObserver) - mediaPlayerComponent.mediaPlayer().release() - } - } - - var debounceJob: Job? = remember { - null - } - - return Box( - modifier = modifier.onGloballyPositioned { coordinates -> - if (middleLine == 0.0f) { - var rootCoordinates = coordinates - while (rootCoordinates.parentCoordinates != null) { - rootCoordinates = rootCoordinates.parentCoordinates!! - } - rootCoordinates.boundsInWindow().run { - middleLine = (top + bottom) / 2 - } - } - coordinates.boundsInWindow().run { - VideoPool.setRect(videoKey, this) - if (!isMostCenter && VideoPool.containsMiddleLine(videoKey, middleLine)) { - debounceJob?.cancel() - debounceJob = composableScope.launch { - delay(VideoPool.DEBOUNCE_DELAY) - if (VideoPool.containsMiddleLine(videoKey, middleLine)) { - isMostCenter = true - } - } - } else if (isMostCenter && !VideoPool.isMostCenter(videoKey, middleLine)) { - isMostCenter = false - } - } - }, - ) { - SwingPanel( - background = Color.Transparent, - factory = { - mediaPlayerComponent - } - ) { - val controls = it.mediaPlayer().controls() - if (isMostCenter && active) { - controls.play() - } else { - controls.setPause(true) - } - } - } -} - actual class NativePlayer { actual var player: Any? = null - get() = TODO("Not yet implemented") + actual var playWhenReady: Boolean = false - get() = TODO("Not yet implemented") actual fun contentPosition(): Long { - TODO("Not yet implemented") + return 0L } actual fun setCustomControl(customControl: Any?) { @@ -182,31 +57,29 @@ actual class NativePlayer { actual fun release() { } - } actual class NativePlayerView actual constructor() { actual var playerView: Any? = null - get() = TODO("Not yet implemented") actual var player: NativePlayer? = null - get() = TODO("Not yet implemented") - actual fun resume(): Unit? { - TODO("Not yet implemented") - } + private fun realPlayerView() = playerView as? MediaPlayerComponent - actual fun pause(): Unit? { - TODO("Not yet implemented") - } + actual fun resume() = realPlayerView()?.mediaPlayer()?.controls()?.play() + + actual fun pause() = realPlayerView()?.mediaPlayer()?.controls()?.pause() } actual fun nativeViewFactory( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - context: Any + context: Any, + player: Any?, ): NativePlayerView { - TODO("Not yet implemented") + return NativePlayerView().apply { + playerView = player + } } actual fun getNativePlayer( @@ -217,7 +90,13 @@ actual fun getNativePlayer( setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit ): NativePlayer { - TODO("Not yet implemented") + return NativePlayer().apply { + player = if (isMacOS()) { + CallbackMediaPlayerComponent() + } else { + EmbeddedMediaPlayerComponent() + } + } } @Composable @@ -226,18 +105,37 @@ actual fun PlatformView( showControls: Boolean, keepScreenOn: Boolean, modifier: Modifier, + player: Any?, update: (NativePlayerView) -> Unit ) { + val nativePlayer = remember { + nativeViewFactory( + zOrderMediaOverlay, + showControls, + keepScreenOn, + Any(), + player + ) + } + SwingPanel( + factory = { + nativePlayer.playerView as Component + }, + modifier = modifier, + update = { + update.invoke(nativePlayer) + } + ) } @Composable actual fun getContext(): Any { - TODO("Not yet implemented") + return Any() } @Composable actual fun httpConfig(): Any { - TODO("Not yet implemented") + return Any() } /** From 31f5571a99284cc89ca029e2b9eb8eeb273587b2 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 19:46:23 +0800 Subject: [PATCH 211/615] rename --- .../component/foundation/VideoPlayer.kt | 24 +++++++++---------- .../foundation/DesktopVideoPlayer.kt | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 50eea6981..6f59fbc82 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -79,7 +79,7 @@ fun VideoPlayer( val httpConfig = httpConfig() Box { if (playInitial) { - val player = remember(url) { + val nativePlayer = remember(url) { getNativePlayer( url = url, autoPlay = autoPlay, @@ -93,15 +93,15 @@ fun VideoPlayer( } ) } - player.setVolume(volume) + nativePlayer.setVolume(volume) fun updateState() { - autoPlay = player.playWhenReady - VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition())) + autoPlay = nativePlayer.playWhenReady + VideoPool.set(url, 0L.coerceAtLeast(nativePlayer.contentPosition())) } LaunchedEffect(customControl) { - player.setCustomControl(customControl) + nativePlayer.setCustomControl(customControl) } var isResume by remember { mutableStateOf(true) @@ -115,12 +115,12 @@ fun VideoPlayer( when (state) { Lifecycle.State.Active -> { isResume = true - player.playWhenReady = autoPlay + nativePlayer.playWhenReady = autoPlay } Lifecycle.State.InActive -> { isResume = false updateState() - player.playWhenReady = false + nativePlayer.playWhenReady = false } else -> {} } @@ -129,7 +129,7 @@ fun VideoPlayer( lifecycle.addObserver(observer) onDispose { updateState() - player.release() + nativePlayer.release() VideoPool.removeRect(videoKey) lifecycle.removeObserver(observer) } @@ -146,7 +146,7 @@ fun VideoPlayer( zOrderMediaOverlay = zOrderMediaOverlay, showControls = showControls, keepScreenOn = keepScreenOn, - player = player, + player = nativePlayer.player, modifier = modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates @@ -173,15 +173,15 @@ fun VideoPlayer( } }, ) { - it.player = player + it.player = nativePlayer if (isResume && isMostCenter) { if (isListItem) { - player.playWhenReady = autoPlay + nativePlayer.playWhenReady = autoPlay } it.resume() } else { if (isListItem) { - player.playWhenReady = false + nativePlayer.playWhenReady = false } it.pause() } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index af9f104f1..d1f327129 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -119,7 +119,7 @@ actual fun PlatformView( } SwingPanel( factory = { - nativePlayer.playerView as Component + nativePlayer.playerView as CallbackMediaPlayerComponent }, modifier = modifier, update = { From f0be711af51bb0d5cab29ea2400aab4154fe8a0c Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 18 Sep 2021 19:57:39 +0800 Subject: [PATCH 212/615] [wip] --- .../component/foundation/DesktopVideoPlayer.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index d1f327129..7e0eed401 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -28,7 +28,6 @@ import uk.co.caprica.vlcj.player.base.MediaPlayer import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import uk.co.caprica.vlcj.player.component.MediaPlayerComponent -import java.awt.Component import java.util.Locale actual class NativePlayer { @@ -91,10 +90,12 @@ actual fun getNativePlayer( setPLaying: (Boolean) -> Unit ): NativePlayer { return NativePlayer().apply { - player = if (isMacOS()) { + player = (if (isMacOS()) { CallbackMediaPlayerComponent() } else { EmbeddedMediaPlayerComponent() + }).apply { + mediaPlayer().media().prepare(url) } } } @@ -119,7 +120,11 @@ actual fun PlatformView( } SwingPanel( factory = { - nativePlayer.playerView as CallbackMediaPlayerComponent + if (isMacOS()) { + nativePlayer.playerView as CallbackMediaPlayerComponent + } else { + nativePlayer.playerView as EmbeddedMediaPlayerComponent + } }, modifier = modifier, update = { From d5176016169c0ad349fa3ca27429d5c07ebb16ab Mon Sep 17 00:00:00 2001 From: huixing Date: Sun, 19 Sep 2021 11:24:09 +0800 Subject: [PATCH 213/615] fix coordinates size always 0 --- .../component/foundation/VideoPlayer.kt | 43 +++++++++++-------- .../foundation/DesktopVideoPlayer.kt | 16 ++++--- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 6f59fbc82..10e098821 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -142,12 +142,8 @@ fun VideoPlayer( mutableStateOf(false) } var debounceJob: Job? = null - PlatformView( - zOrderMediaOverlay = zOrderMediaOverlay, - showControls = showControls, - keepScreenOn = keepScreenOn, - player = nativePlayer.player, - modifier = modifier.onGloballyPositioned { coordinates -> + Box( + modifier = Modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates while (rootCoordinates.parentCoordinates != null) { @@ -171,22 +167,31 @@ fun VideoPlayer( isMostCenter = false } } - }, + } ) { - it.player = nativePlayer - if (isResume && isMostCenter) { - if (isListItem) { - nativePlayer.playWhenReady = autoPlay - } - it.resume() - } else { - if (isListItem) { - nativePlayer.playWhenReady = false + PlatformView( + zOrderMediaOverlay = zOrderMediaOverlay, + showControls = showControls, + keepScreenOn = keepScreenOn, + player = nativePlayer.player, + modifier = modifier, + ) { + it.player = nativePlayer + if (isResume && isMostCenter) { + if (isListItem) { + nativePlayer.playWhenReady = autoPlay + } + it.resume() + } else { + if (isListItem) { + nativePlayer.playWhenReady = false + } + it.pause() } - it.pause() } } } + if ((shouldShowThumb || !playing) && thumb != null) { thumb() Box( @@ -200,7 +205,9 @@ fun VideoPlayer( .align(Alignment.Center) .size(UserAvatarDefaults.AvatarSize) .background(MaterialTheme.colors.primary, CircleShape), - contentDescription = resLoder.getString(MR.strings.accessibility_common_video_play) + contentDescription = resLoder.getString( + MR.strings.accessibility_common_video_play + ) ) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 7e0eed401..61c539f52 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -90,12 +90,16 @@ actual fun getNativePlayer( setPLaying: (Boolean) -> Unit ): NativePlayer { return NativePlayer().apply { - player = (if (isMacOS()) { - CallbackMediaPlayerComponent() - } else { - EmbeddedMediaPlayerComponent() - }).apply { - mediaPlayer().media().prepare(url) + player = ( + if (isMacOS()) { + CallbackMediaPlayerComponent() + } else { + EmbeddedMediaPlayerComponent() + } + ).apply { + mediaPlayer().apply { + media().prepare(url) + } } } } From ae87c49a2ac836e2bc74d67f25049e197277d9ad Mon Sep 17 00:00:00 2001 From: huixing Date: Sun, 19 Sep 2021 21:44:44 +0800 Subject: [PATCH 214/615] rebuild player api --- .../foundation/AndroidVideoPlayer.kt | 162 +++++++----------- .../component/foundation/VideoPlayer.kt | 66 +++---- .../foundation/DesktopVideoPlayer.kt | 71 ++------ 3 files changed, 106 insertions(+), 193 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index b73b501af..8348f3c58 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -45,96 +45,50 @@ import com.twidere.twiderex.utils.video.VideoPool @Composable actual fun PlatformView( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, modifier: Modifier, - player: Any?, + nativePLayerView: NativePlayerView, update: (NativePlayerView) -> Unit ) { - val context = LocalContext.current - val nativePlayer = remember { - nativeViewFactory( - zOrderMediaOverlay, - showControls, - keepScreenOn, - context - ) - } AndroidView( factory = { - nativePlayer.playerView as View + nativePLayerView.player as View }, modifier = modifier, update = { - update.invoke(nativePlayer) + update.invoke(nativePLayerView) } ) } -actual class NativePlayerView actual constructor() { - actual var playerView: Any? = null - actual var player: NativePlayer? = null - - private fun realPlayerView() = playerView as? StyledPlayerView - - actual fun resume() = realPlayerView()?.onResume() - - actual fun pause() = realPlayerView()?.onPause() -} - -actual class NativePlayer { +actual class NativePlayerView { actual var player: Any? = null - fun realPlayer() = player as? RemainingTimeExoPlayer + private fun realPlayerView() = player as? StyledPlayerView - actual var playWhenReady: Boolean - get() = realPlayer()?.playWhenReady ?: false - set(value) { - realPlayer()?.playWhenReady = value - } - actual fun contentPosition(): Long = realPlayer()?.contentPosition ?: 0L - - actual fun setCustomControl(customControl: Any?) { - (customControl as? PlayerControlView)?.player = realPlayer() - } + actual var playWhenReady: Boolean = false actual fun resume() { - // getRealPlayer()?.res + realPlayerView()?.onResume() } actual fun pause() { - realPlayer()?.pause() + realPlayerView()?.onPause() } + actual fun contentPosition(): Long = 0L + actual fun update() { } actual fun setVolume(volume: Float) { - realPlayer()?.volume = volume } actual fun release() { - realPlayer()?.release() + realPlayerView()?.player?.release() } } -actual fun nativeViewFactory( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, - context: Any, - player: Any?, -): NativePlayerView { - return NativePlayerView().apply { - playerView = StyledPlayerView(context as Context).also { playerView -> - (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) - playerView.useController = showControls - playerView.keepScreenOn = keepScreenOn - } - } -} @Composable actual fun getContext(): Any { @@ -146,58 +100,68 @@ actual fun httpConfig(): Any { return LocalHttpConfig.current } -actual fun getNativePlayer( +actual fun getNativePlayerView( url: String, autoPlay: Boolean, context: Any, httpConfig: Any, + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit, -): NativePlayer { - return NativePlayer().apply { - player = RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context as Context) - .apply { - if ((httpConfig as HttpConfig).proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } +): NativePlayerView { + return NativePlayerView().apply { + StyledPlayerView(context as Context).also { playerView -> + (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) + playerView.useController = showControls + playerView.keepScreenOn = keepScreenOn + }.apply { + player = RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context as Context) + .apply { + if ((httpConfig as HttpConfig).proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } + } + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + setShowThumb(state != Player.STATE_READY) } - } - ).apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - setShowThumb(state != Player.STATE_READY) - } - override fun onIsPlayingChanged(isPlaying: Boolean) { - setPLaying(isPlaying) + override fun onIsPlayingChanged(isPlaying: Boolean) { + setPLaying(isPlaying) + } + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) } - }) - - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) + prepare() + seekTo(VideoPool.get(url)) } - prepare() - seekTo(VideoPool.get(url)) } + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 10e098821..9c0f99b09 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -79,12 +79,15 @@ fun VideoPlayer( val httpConfig = httpConfig() Box { if (playInitial) { - val nativePlayer = remember(url) { - getNativePlayer( + val nativePlayerView = remember(url) { + getNativePlayerView( url = url, autoPlay = autoPlay, httpConfig = httpConfig, context = context, + zOrderMediaOverlay = zOrderMediaOverlay, + showControls = showControls, + keepScreenOn = keepScreenOn, setShowThumb = { shouldShowThumb = it }, @@ -93,15 +96,16 @@ fun VideoPlayer( } ) } - nativePlayer.setVolume(volume) + + nativePlayerView.setVolume(volume) fun updateState() { - autoPlay = nativePlayer.playWhenReady - VideoPool.set(url, 0L.coerceAtLeast(nativePlayer.contentPosition())) + autoPlay = nativePlayerView.playWhenReady + VideoPool.set(url, 0L.coerceAtLeast(nativePlayerView.contentPosition())) } LaunchedEffect(customControl) { - nativePlayer.setCustomControl(customControl) + // nativePlayerView.setCustomControl(customControl) } var isResume by remember { mutableStateOf(true) @@ -115,12 +119,12 @@ fun VideoPlayer( when (state) { Lifecycle.State.Active -> { isResume = true - nativePlayer.playWhenReady = autoPlay + nativePlayerView.playWhenReady = autoPlay } Lifecycle.State.InActive -> { isResume = false updateState() - nativePlayer.playWhenReady = false + nativePlayerView.playWhenReady = false } else -> {} } @@ -129,7 +133,7 @@ fun VideoPlayer( lifecycle.addObserver(observer) onDispose { updateState() - nativePlayer.release() + nativePlayerView.release() VideoPool.removeRect(videoKey) lifecycle.removeObserver(observer) } @@ -170,21 +174,17 @@ fun VideoPlayer( } ) { PlatformView( - zOrderMediaOverlay = zOrderMediaOverlay, - showControls = showControls, - keepScreenOn = keepScreenOn, - player = nativePlayer.player, + nativePLayerView = nativePlayerView, modifier = modifier, ) { - it.player = nativePlayer if (isResume && isMostCenter) { if (isListItem) { - nativePlayer.playWhenReady = autoPlay + nativePlayerView.playWhenReady = autoPlay } it.resume() } else { if (isListItem) { - nativePlayer.playWhenReady = false + nativePlayerView.playWhenReady = false } it.pause() } @@ -221,51 +221,33 @@ internal fun getPlayInitial() = when (LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Off -> false } -expect class NativePlayerView() { - var playerView: Any? - var player: NativePlayer? - fun resume(): Unit? - fun pause(): Unit? -} - -expect class NativePlayer { - - // var playWhenReady = realPlayer as +expect class NativePlayerView { var player: Any? var playWhenReady: Boolean - fun contentPosition(): Long - fun setCustomControl(customControl: Any?) fun resume() fun pause() + fun contentPosition(): Long fun update() fun setVolume(volume: Float) fun release() } -expect fun nativeViewFactory( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, - context: Any, - player: Any? = null -): NativePlayerView - -expect fun getNativePlayer( +expect fun getNativePlayerView( url: String, autoPlay: Boolean, context: Any, httpConfig: Any, + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit -): NativePlayer +): NativePlayerView @Composable expect fun PlatformView( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, modifier: Modifier, - player: Any? = null, + nativePLayerView: NativePlayerView, update: (NativePlayerView) -> Unit ) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 61c539f52..1a8e229cf 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.component.foundation import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel import uk.co.caprica.vlcj.player.base.MediaPlayer @@ -30,24 +29,24 @@ import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import uk.co.caprica.vlcj.player.component.MediaPlayerComponent import java.util.Locale -actual class NativePlayer { - actual var player: Any? = null +actual class NativePlayerView { - actual var playWhenReady: Boolean = false + actual var player: Any?= null - actual fun contentPosition(): Long { - return 0L - } + actual var playWhenReady: Boolean = false - actual fun setCustomControl(customControl: Any?) { - } + private fun realPlayerView() = player as? MediaPlayerComponent actual fun resume() { + realPlayerView()?.mediaPlayer()?.controls()?.play() } actual fun pause() { + realPlayerView()?.mediaPlayer()?.controls()?.pause() } + actual fun contentPosition(): Long = 0L + actual fun update() { } @@ -58,38 +57,18 @@ actual class NativePlayer { } } -actual class NativePlayerView actual constructor() { - actual var playerView: Any? = null - actual var player: NativePlayer? = null - - private fun realPlayerView() = playerView as? MediaPlayerComponent - - actual fun resume() = realPlayerView()?.mediaPlayer()?.controls()?.play() - - actual fun pause() = realPlayerView()?.mediaPlayer()?.controls()?.pause() -} - -actual fun nativeViewFactory( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, - context: Any, - player: Any?, -): NativePlayerView { - return NativePlayerView().apply { - playerView = player - } -} - -actual fun getNativePlayer( +actual fun getNativePlayerView( url: String, autoPlay: Boolean, context: Any, httpConfig: Any, + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit -): NativePlayer { - return NativePlayer().apply { + setPLaying: (Boolean) -> Unit, +): NativePlayerView { + return NativePlayerView().apply { player = ( if (isMacOS()) { CallbackMediaPlayerComponent() @@ -106,33 +85,21 @@ actual fun getNativePlayer( @Composable actual fun PlatformView( - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, modifier: Modifier, - player: Any?, + nativePLayerView: NativePlayerView, update: (NativePlayerView) -> Unit ) { - val nativePlayer = remember { - nativeViewFactory( - zOrderMediaOverlay, - showControls, - keepScreenOn, - Any(), - player - ) - } SwingPanel( factory = { if (isMacOS()) { - nativePlayer.playerView as CallbackMediaPlayerComponent + nativePLayerView.player as CallbackMediaPlayerComponent } else { - nativePlayer.playerView as EmbeddedMediaPlayerComponent + nativePLayerView.player as EmbeddedMediaPlayerComponent } }, modifier = modifier, update = { - update.invoke(nativePlayer) + update.invoke(nativePLayerView) } ) } From 6557266e0c9aa35adff05aa6739426693c2583b0 Mon Sep 17 00:00:00 2001 From: huixing Date: Sun, 19 Sep 2021 21:48:52 +0800 Subject: [PATCH 215/615] fix desktop player crash --- .../twidere/twiderex/component/foundation/DesktopVideoPlayer.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 1a8e229cf..2743f4eea 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -54,6 +54,7 @@ actual class NativePlayerView { } actual fun release() { + realPlayerView()?.mediaPlayer()?.release() } } From 40c26c611349435edf3997c954e01e213a2eb6f5 Mon Sep 17 00:00:00 2001 From: huixing Date: Sun, 19 Sep 2021 23:29:24 +0800 Subject: [PATCH 216/615] format --- .../component/foundation/AndroidVideoPlayer.kt | 16 ++++++---------- .../twiderex/component/foundation/VideoPlayer.kt | 2 +- .../component/foundation/DesktopVideoPlayer.kt | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 8348f3c58..91f177785 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -24,7 +24,6 @@ import android.content.Context import android.view.SurfaceView import android.view.View import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView @@ -34,7 +33,6 @@ import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.twidere.services.http.config.HttpConfig @@ -61,19 +59,19 @@ actual fun PlatformView( } actual class NativePlayerView { - actual var player: Any? = null - private fun realPlayerView() = player as? StyledPlayerView + actual var player: Any = Any() + private fun realPlayerView() = player as StyledPlayerView actual var playWhenReady: Boolean = false actual fun resume() { - realPlayerView()?.onResume() + realPlayerView().onResume() } actual fun pause() { - realPlayerView()?.onPause() + realPlayerView().onPause() } actual fun contentPosition(): Long = 0L @@ -85,11 +83,10 @@ actual class NativePlayerView { } actual fun release() { - realPlayerView()?.player?.release() + realPlayerView().player?.release() } } - @Composable actual fun getContext(): Any { return LocalContext.current @@ -112,7 +109,7 @@ actual fun getNativePlayerView( setPLaying: (Boolean) -> Unit, ): NativePlayerView { return NativePlayerView().apply { - StyledPlayerView(context as Context).also { playerView -> + this.player = StyledPlayerView(context as Context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) playerView.useController = showControls playerView.keepScreenOn = keepScreenOn @@ -162,6 +159,5 @@ actual fun getNativePlayerView( seekTo(VideoPool.get(url)) } } - } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 9c0f99b09..fb5bc3612 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -222,7 +222,7 @@ internal fun getPlayInitial() = when (LocalVideoPlayback.current) { } expect class NativePlayerView { - var player: Any? + var player: Any var playWhenReady: Boolean fun resume() fun pause() diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 2743f4eea..124aa6099 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -29,9 +29,9 @@ import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import uk.co.caprica.vlcj.player.component.MediaPlayerComponent import java.util.Locale -actual class NativePlayerView { +actual class NativePlayerView { - actual var player: Any?= null + actual var player: Any = Any() actual var playWhenReady: Boolean = false From 53914d138f8adc75d3ae712c064acb8a5cfc1421 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 20 Sep 2021 14:57:00 +0800 Subject: [PATCH 217/615] move NativePlayerView create to constructor --- .../foundation/AndroidVideoPlayer.kt | 127 +++++++++--------- .../component/foundation/VideoPlayer.kt | 33 ++--- .../foundation/DesktopVideoPlayer.kt | 50 +++---- 3 files changed, 96 insertions(+), 114 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 91f177785..b70f21321 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -58,9 +58,67 @@ actual fun PlatformView( ) } -actual class NativePlayerView { +actual class NativePlayerView actual constructor( + url: String, + autoPlay: Boolean, + context: Any, + httpConfig: Any, + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + setShowThumb: (Boolean) -> Unit, + setPLaying: (Boolean) -> Unit, +) { + actual var player: Any = StyledPlayerView(context as Context).also { playerView -> + (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) + playerView.useController = showControls + playerView.keepScreenOn = keepScreenOn + }.apply { + player = RemainingTimeExoPlayer( + SimpleExoPlayer.Builder(context as Context) + .apply { + if ((httpConfig as HttpConfig).proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSourceFactory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) + } + } + } + ).apply { + repeatMode = Player.REPEAT_MODE_ALL + playWhenReady = autoPlay + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + setShowThumb(state != Player.STATE_READY) + } - actual var player: Any = Any() + override fun onIsPlayingChanged(isPlaying: Boolean) { + setPLaying(isPlaying) + } + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) + } + prepare() + seekTo(VideoPool.get(url)) + } + } private fun realPlayerView() = player as StyledPlayerView @@ -96,68 +154,3 @@ actual fun getContext(): Any { actual fun httpConfig(): Any { return LocalHttpConfig.current } - -actual fun getNativePlayerView( - url: String, - autoPlay: Boolean, - context: Any, - httpConfig: Any, - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, - setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit, -): NativePlayerView { - return NativePlayerView().apply { - this.player = StyledPlayerView(context as Context).also { playerView -> - (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) - playerView.useController = showControls - playerView.keepScreenOn = keepScreenOn - }.apply { - player = RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context as Context) - .apply { - if ((httpConfig as HttpConfig).proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } - } - } - ).apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - setShowThumb(state != Player.STATE_READY) - } - - override fun onIsPlayingChanged(isPlaying: Boolean) { - setPLaying(isPlaying) - } - }) - - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) - } - prepare() - seekTo(VideoPool.get(url)) - } - } - } -} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index fb5bc3612..41f671b50 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -32,7 +32,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -62,7 +61,7 @@ fun VideoPlayer( modifier: Modifier = Modifier, url: String, volume: Float = 1f, - customControl: Any? = null, + customControl: @Composable ((NativePlayerView) -> Unit)? = null, showControls: Boolean = customControl == null, zOrderMediaOverlay: Boolean = false, keepScreenOn: Boolean = false, @@ -80,7 +79,7 @@ fun VideoPlayer( Box { if (playInitial) { val nativePlayerView = remember(url) { - getNativePlayerView( + NativePlayerView( url = url, autoPlay = autoPlay, httpConfig = httpConfig, @@ -104,9 +103,6 @@ fun VideoPlayer( VideoPool.set(url, 0L.coerceAtLeast(nativePlayerView.contentPosition())) } - LaunchedEffect(customControl) { - // nativePlayerView.setCustomControl(customControl) - } var isResume by remember { mutableStateOf(true) } @@ -189,6 +185,7 @@ fun VideoPlayer( it.pause() } } + customControl?.invoke(nativePlayerView) } } @@ -221,18 +218,7 @@ internal fun getPlayInitial() = when (LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Off -> false } -expect class NativePlayerView { - var player: Any - var playWhenReady: Boolean - fun resume() - fun pause() - fun contentPosition(): Long - fun update() - fun setVolume(volume: Float) - fun release() -} - -expect fun getNativePlayerView( +expect class NativePlayerView( url: String, autoPlay: Boolean, context: Any, @@ -242,7 +228,16 @@ expect fun getNativePlayerView( keepScreenOn: Boolean, setShowThumb: (Boolean) -> Unit, setPLaying: (Boolean) -> Unit -): NativePlayerView +) { + var player: Any + var playWhenReady: Boolean + fun resume() + fun pause() + fun contentPosition(): Long + fun update() + fun setVolume(volume: Float) + fun release() +} @Composable expect fun PlatformView( diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 124aa6099..7383dc470 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -29,9 +29,29 @@ import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent import uk.co.caprica.vlcj.player.component.MediaPlayerComponent import java.util.Locale -actual class NativePlayerView { +actual class NativePlayerView actual constructor( + url: String, + autoPlay: Boolean, + context: Any, + httpConfig: Any, + zOrderMediaOverlay: Boolean, + showControls: Boolean, + keepScreenOn: Boolean, + setShowThumb: (Boolean) -> Unit, + setPLaying: (Boolean) -> Unit, +) { - actual var player: Any = Any() + actual var player: Any = ( + if (isMacOS()) { + CallbackMediaPlayerComponent() + } else { + EmbeddedMediaPlayerComponent() + } + ).apply { + mediaPlayer().apply { + media().prepare(url) + } + } actual var playWhenReady: Boolean = false @@ -58,32 +78,6 @@ actual class NativePlayerView { } } -actual fun getNativePlayerView( - url: String, - autoPlay: Boolean, - context: Any, - httpConfig: Any, - zOrderMediaOverlay: Boolean, - showControls: Boolean, - keepScreenOn: Boolean, - setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit, -): NativePlayerView { - return NativePlayerView().apply { - player = ( - if (isMacOS()) { - CallbackMediaPlayerComponent() - } else { - EmbeddedMediaPlayerComponent() - } - ).apply { - mediaPlayer().apply { - media().prepare(url) - } - } - } -} - @Composable actual fun PlatformView( modifier: Modifier, From 4314015bdd843ba7db0694c52a9e64106444bde8 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 20 Sep 2021 21:57:58 +0800 Subject: [PATCH 218/615] fix build and format code --- .../foundation/AndroidVideoPlayer.kt | 4 ++-- .../foundation/DesktopVideoPlayer.kt | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index b70f21321..307273563 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -75,7 +75,7 @@ actual class NativePlayerView actual constructor( playerView.keepScreenOn = keepScreenOn }.apply { player = RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context as Context) + SimpleExoPlayer.Builder(context) .apply { if ((httpConfig as HttpConfig).proxyConfig.enable) { // replace DataSource @@ -132,7 +132,7 @@ actual class NativePlayerView actual constructor( realPlayerView().onPause() } - actual fun contentPosition(): Long = 0L + actual fun contentPosition(): Long = realPlayerView().player?.contentPosition?:0 actual fun update() { } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 7383dc470..83faa0ed6 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -23,10 +23,11 @@ package com.twidere.twiderex.component.foundation import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel +import com.twidere.twiderex.utils.video.VideoPool import uk.co.caprica.vlcj.player.base.MediaPlayer +import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent -import uk.co.caprica.vlcj.player.component.MediaPlayerComponent import java.util.Locale actual class NativePlayerView actual constructor( @@ -49,23 +50,27 @@ actual class NativePlayerView actual constructor( } ).apply { mediaPlayer().apply { + events().addMediaPlayerEventListener(object : MediaPlayerEventAdapter() { + override fun opening(mediaPlayer: MediaPlayer?) { + super.opening(mediaPlayer) + mediaPlayer?.controls()?.skipTime(VideoPool.get(url)) + } + }) media().prepare(url) } } actual var playWhenReady: Boolean = false - private fun realPlayerView() = player as? MediaPlayerComponent - actual fun resume() { - realPlayerView()?.mediaPlayer()?.controls()?.play() + player.mediaPlayer().controls()?.play() } actual fun pause() { - realPlayerView()?.mediaPlayer()?.controls()?.pause() + player.mediaPlayer().controls()?.pause() } - actual fun contentPosition(): Long = 0L + actual fun contentPosition(): Long = player.mediaPlayer().status().time() actual fun update() { } @@ -74,7 +79,7 @@ actual class NativePlayerView actual constructor( } actual fun release() { - realPlayerView()?.mediaPlayer()?.release() + player.mediaPlayer().release() } } From 5638cf280903d55dd2879295e19b2b7653dcf928 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 20 Sep 2021 22:36:27 +0800 Subject: [PATCH 219/615] remove old VideoPlayer --- .../component/foundation/VideoPlayer.kt | 215 ------------------ .../com/twidere/twiderex/scenes/MediaScene.kt | 25 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 26 +-- .../utils/video/CacheDataSourceFactory.kt | 63 ----- .../twiderex/utils/video/VideoCache.kt | 38 ---- .../twidere/twiderex/utils/video/VideoPool.kt | 35 --- .../foundation/AndroidVideoPlayer.kt | 2 +- .../component/foundation/VideoPlayer.kt | 4 +- 8 files changed, 30 insertions(+), 378 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt deleted file mode 100644 index a75881778..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.component.foundation - -import android.view.SurfaceView -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon -import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.SimpleExoPlayer -import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource -import com.google.android.exoplayer2.source.DefaultMediaSourceFactory -import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.ui.PlayerControlView -import com.google.android.exoplayer2.ui.StyledPlayerView -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory -import com.twidere.twiderex.R -import com.twidere.twiderex.component.status.UserAvatarDefaults -import com.twidere.twiderex.http.TwidereServiceFactory -import com.twidere.twiderex.preferences.LocalHttpConfig -import com.twidere.twiderex.preferences.model.DisplayPreferences -import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered -import com.twidere.twiderex.ui.LocalVideoPlayback -import com.twidere.twiderex.utils.video.CacheDataSourceFactory -import com.twidere.twiderex.utils.video.VideoPool - -@Composable -fun VideoPlayer( - modifier: Modifier = Modifier, - url: String, - volume: Float = 1f, - customControl: PlayerControlView? = null, - showControls: Boolean = customControl == null, - zOrderMediaOverlay: Boolean = false, - keepScreenOn: Boolean = false, - thumb: @Composable (() -> Unit)? = null, -) { - var playing by remember { mutableStateOf(false) } - val playBackMode = LocalVideoPlayback.current - val isActiveNetworkMetered = LocalIsActiveNetworkMetered.current - var shouldShowThumb by remember { mutableStateOf(false) } - val playInitial = when (playBackMode) { - DisplayPreferences.AutoPlayback.Auto -> !isActiveNetworkMetered - DisplayPreferences.AutoPlayback.Always -> true - DisplayPreferences.AutoPlayback.Off -> false - } - var autoPlay by remember(url) { mutableStateOf(playInitial) } - val context = LocalContext.current - val lifecycle = LocalLifecycleOwner.current.lifecycle - val httpConfig = LocalHttpConfig.current - - Box { - if (playInitial) { - val player = remember(url) { - SimpleExoPlayer.Builder(context) - .apply { - if (httpConfig.proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } - } - } - .build().apply { - repeatMode = Player.REPEAT_MODE_ALL - playWhenReady = autoPlay - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - shouldShowThumb = state != Player.STATE_READY - } - - override fun onIsPlayingChanged(isPlaying: Boolean) { - playing = isPlaying - } - }) - - setVolume(volume) - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) - } - prepare() - seekTo(VideoPool.get(url)) - } - } - - fun updateState() { - autoPlay = player.playWhenReady - VideoPool.set(url, 0L.coerceAtLeast(player.contentPosition)) - } - - LaunchedEffect(customControl) { - if (customControl != null) { - customControl.player = player - } - } - var isResume by remember { - mutableStateOf(true) - } - DisposableEffect(Unit) { - val observer = object : LifecycleObserver { - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onResume() { - isResume = true - player.playWhenReady = autoPlay - } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun onPause() { - isResume = false - updateState() - player.playWhenReady = false - } - } - lifecycle.addObserver(observer) - onDispose { - updateState() - player.release() - lifecycle.removeObserver(observer) - } - } - - AndroidView( - modifier = modifier, - factory = { context -> - StyledPlayerView(context).also { playerView -> - (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) - playerView.useController = showControls - playerView.keepScreenOn = keepScreenOn - } - } - ) { - it.player = player - if (isResume) { - it.onResume() - } else { - it.onPause() - } - } - } - if ((shouldShowThumb || !playing) && thumb != null) { - thumb() - Box( - modifier = Modifier - .fillMaxSize() - ) { - Icon( - imageVector = Icons.Default.PlayArrow, - tint = Color.White.copy(alpha = LocalContentAlpha.current), - modifier = Modifier - .align(Alignment.Center) - .size(UserAvatarDefaults.AvatarSize) - .background(MaterialTheme.colors.primary, CircleShape), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_video_play) - ) - } - } - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 9ba4eeda9..f25ebce75 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -81,6 +81,7 @@ import com.mxalbert.zoomable.Zoomable import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress +import com.twidere.twiderex.component.foundation.NativePlayerView import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer import com.twidere.twiderex.component.status.LikeButton @@ -162,16 +163,18 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM pageCount = status.media.size, ) val currentMedia = status.media[pagerState.currentPage] - val context = LocalContext.current - val videoControl = remember(pagerState.currentPage) { - if (currentMedia.type == MediaType.video) { - PlayerControlView(context).apply { - showTimeoutMs = 0 - } - } else { - null - } - } + // val context = LocalContext.current + // todo use redefine custom control view by compose + val videoControl = null + // remember(pagerState.currentPage) { + // if (currentMedia.type == MediaType.video) { + // PlayerControlView(context).apply { + // showTimeoutMs = 0 + // } + // } else { + // null + // } + // } val swiperState = rememberSwiperState( onDismiss = { navController.popBackStack() @@ -407,7 +410,7 @@ fun MediaView( initialPage = 0, pageCount = media.size, ), - customControl: PlayerControlView? = null, + customControl: @Composable ((NativePlayerView) -> Unit)? = null, ) { Box( modifier = Modifier diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index a94e1facc..4d40f3d8d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -49,7 +49,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -67,7 +66,6 @@ import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.setOnSystemBarsVisibilityChangeListener import com.twidere.twiderex.extensions.showControls import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback @@ -101,17 +99,19 @@ fun PureMediaScene(belongToKey: MicroBlogKey, selectedIndex: Int) { initialPage = selectedIndex, pageCount = medias.size, ) - val currentMedia = medias[pagerState.currentPage] - val context = LocalContext.current - val videoControl = remember(pagerState.currentPage) { - if (currentMedia.type == MediaType.video) { - PlayerControlView(context).apply { - showTimeoutMs = 0 - } - } else { - null - } - } + // val currentMedia = medias[pagerState.currentPage] + // val context = LocalContext.current + // todo use redefine custom control view by compose + val videoControl = null + // remember(pagerState.currentPage) { + // if (currentMedia.type == MediaType.video) { + // PlayerControlView(context).apply { + // showTimeoutMs = 0 + // } + // } else { + // null + // } + // } val swiperState = rememberSwiperState( onDismiss = { navController.popBackStack() diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt deleted file mode 100644 index b4684bba2..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils.video - -import android.content.Context -import com.google.android.exoplayer2.upstream.DataSource -import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource -import com.google.android.exoplayer2.upstream.FileDataSource -import com.google.android.exoplayer2.upstream.cache.CacheDataSink -import com.google.android.exoplayer2.upstream.cache.CacheDataSource -import com.google.android.exoplayer2.upstream.cache.SimpleCache -import com.google.android.exoplayer2.util.Util -import com.twidere.twiderex.R - -class CacheDataSourceFactory( - private val context: Context, - private val maxFileSize: Long, -) : DataSource.Factory { - private val simpleCache: SimpleCache by lazy { - VideoCache.getInstance(context) - } - - private val defaultDatasourceFactory: DefaultDataSourceFactory - override fun createDataSource(): DataSource { - return CacheDataSource( - simpleCache, defaultDatasourceFactory.createDataSource(), - FileDataSource(), CacheDataSink(simpleCache, maxFileSize), - CacheDataSource.FLAG_BLOCK_ON_CACHE or CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR, null - ) - } - - init { - val userAgent = Util.getUserAgent(context, context.getString(R.string.app_name)) - val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build() - defaultDatasourceFactory = DefaultDataSourceFactory( - this.context, - bandwidthMeter, - DefaultHttpDataSource.Factory() - .setUserAgent(userAgent) - .setTransferListener(bandwidthMeter) - ) - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt deleted file mode 100644 index 3917e7f87..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils.video - -import android.content.Context -import com.google.android.exoplayer2.database.ExoDatabaseProvider -import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor -import com.google.android.exoplayer2.upstream.cache.SimpleCache -import java.io.File - -object VideoCache { - private var simpleCache: SimpleCache? = null - private const val maxCacheSize: Long = 100 * 1024 * 1024 - fun getInstance(context: Context): SimpleCache { - val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize) - if (simpleCache == null) simpleCache = - SimpleCache(File(context.cacheDir, "media"), evictor, ExoDatabaseProvider(context)) - return simpleCache as SimpleCache - } -} diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt deleted file mode 100644 index e919d7e37..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils.video - -import java.util.concurrent.ConcurrentHashMap - -object VideoPool { - private val pool = ConcurrentHashMap() - - fun get(url: String): Long { - return pool[url] ?: 1L - } - - fun set(url: String, position: Long) { - pool[url] = position - } -} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 307273563..9366d7760 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -132,7 +132,7 @@ actual class NativePlayerView actual constructor( realPlayerView().onPause() } - actual fun contentPosition(): Long = realPlayerView().player?.contentPosition?:0 + actual fun contentPosition(): Long = realPlayerView().player?.contentPosition ?: 0 actual fun update() { } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 41f671b50..7a3cc4421 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -175,12 +175,12 @@ fun VideoPlayer( ) { if (isResume && isMostCenter) { if (isListItem) { - nativePlayerView.playWhenReady = autoPlay + it.playWhenReady = autoPlay } it.resume() } else { if (isListItem) { - nativePlayerView.playWhenReady = false + it.playWhenReady = false } it.pause() } From 5f273ff22d0f5b293bdce42cb8247f6a8a04e9ee Mon Sep 17 00:00:00 2001 From: huixing Date: Tue, 21 Sep 2021 11:15:25 +0800 Subject: [PATCH 220/615] compose seekBar --- .../twiderex/component/foundation/SeekBar.kt | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt new file mode 100644 index 000000000..d30edb514 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt @@ -0,0 +1,197 @@ +package com.twidere.twiderex.component.foundation + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.gestures.rememberDraggableState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PointMode +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import java.text.SimpleDateFormat +import java.util.Locale + +@Stable +class SeekBarState( + private val duration: Long, + initPosition: Long = 0L, + private val stepFreq: Int = 1000, + private val onDragEnd: (position: Long) -> Unit +) { + + var dragging by mutableStateOf(false) + + var curTime by mutableStateOf(0L) + + var intSize by mutableStateOf(Size.Zero) + + var timePosition by mutableStateOf(initPosition) + + var progressPosition by mutableStateOf(0f) + + fun setSize(size: Size) { + intSize = size + } + + fun start() { + dragging = true + update() + } + + fun onDragChange(delta: Float) { + if ( + delta >= -progressPosition && + delta <= intSize.width - progressPosition + ) { + progressPosition += delta + update() + } + } + + fun onTimeChange(timePosition: Long) { + this.timePosition = timePosition.coerceAtMost(duration) + update() + } + + fun end() { + curTime = calDragPos() + onDragEnd(curTime) + dragging = false + timePosition = curTime + update() + } + + fun update() { + if (!dragging) + curTime = timePosition + + if (!dragging) + progressPosition = calProgress() + } + + fun progressTimeText( + pattern: String = "mm:ss" + ): String = SimpleDateFormat( + pattern, + Locale.getDefault() + ).format( + if (!dragging) + curTime + else calDragPos() + ) + + fun fullTimeText( + pattern: String = "mm:ss" + ): String = SimpleDateFormat( + pattern, Locale.getDefault() + ).format( + duration + ) + + private fun calDragPos(): Long { + val percent = ( + stepFreq * progressPosition / intSize.width + ).toInt() + return duration * percent / stepFreq + } + + private fun calProgress(): Float { + val percent = if (duration != 0L) + curTime * stepFreq / duration + else 0 + return (intSize.width * percent / stepFreq) + } +} + +@Composable +fun SeekBar( + modifier: Modifier = Modifier, + pointColor: Color = Color.White, + progressLineColor: Color = Color.White, + backgroundLineColor: Color = Color.Gray.copy(alpha = 0.4f), + strokeWidth: Dp = 8.dp, + showText: Boolean = true, + state: SeekBarState, +) { + Row( + modifier = modifier + .fillMaxWidth() + ) { + if (showText) { + Text( + text = state.progressTimeText(), + modifier = Modifier + .padding(horizontal = 16.dp) + ) + } + + Box( + modifier = Modifier + .height(20.dp).weight(1f) + .draggable( + rememberDraggableState { delta -> + state.onDragChange(delta) + }, orientation = Orientation.Horizontal, + onDragStarted = { + state.start() + }, onDragStopped = { + state.end() + } + ) + ) { + Canvas( + modifier = Modifier.matchParentSize() + ) { + state.setSize(size) + drawLine( + color = backgroundLineColor, + start = Offset(x = 0f, y = (state.intSize.height / 2)), + end = Offset(x = state.intSize.width, y = (state.intSize.height / 2)), + strokeWidth = strokeWidth.value, + cap = StrokeCap.Round + ) + + drawLine( + color = progressLineColor, + start = Offset(x = 0f, y = (state.intSize.height / 2)), + end = Offset(x = state.progressPosition, y = (state.intSize.height / 2)), + strokeWidth = strokeWidth.value, + cap = StrokeCap.Round + ) + + drawPoints( + points = listOf(Offset(state.progressPosition, center.y)), + pointMode = PointMode.Points, + color = pointColor, + strokeWidth = (strokeWidth * 2f).toPx(), + cap = StrokeCap.Round + ) + } + } + + if (showText) { + Text( + text = state.fullTimeText(), + modifier = Modifier + .padding( + horizontal = 16.dp + ) + ) + } + } +} \ No newline at end of file From 73ce9875611922f3776a90fe52e8c28e9c193091 Mon Sep 17 00:00:00 2001 From: huixing Date: Tue, 21 Sep 2021 14:37:29 +0800 Subject: [PATCH 221/615] add custom video controller[wip] --- .../foundation/AndroidVideoPlayer.kt | 12 ++-- .../twiderex/component/foundation/SeekBar.kt | 28 ++++++++- .../component/foundation/VideoPlayer.kt | 43 ++++++++++--- .../utils/video/CustomVideoControl.kt | 62 +++++++++++++++++++ .../foundation/DesktopVideoPlayer.kt | 15 ++++- 5 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 9366d7760..c2e133b4c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -66,8 +66,7 @@ actual class NativePlayerView actual constructor( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit, + playerCallBack: PlayerCallBack? ) { actual var player: Any = StyledPlayerView(context as Context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) @@ -99,11 +98,11 @@ actual class NativePlayerView actual constructor( playWhenReady = autoPlay addListener(object : Player.Listener { override fun onPlaybackStateChanged(state: Int) { - setShowThumb(state != Player.STATE_READY) + playerCallBack?.showThumb(state != Player.STATE_READY) } override fun onIsPlayingChanged(isPlaying: Boolean) { - setPLaying(isPlaying) + playerCallBack?.setPlaying(isPlaying) } }) @@ -143,6 +142,11 @@ actual class NativePlayerView actual constructor( actual fun release() { realPlayerView().player?.release() } + + actual fun duration(): Long = realPlayerView().player?.duration ?: 0 + actual fun seekTo(time: Long) { + realPlayerView().player?.seekTo(time) + } } @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt index d30edb514..4d2226230 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SeekBar.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.Canvas @@ -146,10 +166,12 @@ fun SeekBar( .draggable( rememberDraggableState { delta -> state.onDragChange(delta) - }, orientation = Orientation.Horizontal, + }, + orientation = Orientation.Horizontal, onDragStarted = { state.start() - }, onDragStopped = { + }, + onDragStopped = { state.end() } ) @@ -194,4 +216,4 @@ fun SeekBar( ) } } -} \ No newline at end of file +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 7a3cc4421..5c2a0458a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -22,9 +22,12 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.LocalContentAlpha import androidx.compose.material.MaterialTheme @@ -76,8 +79,10 @@ fun VideoPlayer( val resLoder = LocalResLoader.current val context = getContext() val httpConfig = httpConfig() + var mediaPrepared by remember { mutableStateOf(false) } Box { if (playInitial) { + val nativePlayerView = remember(url) { NativePlayerView( url = url, @@ -87,11 +92,18 @@ fun VideoPlayer( zOrderMediaOverlay = zOrderMediaOverlay, showControls = showControls, keepScreenOn = keepScreenOn, - setShowThumb = { - shouldShowThumb = it - }, - setPLaying = { - playing = it + playerCallBack = object : PlayerCallBack { + override fun showThumb(showThunb: Boolean) { + shouldShowThumb = showThunb + } + + override fun setPlaying(isPlaying: Boolean) { + playing = isPlaying + } + + override fun onprepare() { + mediaPrepared = true + } } ) } @@ -122,7 +134,8 @@ fun VideoPlayer( updateState() nativePlayerView.playWhenReady = false } - else -> {} + else -> { + } } } } @@ -142,7 +155,7 @@ fun VideoPlayer( mutableStateOf(false) } var debounceJob: Job? = null - Box( + Column( modifier = Modifier.onGloballyPositioned { coordinates -> if (middleLine == 0.0f) { var rootCoordinates = coordinates @@ -185,7 +198,10 @@ fun VideoPlayer( it.pause() } } - customControl?.invoke(nativePlayerView) + if (mediaPrepared) { + Divider(Modifier.height(30.dp)) + customControl?.invoke(nativePlayerView) + } } } @@ -218,6 +234,12 @@ internal fun getPlayInitial() = when (LocalVideoPlayback.current) { DisplayPreferences.AutoPlayback.Off -> false } +interface PlayerCallBack { + fun showThumb(showThunb: Boolean) + fun setPlaying(isPlaying: Boolean) + fun onprepare() +} + expect class NativePlayerView( url: String, autoPlay: Boolean, @@ -226,14 +248,15 @@ expect class NativePlayerView( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit + playerCallBack: PlayerCallBack? = null ) { var player: Any var playWhenReady: Boolean fun resume() fun pause() fun contentPosition(): Long + fun duration(): Long + fun seekTo(time: Long) fun update() fun setVolume(volume: Float) fun release() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt new file mode 100644 index 000000000..6598aed95 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils.video + +import androidx.compose.foundation.layout.Row +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import com.twidere.twiderex.component.foundation.NativePlayerView +import com.twidere.twiderex.component.foundation.SeekBar +import com.twidere.twiderex.component.foundation.SeekBarState + +@Composable +fun CustomVideoControl( + player: NativePlayerView +) { + val seekBarState = remember { + SeekBarState( + duration = player.duration(), + initPosition = player.contentPosition() + ) { seekTime -> + player.seekTo(seekTime) + } + } + var playerState = true + Row { + Button( + onClick = { + if (playerState) { + player.resume() + } else { + player.pause() + } + playerState = !playerState + } + ) { + Text("play/pause") + } + SeekBar( + state = seekBarState + ) + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 83faa0ed6..a3f29433a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -38,8 +38,7 @@ actual class NativePlayerView actual constructor( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - setShowThumb: (Boolean) -> Unit, - setPLaying: (Boolean) -> Unit, + playerCallBack: PlayerCallBack? ) { actual var player: Any = ( @@ -55,6 +54,11 @@ actual class NativePlayerView actual constructor( super.opening(mediaPlayer) mediaPlayer?.controls()?.skipTime(VideoPool.get(url)) } + + override fun mediaPlayerReady(mediaPlayer: MediaPlayer?) { + super.mediaPlayerReady(mediaPlayer) + playerCallBack?.onprepare() + } }) media().prepare(url) } @@ -81,6 +85,13 @@ actual class NativePlayerView actual constructor( actual fun release() { player.mediaPlayer().release() } + + // only can get this value after prepare + actual fun duration(): Long = player.mediaPlayer().media().info().duration() + + actual fun seekTo(time: Long) { + player.mediaPlayer().controls().setTime(time) + } } @Composable From 640f8dc80146fd9d7e3d23b6a4f70217d96c0f0f Mon Sep 17 00:00:00 2001 From: huixing Date: Tue, 21 Sep 2021 16:16:11 +0800 Subject: [PATCH 222/615] show progress on seekBar when playing --- .../foundation/AndroidVideoPlayer.kt | 24 ++++++++++++++++++- .../component/foundation/VideoPlayer.kt | 10 ++++++-- .../utils/video/CustomVideoControl.kt | 8 ++++++- .../foundation/DesktopVideoPlayer.kt | 11 ++++++++- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index c2e133b4c..f202d9ca3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -40,6 +40,11 @@ import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig import com.twidere.twiderex.utils.video.CacheDataSourceFactory import com.twidere.twiderex.utils.video.VideoPool +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.coroutines.EmptyCoroutineContext @Composable actual fun PlatformView( @@ -66,8 +71,13 @@ actual class NativePlayerView actual constructor( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - playerCallBack: PlayerCallBack? ) { + actual var playerCallBack: PlayerCallBack? = null + + actual var playerProgressCallBack: PlayerProgressCallBack? = null + + private var job: Job? = null + actual var player: Any = StyledPlayerView(context as Context).also { playerView -> (playerView.videoSurfaceView as? SurfaceView)?.setZOrderMediaOverlay(zOrderMediaOverlay) playerView.useController = showControls @@ -103,6 +113,15 @@ actual class NativePlayerView actual constructor( override fun onIsPlayingChanged(isPlaying: Boolean) { playerCallBack?.setPlaying(isPlaying) + job?.cancel() + if (isPlaying) { + job = CoroutineScope(EmptyCoroutineContext).launch { + while (true) { + delay(1000) + playerProgressCallBack?.onTimeChanged(contentPosition) + } + } + } } }) @@ -140,6 +159,9 @@ actual class NativePlayerView actual constructor( } actual fun release() { + job?.cancel() + playerCallBack = null + playerProgressCallBack = null realPlayerView().player?.release() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 5c2a0458a..332a692ea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -92,6 +92,7 @@ fun VideoPlayer( zOrderMediaOverlay = zOrderMediaOverlay, showControls = showControls, keepScreenOn = keepScreenOn, + ).apply { playerCallBack = object : PlayerCallBack { override fun showThumb(showThunb: Boolean) { shouldShowThumb = showThunb @@ -105,7 +106,7 @@ fun VideoPlayer( mediaPrepared = true } } - ) + } } nativePlayerView.setVolume(volume) @@ -240,6 +241,10 @@ interface PlayerCallBack { fun onprepare() } +interface PlayerProgressCallBack { + fun onTimeChanged(time: Long) +} + expect class NativePlayerView( url: String, autoPlay: Boolean, @@ -248,10 +253,11 @@ expect class NativePlayerView( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - playerCallBack: PlayerCallBack? = null ) { var player: Any var playWhenReady: Boolean + var playerCallBack: PlayerCallBack? + var playerProgressCallBack: PlayerProgressCallBack? fun resume() fun pause() fun contentPosition(): Long diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt index 6598aed95..a66b2ea07 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt @@ -26,6 +26,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.twidere.twiderex.component.foundation.NativePlayerView +import com.twidere.twiderex.component.foundation.PlayerProgressCallBack import com.twidere.twiderex.component.foundation.SeekBar import com.twidere.twiderex.component.foundation.SeekBarState @@ -42,10 +43,15 @@ fun CustomVideoControl( } } var playerState = true + player.playerProgressCallBack = object : PlayerProgressCallBack { + override fun onTimeChanged(time: Long) { + seekBarState.onTimeChange(time) + } + } Row { Button( onClick = { - if (playerState) { + if (!playerState) { player.resume() } else { player.pause() diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index a3f29433a..5eac5bcbe 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -38,8 +38,10 @@ actual class NativePlayerView actual constructor( zOrderMediaOverlay: Boolean, showControls: Boolean, keepScreenOn: Boolean, - playerCallBack: PlayerCallBack? ) { + actual var playerCallBack: PlayerCallBack? = null + + actual var playerProgressCallBack: PlayerProgressCallBack? = null actual var player: Any = ( if (isMacOS()) { @@ -59,6 +61,11 @@ actual class NativePlayerView actual constructor( super.mediaPlayerReady(mediaPlayer) playerCallBack?.onprepare() } + + override fun timeChanged(mediaPlayer: MediaPlayer?, newTime: Long) { + super.timeChanged(mediaPlayer, newTime) + playerProgressCallBack?.onTimeChanged(newTime) + } }) media().prepare(url) } @@ -83,6 +90,8 @@ actual class NativePlayerView actual constructor( } actual fun release() { + playerCallBack = null + playerProgressCallBack = null player.mediaPlayer().release() } From 9521086c9daf2fad69722c12bea996ee917ba0e6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 22 Sep 2021 16:26:56 +0800 Subject: [PATCH 223/615] add user test --- .../twiderex/component/UserComponent.kt | 7 +- .../platform/MastodonListsCreateDialog.kt | 2 - .../lists/platform/TwitterListsCreateScene.kt | 2 - .../twiderex/repository/TimelineRepository.kt | 22 +++ .../viewmodel/user/FollowersViewModel.kt | 2 +- .../viewmodel/user/FollowingViewModel.kt | 2 +- .../user/UserFavouriteTimelineViewModel.kt | 2 +- .../user/UserMediaTimelineViewModel.kt | 52 ++----- .../viewmodel/user/UserTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/user/UserViewModel.kt | 40 ++--- .../viewmodel/ActiveAccountViewModelTest.kt | 24 ++- .../twiderex/viewmodel/DraftViewModelTest.kt | 30 +++- .../twiderex/viewmodel/MediaViewModelTest.kt | 2 - .../viewmodel/user/FollowersViewModelTest.kt | 73 ++++++++++ .../viewmodel/user/FollowingViewModelTest.kt | 73 ++++++++++ .../UserFavouriteTimelineViewModelTest.kt | 73 ++++++++++ .../user/UserMediaTimelineViewModelTest.kt | 74 ++++++++++ .../user/UserTimelineViewModelTest.kt | 101 +++++++++++++ .../user/UserViewModelRelationshipTest.kt | 76 ---------- .../viewmodel/user/UserViewModelTest.kt | 137 ++++++++++++++++++ 20 files changed, 639 insertions(+), 157 deletions(-) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt delete mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt index 4c4cd630e..5e7798609 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -65,7 +65,6 @@ import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -230,10 +229,10 @@ fun UserStatusTimeline( viewModel: UserViewModel, ) { val user by viewModel.user.observeAsState(initial = null) - var excludeReplies by rememberSaveable { mutableStateOf(false) } val timelineViewModel: UserTimelineViewModel = getViewModel { - parametersOf(userKey, excludeReplies) + parametersOf(userKey) } + val excludeReplies by timelineViewModel.excludeReplies.observeAsState(initial = false) val timelineSource = timelineViewModel.source.collectAsLazyPagingItems() // FIXME: 2021/2/20 Recover the scroll position require visiting the loadState once, have no idea why @Suppress("UNUSED_VARIABLE") @@ -244,7 +243,7 @@ fun UserStatusTimeline( user?.let { user -> item { UserStatusTimelineFilter(user, excludeReplies) { - excludeReplies = it + timelineViewModel.setExcludeReplies(it) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index d30035ba3..9c0894ed1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -33,12 +33,10 @@ import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel import kotlinx.coroutines.launch -import org.koin.core.parameter.parametersOf @Composable fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index 61d55a628..c0bf16a32 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -46,7 +46,6 @@ import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.TwitterListsModifyComponent import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -54,7 +53,6 @@ import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel import kotlinx.coroutines.launch import moe.tlaster.precompose.navigation.NavOptions import moe.tlaster.precompose.navigation.PopUpTo -import org.koin.core.parameter.parametersOf @Composable fun TwitterListsCreateScene() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt index d891f1f39..91d879c5a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt @@ -22,19 +22,23 @@ package com.twidere.twiderex.repository import androidx.paging.ExperimentalPagingApi import androidx.paging.PagingData +import androidx.paging.flatMap import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.TimelineService import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.list.ListsTimelineMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.paging.toUi import com.twidere.twiderex.paging.mediator.timeline.MastodonHashtagTimelineMediator import com.twidere.twiderex.paging.mediator.user.UserFavouriteMediator +import com.twidere.twiderex.paging.mediator.user.UserMediaMediator import com.twidere.twiderex.paging.mediator.user.UserStatusMediator import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @OptIn(ExperimentalPagingApi::class) class TimelineRepository( @@ -97,4 +101,22 @@ class TimelineRepository( ) return mediator.pager().toUi() } + + fun mediaTimeline( + userKey: MicroBlogKey, + accountKey: MicroBlogKey, + service: TimelineService + ): Flow>> { + val mediator = UserMediaMediator( + userKey = userKey, + database = database, + accountKey = accountKey, + service = service + ) + return mediator.pager().toUi().map { + it.flatMap { + it.media.map { media -> media to it } + } + } + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index ac935158e..cab800ab3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -32,8 +32,8 @@ import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class FollowersViewModel( - private val accountRepository: AccountRepository, private val repository: UserListRepository, + private val accountRepository: AccountRepository, private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index a6d5c1b8b..be2299726 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -32,8 +32,8 @@ import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope class FollowingViewModel( - private val accountRepository: AccountRepository, private val repository: UserListRepository, + private val accountRepository: AccountRepository, private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 54aa89692..95535e01f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -43,7 +43,7 @@ class UserFavouriteTimelineViewModel( @OptIn(ExperimentalCoroutinesApi::class) val source by lazy { - account.mapNotNull { it }.flatMapLatest { + account.flatMapLatest { repository.favouriteTimeline( userKey = userKey, accountKey = it.accountKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index ad9b7a343..aefaa9469 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -20,69 +20,39 @@ */ package com.twidere.twiderex.viewmodel.user -import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn -import androidx.paging.flatMap -import androidx.paging.map import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.db.CacheDatabase import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.paging.mediator.paging.pager -import com.twidere.twiderex.paging.mediator.user.UserMediaMediator import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class UserMediaTimelineViewModel( - private val database: CacheDatabase, - private val repository: AccountRepository, + private val repository: TimelineRepository, + private val accountRepository: AccountRepository, private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - repository.activeAccount.asStateIn(viewModelScope, null) + accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) val source: Flow>> by lazy { - pagingMediator.mapNotNull { it } - .flatMapLatest { pagingMediator -> - pagingMediator.pager( - config = PagingConfig( - pageSize = 200, - prefetchDistance = 4, - enablePlaceholders = false, - ) - ).flow.map { pagingData -> - pagingData.map { - it.status - } - }.cachedIn(viewModelScope).map { - it.flatMap { - it.media.map { media -> media to it } - } - } - }.cachedIn(viewModelScope) - } - - val pagingMediator by lazy { - account.map { - it?.let { - UserMediaMediator( - userKey = userKey, - database = database, - accountKey = it.accountKey, - service = it.service as TimelineService - ) - } - }.asStateIn(viewModelScope, null) + account.flatMapLatest { + repository.mediaTimeline( + userKey, + it.accountKey, + it.service as TimelineService + ) + }.cachedIn(viewModelScope) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 8f7e20e60..af7858ce1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -51,7 +51,7 @@ class UserTimelineViewModel( @OptIn(FlowPreview::class) val source by lazy { - combine(account.mapNotNull { it }, _excludeReplies) { account, excludeReplies -> + combine(account, _excludeReplies) { account, excludeReplies -> repository.userTimeline( userKey = userKey, accountKey = account.accountKey, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index ed1471663..b08d19cba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -32,8 +32,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -53,27 +53,27 @@ class UserViewModel( val refreshing = MutableStateFlow(false) val loadingRelationship = MutableStateFlow(false) val user = repository.getUserFlow(userKey) - val relationship = combine(account.mapNotNull { it }, refreshFlow) { account, _ -> - loadingRelationship.value = true + val relationship = combine(account, refreshFlow) { account, _ -> + loadingRelationship.compareAndSet(expect = false, update = true) val relationshipService = account.service as RelationshipService try { relationshipService.showRelationship(userKey.id) } catch (e: Throwable) { null } finally { - loadingRelationship.value = false + loadingRelationship.compareAndSet(expect = true, update = false) } } @OptIn(ExperimentalCoroutinesApi::class) val isMe by lazy { - account.transformLatest { - emit(it.accountKey == userKey) - }.asStateIn(viewModelScope, false) + account.mapLatest { + it.accountKey == userKey + } } private fun collectUser() = viewModelScope.launch { - combine(account.mapNotNull { it }, refreshFlow) { account, _ -> + combine(account, refreshFlow) { account, _ -> refreshing.value = true runCatching { repository.lookupUserById( @@ -89,30 +89,30 @@ class UserViewModel( } fun follow() = viewModelScope.launch { - loadingRelationship.value = true + loadingRelationship.compareAndSet(expect = false, update = true) val account = account.firstOrNull() ?: return@launch val relationshipService = account.service as? RelationshipService ?: return@launch - runCatching { + try { relationshipService.follow(userKey.id) - }.onSuccess { refresh() - }.onFailure { - loadingRelationship.value = false - inAppNotification.notifyError(it) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + } finally { + loadingRelationship.compareAndSet(expect = true, update = false) } } fun unfollow() = viewModelScope.launch { - loadingRelationship.value = true + loadingRelationship.compareAndSet(expect = false, update = true) val account = account.firstOrNull() ?: return@launch val relationshipService = account.service as? RelationshipService ?: return@launch - runCatching { + try { relationshipService.unfollow(userKey.id) - }.onSuccess { refresh() - }.onFailure { - loadingRelationship.value = false - inAppNotification.notifyError(it) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + } finally { + loadingRelationship.compareAndSet(expect = true, update = false) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt index bfad82f1d..43002ca8a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.model.enums.PlatformType @@ -23,7 +43,7 @@ internal class ActiveAccountViewModelTest : ViewModelTestBase() { } @Test - fun set_activeAccount() { + fun set_activeAccount() { viewModel.setActiveAccount(mockk()) verify(exactly = 1) { repository.setCurrentAccount(any()) } } @@ -49,4 +69,4 @@ internal class ActiveAccountViewModelTest : ViewModelTestBase() { verify(exactly = 1) { repository.getFirstByType(PlatformType.Fanfou) } } } -} \ No newline at end of file +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt index 2595c98a7..6941957f7 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.action.DraftAction @@ -50,9 +70,11 @@ internal class DraftViewModelTest : ViewModelTestBase() { @Test fun delete_draft() = runBlocking { - viewModel.delete(mockk{ - every { draftId }.returns("123") - }) + viewModel.delete( + mockk { + every { draftId }.returns("123") + } + ) verify(exactly = 1) { action.delete("123") } } -} \ No newline at end of file +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt index 77935f48c..8b22e9213 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt @@ -27,7 +27,6 @@ import com.twidere.twiderex.repository.StatusRepository import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.justRun import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.flow.firstOrNull @@ -62,7 +61,6 @@ internal class MediaViewModelTest : AccountViewModelTestBase() { } ) ) - justRun { mediaAction.download(any(), any(), any()) } } @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt new file mode 100644 index 000000000..f3a11d361 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.UserListRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class FollowersViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockRelationshipService() + + @MockK + private lateinit var repository: UserListRepository + + private lateinit var viewModel: FollowersViewModel + + override fun setUp() { + super.setUp() + every { repository.followers(any(), any()) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk() + } + ) + ) + ) + viewModel = FollowersViewModel( + repository, + mockAccountRepository, + MicroBlogKey.twitter("321") + ) + } + + @Test + fun source_any(): Unit = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assert(it.collectDataForTest().any()) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt new file mode 100644 index 000000000..abe259c70 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.UserListRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class FollowingViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockRelationshipService() + + @MockK + private lateinit var repository: UserListRepository + + private lateinit var viewModel: FollowingViewModel + + override fun setUp() { + super.setUp() + every { repository.following(any(), any()) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk() + } + ) + ) + ) + viewModel = FollowingViewModel( + repository, + mockAccountRepository, + MicroBlogKey.twitter("321") + ) + } + + @Test + fun source_any(): Unit = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assert(it.collectDataForTest().any()) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt new file mode 100644 index 000000000..ea15d1dbc --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt @@ -0,0 +1,73 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.TimelineRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class UserFavouriteTimelineViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockTimelineService() + + @MockK + private lateinit var repository: TimelineRepository + + private lateinit var viewModel: UserFavouriteTimelineViewModel + + override fun setUp() { + super.setUp() + every { repository.favouriteTimeline(any(), any(), any(), any()) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk() + } + ) + ) + ) + viewModel = UserFavouriteTimelineViewModel( + repository, + mockAccountRepository, + MicroBlogKey.twitter("321") + ) + } + + @Test + fun source_any(): Unit = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assert(it.collectDataForTest().any()) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt new file mode 100644 index 000000000..4d87e75fe --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt @@ -0,0 +1,74 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.repository.TimelineRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class UserMediaTimelineViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockTimelineService() + + @MockK + private lateinit var repository: TimelineRepository + + private lateinit var viewModel: UserMediaTimelineViewModel + + override fun setUp() { + super.setUp() + every { repository.mediaTimeline(any(), any(), any()) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk() to mockk() + } + ) + ) + ) + viewModel = UserMediaTimelineViewModel( + repository, + mockAccountRepository, + MicroBlogKey.twitter("321") + ) + } + + @Test + fun source_any(): Unit = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + assert(it.collectDataForTest().any()) + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt new file mode 100644 index 000000000..7680b461f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt @@ -0,0 +1,101 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import androidx.paging.PagingData +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.paging.collectDataForTest +import com.twidere.twiderex.mock.service.MockTimelineService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.TimelineRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertNotNull + +internal class UserTimelineViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockTimelineService() + + @MockK + private lateinit var repository: TimelineRepository + + private lateinit var viewModel: UserTimelineViewModel + + override fun setUp() { + super.setUp() + every { repository.userTimeline(any(), any(), any(), true) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk { + every { statusId }.returns(it.toString()) + every { inReplyToStatusId }.returns(null) + } + } + ) + ) + ) + every { repository.userTimeline(any(), any(), any(), false) }.returns( + flowOf( + PagingData.from( + (0..3).map { + mockk { + every { statusId }.returns(it.toString()) + every { inReplyToStatusId }.returns(it.toString()) + } + } + ) + ) + ) + viewModel = UserTimelineViewModel( + repository, + mockAccountRepository, + MicroBlogKey.twitter("321") + ) + } + + @Test + fun source_any(): Unit = runBlocking { + viewModel.source.firstOrNull().let { + assertNotNull(it) + it.collectDataForTest().let { + assert(it.any()) + } + } + } + + @Test + fun exclude_replies(): Unit = runBlocking { + viewModel.setExcludeReplies(true) + viewModel.source.firstOrNull().let { + assertNotNull(it) + it.collectDataForTest().let { + assert(it.all { it.inReplyToStatusId.isNullOrEmpty() }) + } + } + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt deleted file mode 100644 index 54a0e170f..000000000 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelRelationshipTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.twidere.twiderex.viewmodel.user - -import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.mock.service.MockRelationshipService -import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.notification.InAppNotification -import com.twidere.twiderex.repository.UserRepository -import com.twidere.twiderex.viewmodel.AccountViewModelTestBase -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking -import org.junit.Test -import kotlin.test.assertNotNull - -internal class UserViewModelRelationshipTest : AccountViewModelTestBase() { - override val mockService: MicroBlogService - get() = MockRelationshipService() - - @MockK(relaxed = true) - private lateinit var repository: UserRepository - - @MockK - private lateinit var inAppNotification: InAppNotification - - override fun setUp() { - super.setUp() - } - - @Test - fun is_me() = runBlocking { - every { repository.getUserFlow(any()) }.returns(flowOf(mockk { - every { userKey }.returns(mockAccount.accountKey) - })) - val viewModel = UserViewModel( - repository, - mockAccountRepository, - inAppNotification, - mockAccount.accountKey - ) - viewModel.isMe.firstOrNull().let { - assertNotNull(it) - assert(it) - } - } - - @Test - fun is_not_me() = runBlocking { - every { repository.getUserFlow(any()) }.returns(flowOf(mockk { - every { userKey }.returns(MicroBlogKey.twitter("321")) - })) - val viewModel = UserViewModel( - repository, - mockAccountRepository, - inAppNotification, - MicroBlogKey.twitter("321") - ) - viewModel.isMe.firstOrNull().let { - assertNotNull(it) - assert(!it) - } - } - - @Test - fun follow_success() = runBlocking { - val viewModel = UserViewModel( - repository, - mockAccountRepository, - inAppNotification, - MicroBlogKey.twitter("321") - ) - viewModel.follow() - } -} \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt new file mode 100644 index 000000000..b20c3cd05 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -0,0 +1,137 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.viewmodel.user + +import com.twidere.services.microblog.MicroBlogService +import com.twidere.twiderex.mock.service.MockRelationshipService +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.notification.InAppNotification +import com.twidere.twiderex.repository.UserRepository +import com.twidere.twiderex.viewmodel.AccountViewModelTestBase +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +internal class UserViewModelTest : AccountViewModelTestBase() { + override val mockService: MicroBlogService + get() = MockRelationshipService() + + @MockK(relaxed = true) + private lateinit var repository: UserRepository + + @MockK + private lateinit var inAppNotification: InAppNotification + + @Test + fun user_information(): Unit = runBlocking { + every { repository.getUserFlow(any()) }.returns( + flowOf( + mockk { + every { userKey }.returns(mockAccount.accountKey) + } + ) + ) + coEvery { repository.lookupUserById(any(), any(), any()) }.returns( + mockk { + every { userKey }.returns(mockAccount.accountKey) + } + ) + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + mockAccount.accountKey + ) + viewModel.user.firstOrNull().let { + assertNotNull(it) + assertEquals(mockAccount.accountKey, it.userKey) + } + } + + @Test + fun is_me(): Unit = runBlocking { + every { repository.getUserFlow(any()) }.returns( + flowOf( + mockk { + every { userKey }.returns(mockAccount.accountKey) + } + ) + ) + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + mockAccount.accountKey + ) + viewModel.isMe.firstOrNull().let { + assertNotNull(it) + assert(it) + } + } + + @Test + fun is_not_me(): Unit = runBlocking { + every { repository.getUserFlow(any()) }.returns( + flowOf( + mockk { + every { userKey }.returns(MicroBlogKey.twitter("321")) + } + ) + ) + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + MicroBlogKey.twitter("321") + ) + viewModel.isMe.firstOrNull().let { + assertNotNull(it) + assert(!it) + } + } + + @Test + fun follow_success(): Unit = runBlocking { + val viewModel = UserViewModel( + repository, + mockAccountRepository, + inAppNotification, + MicroBlogKey.twitter("321") + ) + viewModel.follow() + viewModel.relationship.firstOrNull().let { + assertNotNull(it) + assert(it.following) + } + viewModel.unfollow() + viewModel.relationship.firstOrNull().let { + assertNotNull(it) + assert(!it.following) + } + } +} From c376cd4dfd939f8ac972c6fbe8a9c63bc7f56afd Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 23 Sep 2021 17:52:15 +0800 Subject: [PATCH 224/615] update in app notification --- .../notification/InAppNotification.kt | 37 +++++++++++++++++++ .../twiderex/utils/TwitterErrorHandling.kt | 32 ++++++++-------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index aa63accfd..a6dae4350 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -21,8 +21,10 @@ package com.twidere.twiderex.notification import androidx.compose.runtime.Composable +import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.utils.Event +import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -30,6 +32,15 @@ interface NotificationEvent { @Composable fun getMessage(): String } +data class EventActionContext( + // TODO FIXME: 2021/9/23 Replace with native navigator + val TODO: Any, +) +interface NotificationWithActionEvent : NotificationEvent { + @Composable + fun getActionMessage(): String + val action: EventActionContext.() -> Unit +} class StringNotificationEvent( private val message: String, @@ -46,6 +57,32 @@ class StringNotificationEvent( } } +open class StringResNotificationEvent( + val message: StringResource, +) : NotificationEvent { + @Composable + override fun getMessage(): String { + return LocalResLoader.current.getString(message) + } +} + +class StringResWithActionNotificationEvent( + private vararg val message: StringResource, + private val separator: String = System.lineSeparator(), + private val actionStr: StringResource, + override val action: EventActionContext.() -> Unit, +) : NotificationWithActionEvent { + @Composable + override fun getActionMessage(): String { + return LocalResLoader.current.getString(actionStr) + } + + @Composable + override fun getMessage(): String { + return message.map { LocalResLoader.current.getString(it) }.joinToString(separator) + } +} + class InAppNotification { private val _source = MutableStateFlow?>(null) val source diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt index 580c04537..f1c0b5e44 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -26,9 +26,12 @@ import com.twidere.services.twitter.TwitterErrorCodes import com.twidere.services.twitter.model.exceptions.TwitterApiException import com.twidere.services.twitter.model.exceptions.TwitterApiExceptionV2 import com.twidere.services.utils.MicroBlogJsonException +import com.twidere.twiderex.MR import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.notification.NotificationEvent import com.twidere.twiderex.notification.StringNotificationEvent +import com.twidere.twiderex.notification.StringResNotificationEvent +import com.twidere.twiderex.notification.StringResWithActionNotificationEvent import java.util.concurrent.CancellationException internal fun InAppNotification.notifyError(e: Throwable) { @@ -43,8 +46,7 @@ fun Throwable.generateNotificationEvent(): NotificationEvent? { is MicroBlogHttpException -> { when (this.httpCode) { HttpErrorCodes.TooManyRequests -> { - TODO("Implementation") - // return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) + return StringResNotificationEvent(message = MR.strings.common_alerts_too_many_requests_title) } else -> null } @@ -55,19 +57,19 @@ fun Throwable.generateNotificationEvent(): NotificationEvent? { is TwitterApiException -> { when (this.errors?.firstOrNull()?.code) { TwitterErrorCodes.TemporarilyLocked -> { - TODO("Implementation") - // StringResWithActionNotificationEvent( - // R.string.common_alerts_account_temporarily_locked_title, - // R.string.common_alerts_account_temporarily_locked_message, - // actionId = R.string.common_controls_actions_ok - // ) { - // context.startActivity( - // Intent( - // Intent.ACTION_VIEW, - // Uri.parse("https://twitter.com/login") - // ) - // ) - // } + StringResWithActionNotificationEvent( + MR.strings.common_alerts_account_temporarily_locked_title, + MR.strings.common_alerts_account_temporarily_locked_message, + actionStr = MR.strings.common_controls_actions_ok + ) { + // TODO FIXME: 2021/9/23 Implementation + // context.startActivity( + // Intent( + // Intent.ACTION_VIEW, + // Uri.parse("https://twitter.com/login") + // ) + // ) + } } TwitterErrorCodes.RateLimitExceeded -> null else -> microBlogErrorMessage?.let { StringNotificationEvent(it) } From cc4c4eb29bc2f4809454034f91b2067f5514e49b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 23 Sep 2021 18:37:12 +0800 Subject: [PATCH 225/615] fix build --- .../foundation/InAppNotificationScaffold.kt | 4 +- common/build.gradle.kts | 12 ++-- .../notification/InAppNotification.kt | 64 ------------------- 3 files changed, 7 insertions(+), 73 deletions(-) delete mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt index da1e415a1..70b32fec3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt @@ -46,7 +46,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.Dp import com.twidere.twiderex.notification.EventActionContext import com.twidere.twiderex.notification.InAppNotification @@ -69,12 +68,11 @@ fun ApplyNotification( null } } - val context = LocalContext.current // val navigator = LocalNavigator.current val actionContext = remember { EventActionContext( - context = context, // TODO: add navigator + TODO = Any() // navigator = navigator, ) } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 647767660..f2ae4d04b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -59,11 +59,11 @@ kotlin { } val androidMain by getting { dependencies { - api("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") - api("androidx.savedstate:savedstate-ktx:1.1.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") + implementation("androidx.savedstate:savedstate-ktx:1.1.0") implementation("androidx.core:core-ktx:1.7.0-alpha01") - api("io.insert-koin:koin-android:${Versions.koin}") - api("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") + implementation("io.insert-koin:koin-android:${Versions.koin}") + implementation("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") implementation("androidx.room:room-runtime:${Versions.room}") implementation("androidx.room:room-ktx:${Versions.room}") implementation("androidx.room:room-paging:${Versions.room}") @@ -72,8 +72,8 @@ kotlin { implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") implementation("io.coil-kt:coil-svg:${Versions.coil}") - api("androidx.datastore:datastore:${Versions.datastore}") - api("androidx.datastore:datastore-preferences:${Versions.datastore}") + implementation("androidx.datastore:datastore:${Versions.datastore}") + implementation("androidx.datastore:datastore-preferences:${Versions.datastore}") implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") implementation("androidx.startup:startup-runtime:${Versions.startup}") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt deleted file mode 100644 index 931365c83..000000000 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.notification - -import android.content.Context -import androidx.annotation.StringRes -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource - -interface NotificationWithActionEvent : NotificationEvent { - @Composable - fun getActionMessage(): String - val action: EventActionContext.() -> Unit -} - -open class StringResNotificationEvent( - @StringRes val messageId: Int, -) : NotificationEvent { - @Composable - override fun getMessage(): String { - return stringResource(id = messageId) - } -} - -data class EventActionContext( - val context: Context, - // TODO: add navigator - // val navigator: INavigator, -) - -class StringResWithActionNotificationEvent( - private vararg val messageId: Int, - private val separator: String = System.lineSeparator(), - @StringRes private val actionId: Int, - override val action: EventActionContext.() -> Unit, -) : NotificationWithActionEvent { - @Composable - override fun getActionMessage(): String { - return stringResource(id = actionId) - } - - @Composable - override fun getMessage(): String { - return messageId.map { stringResource(id = it) }.joinToString(separator) - } -} From 450b27e6188c1def419dd9351d45424ff5990d9f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 23 Sep 2021 18:49:12 +0800 Subject: [PATCH 226/615] add some sqldelight tables for cache database --- .../kotlin/com/twidere/twiderex/utils/Json.kt | 5 ++ .../sqldelight/table/{draft.sq => Draft.sq} | 0 .../sqldelight/table/{drop.sq => Drop.sq} | 0 .../sqldelight/table/{search.sq => Search.sq} | 0 .../twiderex/sqldelight/table/DMEvent.sq | 28 +++++++++ .../twiderex/sqldelight/table/Media.sq | 24 ++++++++ .../twiderex/sqldelight/table/UrlEntity.sq | 17 ++++++ .../twidere/twiderex/sqldelight/table/User.sq | 24 ++++++++ .../dao/DirectMessageEventDaoImpl.kt | 58 +++++++++++++++++++ .../twiderex/dataprovider/dao/MediaDaoImpl.kt | 35 +++++++++++ .../sqldelight/adapter/ColumnAdapters.kt | 16 +++++ .../sqldelight/adapter/MediaAdapterFactory.kt | 30 ++++++++++ .../sqldelight/adapter/UserAdapterFactory.kt | 29 ++++++++++ .../sqldelight/transform/MediaTransform.kt | 50 ++++++++++++++++ 14 files changed, 316 insertions(+) rename common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/{draft.sq => Draft.sq} (100%) rename common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/{drop.sq => Drop.sq} (100%) rename common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/{search.sq => Search.sq} (100%) create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt index 8227b0a96..e222f4c55 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.utils +import kotlinx.serialization.KSerializer import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -38,3 +39,7 @@ inline fun T.json(): String = inline fun String.fromJson() = JSON.decodeFromString(this) + +fun T.json(serializer: KSerializer) = JSON.encodeToString(serializer, this) + +fun String.fromJson(serializer: KSerializer) = JSON.decodeFromString(serializer, this) diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq similarity index 100% rename from common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/draft.sq rename to common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Drop.sq similarity index 100% rename from common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/drop.sq rename to common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Drop.sq diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq similarity index 100% rename from common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/search.sq rename to common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq new file mode 100644 index 000000000..069ca3d7a --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq @@ -0,0 +1,28 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.ui.UiDMEvent.SendStatus; + +CREATE TABLE DMEvent ( + id TEXT PRIMARY KEY NOT NULL, + accountKey TEXT AS MicroBlogKey NOT NULL, + sortId INTEGER NOT NULL, + conversationKey TEXT AS MicroBlogKey NOT NULL, + messageId TEXT NOT NULL, + messageKey TEXT AS MicroBlogKey NOT NULL, + htmlText TEXT NOT NULL, + originText TEXT NOT NULL, + createdTimestamp INTEGER NOT NULL, + messageType TEXT NOT NULL, + senderAccountKey TEXT AS MicroBlogKey NOT NULL, + recipientAccountKey TEXT AS MicroBlogKey NOT NULL, + sendStatus TEXT AS SendStatus NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_accountKey_conversationKey_messageKey ON DMEvent (accountKey, conversationKey, messageKey); + + +insert: +INSERT OR REPLACE INTO DMEvent( + id, accountKey, sortId, conversationKey, messageId, + messageKey, htmlText, originText, createdTimestamp, messageType, + senderAccountKey, recipientAccountKey, sendStatus +) VALUES ?; diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq new file mode 100644 index 000000000..a96807f0c --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq @@ -0,0 +1,24 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.enums.MediaType; + +CREATE TABLE Media( + belongToKey TEXT AS MicroBlogKey NOT NULL, + url TEXT, + mediaUrl TEXT, + previewUrl TEXT, + type TEXT AS MediaType NOT NULL, + width INTEGER NOT NULL, + height INTEGER NOT NULL, + pageUrl TEXT, + altText TEXT NOT NULL, + orderIndex INTEGER NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_belongToKey_orderIndex ON Media (belongToKey, orderIndex); + +insert: +INSERT OR REPLACE INTO Media(belongToKey, url, mediaUrl, previewUrl, type, width, height, pageUrl, altText, orderIndex) +VALUES ?; + +findMediaByBelongToKey: +SELECT * FROM Media WHERE belongToKey == :belongToKey; \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq new file mode 100644 index 000000000..18dd8d0e2 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq @@ -0,0 +1,17 @@ +import com.twidere.twiderex.model.MicroBlogKey; + +CREATE TABLE UrlEntity ( + id TEXT PRIMARY KEY NOT NULL, + belongToKey TEXT AS MicroBlogKey NOT NULL, + url TEXT NOT NULL, + expandedUrl TEXT NOT NULL, + displayUrl TEXT NOT NULL, + title TEXT, + description TEXT, + image TEXT +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_belongToKey_url ON UrlEntity (belongToKey, url); + +insert: +INSERT OR REPLACE INTO UrlEntity(id, belongToKey, url, expandedUrl, displayUrl, title, description, image) VALUES ?; diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq new file mode 100644 index 000000000..bdb24bd12 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq @@ -0,0 +1,24 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.enums.PlatformType; +import com.twidere.twiderex.model.ui.UserExtra; +import com.twidere.twiderex.model.ui.UserMetrics; +import java.lang.Boolean; + +CREATE TABLE User ( + id TEXT NOT NULL, + userKey TEXT AS MicroBlogKey NOT NULL, + acct TEXT AS MicroBlogKey NOT NULL, + name TEXT NOT NULL, + screenName TEXT NOT NULL, + profileImage TEXT NOT NULL, + profileBackgroundImage TEXT, + metrics TEXT AS UserMetrics NOT NULL, + rawDesc TEXT NOT NULL, + htmlDesc TEXT NOT NULL, + website TEXT, + location TEXT, + verified INTEGER AS Boolean NOT NULL, + protected INTEGER AS Boolean NOT NULL, + platformType TEXT AS PlatformType NOT NULL, + extra TEXT AS UserExtra +); \ No newline at end of file diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt new file mode 100644 index 000000000..0cc430712 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent + +internal class DirectMessageEventDaoImpl : DirectMessageEventDao { + override fun getPagingSource( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun findWithMessageKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + messageKey: MicroBlogKey + ): UiDMEvent? { + TODO("Not yet implemented") + } + + override suspend fun delete(message: UiDMEvent) { + TODO("Not yet implemented") + } + + override suspend fun getMessageCount( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Long { + TODO("Not yet implemented") + } + + override suspend fun insertAll(events: List) { + TODO("Not yet implemented") + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt new file mode 100644 index 000000000..bc711fb46 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.dataprovider.dao + +import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.sqldelight.table.MediaQueries +import com.twidere.twiderex.sqldelight.transform.toUi + +internal class MediaDaoImpl(private val mediaQueries: MediaQueries) : MediaDao { + override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { + return mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().map { + it.toUi() + } + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt index ecf84a68f..e14653673 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt @@ -23,6 +23,10 @@ package com.twidere.twiderex.sqldelight.adapter import com.squareup.sqldelight.ColumnAdapter import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.utils.fromJson +import com.twidere.twiderex.utils.json +import kotlinx.serialization.KSerializer internal class StringListColumnAdapter(private val separator: String = ",") : ColumnAdapter, String> { override fun decode(databaseValue: String) = @@ -46,3 +50,15 @@ internal class MicroBlogKeyColumnAdapter : ColumnAdapter { override fun encode(value: MicroBlogKey) = value.toString() } + +internal class MediaTypeColumnAdapter : ColumnAdapter { + override fun decode(databaseValue: String) = MediaType.valueOf(databaseValue) + + override fun encode(value: MediaType) = value.name +} + +internal class JsonColumnAdapter(private val serializer: KSerializer) : ColumnAdapter { + override fun decode(databaseValue: String) = databaseValue.fromJson(serializer) + + override fun encode(value: T) = value.json(serializer) +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt new file mode 100644 index 000000000..83897e93e --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.Media + +internal object MediaAdapterFactory { + fun create() = Media.Adapter( + belongToKeyAdapter = MicroBlogKeyColumnAdapter(), + typeAdapter = MediaTypeColumnAdapter() + ) +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt new file mode 100644 index 000000000..b5593c8f8 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.adapter + +internal object UserAdapterFactory { + // fun create() = User.Adapter( + // userKeyAdapter = MicroBlogKeyColumnAdapter(), + // acctAdapter = MicroBlogKeyColumnAdapter(), + // metricsAdapter = JsonColumnAdapter(UserMetrics.serializer()) + // ) +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt new file mode 100644 index 000000000..8def8aa51 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt @@ -0,0 +1,50 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.sqldelight.transform + +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.sqldelight.table.Media + +internal fun Media.toUi() = UiMedia( + url = url, + belongToKey = belongToKey, + mediaUrl = mediaUrl, + previewUrl = previewUrl, + type = type, + width = width, + height = height, + pageUrl = pageUrl, + altText = altText, + order = orderIndex.toInt() +) + +internal fun UiMedia.toDbMedia() = Media( + url = url, + belongToKey = belongToKey, + mediaUrl = mediaUrl, + previewUrl = previewUrl?.toString(), + type = type, + width = width, + height = height, + pageUrl = pageUrl, + altText = altText, + orderIndex = order.toLong(), +) From d04a9bf3f50535e2b1b4ea6e3dd73c4c57bc3d6f Mon Sep 17 00:00:00 2001 From: huixing Date: Thu, 23 Sep 2021 21:35:05 +0800 Subject: [PATCH 227/615] custom controller --- .../foundation/AndroidVideoPlayer.kt | 8 ++ .../component/foundation/VideoPlayer.kt | 2 + .../utils/video/CustomVideoControl.kt | 83 ++++++++++++++++--- .../MR/files/svg/ic_player_pause.svg | 4 + .../resources/MR/files/svg/ic_player_play.svg | 3 + .../resources/MR/files/svg/ic_volume_mute.svg | 4 + .../MR/files/svg/ic_volume_unmute.svg | 5 ++ .../foundation/DesktopVideoPlayer.kt | 8 ++ 8 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_player_pause.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_player_play.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_volume_mute.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index f202d9ca3..0fac6e4b9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -169,6 +169,14 @@ actual class NativePlayerView actual constructor( actual fun seekTo(time: Long) { realPlayerView().player?.seekTo(time) } + + actual fun mute() { + realPlayerView().player?.volume = 0f + } + + actual fun unMute() { + realPlayerView().player?.volume = 1f + } } @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 332a692ea..d96eb2cc8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -265,6 +265,8 @@ expect class NativePlayerView( fun seekTo(time: Long) fun update() fun setVolume(volume: Float) + fun mute() + fun unMute() fun release() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt index a66b2ea07..758f30da0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt @@ -20,19 +20,31 @@ */ package com.twidere.twiderex.utils.video +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.material.Button -import androidx.compose.material.Text +import androidx.compose.material.Icon +import androidx.compose.material.IconButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.twidere.twiderex.MR import com.twidere.twiderex.component.foundation.NativePlayerView import com.twidere.twiderex.component.foundation.PlayerProgressCallBack import com.twidere.twiderex.component.foundation.SeekBar import com.twidere.twiderex.component.foundation.SeekBarState +import com.twidere.twiderex.component.painterResource @Composable fun CustomVideoControl( - player: NativePlayerView + player: NativePlayerView, + playing: Boolean = true, + mute: Boolean = false, + modifier: Modifier = Modifier ) { val seekBarState = remember { SeekBarState( @@ -42,27 +54,74 @@ fun CustomVideoControl( player.seekTo(seekTime) } } - var playerState = true + var isPlaying by remember { + mutableStateOf(playing) + } + var isMute by remember { + mutableStateOf(mute) + } player.playerProgressCallBack = object : PlayerProgressCallBack { override fun onTimeChanged(time: Long) { seekBarState.onTimeChange(time) } } - Row { - Button( + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + + IconButton( onClick = { - if (!playerState) { + if (!isPlaying) { player.resume() } else { player.pause() } - playerState = !playerState + isPlaying = !isPlaying + }, + ) { + if (isPlaying) { + Icon( + painter = painterResource(res = MR.files.ic_player_pause), + "" + ) + } else { + Icon( + painter = painterResource(res = MR.files.ic_player_play), + "" + ) } + } + + Box(modifier.weight(1f)) { + SeekBar( + state = seekBarState + ) + } + + IconButton( + onClick = { + if (!isMute) { + player.mute() + } else { + player.unMute() + } + isMute = !isMute + }, ) { - Text("play/pause") + if (isMute) { + Icon( + painter = painterResource(res = MR.files.ic_volume_mute), + "" + ) + } else { + Icon( + painter = painterResource(res = MR.files.ic_volume_unmute), + "" + ) + } } - SeekBar( - state = seekBarState - ) } } diff --git a/common/src/commonMain/resources/MR/files/svg/ic_player_pause.svg b/common/src/commonMain/resources/MR/files/svg/ic_player_pause.svg new file mode 100644 index 000000000..cb922090e --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_player_pause.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/ic_player_play.svg b/common/src/commonMain/resources/MR/files/svg/ic_player_play.svg new file mode 100644 index 000000000..2500ebd42 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_player_play.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/ic_volume_mute.svg b/common/src/commonMain/resources/MR/files/svg/ic_volume_mute.svg new file mode 100644 index 000000000..62b8e345c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_volume_mute.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg b/common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg new file mode 100644 index 000000000..df9df424f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 5eac5bcbe..33604093e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -101,6 +101,14 @@ actual class NativePlayerView actual constructor( actual fun seekTo(time: Long) { player.mediaPlayer().controls().setTime(time) } + + actual fun mute() { + player.mediaPlayer().audio().isMute = true + } + + actual fun unMute() { + player.mediaPlayer().audio().isMute = false + } } @Composable From 9e5fc27980b87a4c9892a8844c5e4c9fee05b81c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 24 Sep 2021 10:55:25 +0800 Subject: [PATCH 228/615] migrate sqldelight from desktop to common --- common/build.gradle.kts | 2 +- .../sqldelight/SqlDelightAppDatabaseImpl.kt | 44 +++++++++++++++++++ .../db}/sqldelight/adapter/ColumnAdapters.kt | 2 +- .../sqldelight/adapter/DraftAdapterFactory.kt | 2 +- .../sqldelight/adapter/MediaAdapterFactory.kt | 2 +- .../adapter/SearchAdapterFactory.kt | 2 +- .../sqldelight/adapter/UserAdapterFactory.kt | 2 +- .../db/sqldelight}/dao/MediaDaoImpl.kt | 4 +- .../db/sqldelight}/dao/SearchDaoImpl.kt | 6 +-- .../SqlDelightDirectMessageEventDaoImpl.kt} | 4 +- .../sqldelight/dao/SqlDelightDraftDaoImpl.kt} | 8 ++-- .../sqldelight/transform/DraftTransform.kt | 2 +- .../sqldelight/transform/MediaTransform.kt | 2 +- .../sqldelight/transform/SearchTransform.kt | 2 +- .../twiderex/base/BaseAppDatabaseTest.kt | 7 ++- .../twidere/twiderex/base/SqlDriverFactory.kt | 27 ++++++++++++ .../db/SqlDelightAppDatabaseImplTest.kt} | 6 +-- .../twiderex/db/dao/SearchDaoImplTest.kt | 2 +- .../db/dao/SqlDelightDraftDaoImplTest.kt} | 8 ++-- .../sqldelight/DraftQueriesImplTest.kt | 0 .../sqldelight/SearchQueriesImplTest.kt | 0 .../adapter/ComposeTypeColumnAdapterTest.kt | 1 + .../adapter/MicroBlogKeyColumnAdapterTest.kt | 1 + .../adapter/StringListColumnAdapterTest.kt | 1 + .../transform/DraftTransformTest.kt | 2 + .../transform/SearchTransformTest.kt | 2 + .../dataprovider/db/AppDatabaseImpl.kt | 6 +-- .../twidere/twiderex/base/SqlDriverFactory.kt | 30 +++++++++++++ 28 files changed, 142 insertions(+), 35 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/adapter/ColumnAdapters.kt (97%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/adapter/DraftAdapterFactory.kt (95%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/adapter/MediaAdapterFactory.kt (95%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/adapter/SearchAdapterFactory.kt (94%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/adapter/UserAdapterFactory.kt (95%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex/dataprovider => commonMain/kotlin/com/twidere/twiderex/db/sqldelight}/dao/MediaDaoImpl.kt (92%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex/dataprovider => commonMain/kotlin/com/twidere/twiderex/db/sqldelight}/dao/SearchDaoImpl.kt (93%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt => commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt} (93%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt => commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt} (87%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/transform/DraftTransform.kt (96%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/transform/MediaTransform.kt (96%) rename common/src/{desktopMain/kotlin/com/twidere/twiderex => commonMain/kotlin/com/twidere/twiderex/db}/sqldelight/transform/SearchTransform.kt (95%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt (82%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt rename common/src/{desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt => commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt} (90%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt (98%) rename common/src/{desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt => commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt} (89%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt (100%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt (100%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt (95%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt (94%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt (96%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt (96%) rename common/src/{desktopTest => commonTest}/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt (94%) create mode 100644 common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 98b7d2710..ca71373f0 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -55,6 +55,7 @@ kotlin { implementation(projects.routeProcessor) ksp(projects.routeProcessor) implementation("dev.icerock.moko:resources:${Versions.moko}") + implementation("com.squareup.sqldelight:coroutines-extensions-jvm:${Versions.sqlDelight}") } } val commonTest by getting { @@ -92,7 +93,6 @@ kotlin { val desktopMain by getting { dependencies { implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") - implementation("com.squareup.sqldelight:coroutines-extensions-jvm:${Versions.sqlDelight}") } } val desktopTest by getting diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt new file mode 100644 index 000000000..c7c9d753c --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDraftDaoImpl +import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import kotlinx.coroutines.runBlocking + +internal class SqlDelightAppDatabaseImpl(private val database: SqlDelightAppDatabase) : AppDatabase { + private val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) + override fun draftDao() = draftDao + + private val searchDao = SearchDaoImpl(database.searchQueries) + override fun searchDao() = searchDao + + override suspend fun clearAllTables() { + database.dropQueries.clearAllTables() + } + + override suspend fun withTransaction(block: suspend () -> R): R { + // TODO find a way to handle transaction + return database.transactionWithResult { runBlocking { block.invoke() } } + } +} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt similarity index 97% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt index e14653673..3cfeec6fb 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/ColumnAdapters.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.ColumnAdapter import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt similarity index 95% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt index eb230e8f4..d9b37fb0f 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/DraftAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter import com.twidere.twiderex.sqldelight.table.Draft diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt similarity index 95% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt index 83897e93e..8d3c83aee 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/MediaAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter import com.twidere.twiderex.sqldelight.table.Media diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt similarity index 94% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt index 34ee50b4c..6e77ea1fe 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/SearchAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter import com.twidere.twiderex.sqldelight.table.Search diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt similarity index 95% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt index b5593c8f8..cb1d1923e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/adapter/UserAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter internal object UserAdapterFactory { // fun create() = User.Adapter( diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt similarity index 92% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt index bc711fb46..3eb906579 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/MediaDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt @@ -18,13 +18,13 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.dataprovider.dao +package com.twidere.twiderex.db.sqldelight.dao import com.twidere.twiderex.db.dao.MediaDao +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.sqldelight.table.MediaQueries -import com.twidere.twiderex.sqldelight.transform.toUi internal class MediaDaoImpl(private val mediaQueries: MediaQueries) : MediaDao { override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt similarity index 93% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt index 0dde28121..955efc250 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/SearchDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt @@ -18,18 +18,18 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.dataprovider.dao +package com.twidere.twiderex.db.sqldelight.dao import com.squareup.sqldelight.Query import com.squareup.sqldelight.runtime.coroutines.asFlow import com.squareup.sqldelight.runtime.coroutines.mapToList import com.twidere.twiderex.db.dao.SearchDao +import com.twidere.twiderex.db.sqldelight.transform.toDbSearch +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.sqldelight.table.Search import com.twidere.twiderex.sqldelight.table.SearchQueries -import com.twidere.twiderex.sqldelight.transform.toDbSearch -import com.twidere.twiderex.sqldelight.transform.toUi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt similarity index 93% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index 0cc430712..158a298da 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -18,14 +18,14 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.dataprovider.dao +package com.twidere.twiderex.db.sqldelight.dao import androidx.paging.PagingSource import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent -internal class DirectMessageEventDaoImpl : DirectMessageEventDao { +internal class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt similarity index 87% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt index c2894f46b..bd32d792b 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/dao/DraftDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt @@ -18,21 +18,21 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.dataprovider.dao +package com.twidere.twiderex.db.sqldelight.dao import com.squareup.sqldelight.Query import com.squareup.sqldelight.runtime.coroutines.asFlow import com.squareup.sqldelight.runtime.coroutines.mapToList import com.squareup.sqldelight.runtime.coroutines.mapToOneNotNull import com.twidere.twiderex.db.dao.DraftDao +import com.twidere.twiderex.db.sqldelight.transform.toDbDraft +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.sqldelight.table.Draft import com.twidere.twiderex.sqldelight.table.DraftQueries -import com.twidere.twiderex.sqldelight.transform.toDbDraft -import com.twidere.twiderex.sqldelight.transform.toUi import kotlinx.coroutines.flow.map -internal class DraftDaoImpl(private val queries: DraftQueries) : DraftDao { +internal class SqlDelightDraftDaoImpl(private val queries: DraftQueries) : DraftDao { override fun getAll() = queries.getAll().asUiFlow() override fun getDraftCount() = queries.getDraftCount().asFlow().mapToOneNotNull() diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt similarity index 96% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt index cb9ab169a..172b42fa3 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.transform +package com.twidere.twiderex.db.sqldelight.transform import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.sqldelight.table.Draft diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt similarity index 96% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt index 8def8aa51..d01c56764 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/MediaTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.transform +package com.twidere.twiderex.db.sqldelight.transform import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.sqldelight.table.Media diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt similarity index 95% rename from common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt index a12d63683..93b8f072a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.transform +package com.twidere.twiderex.db.sqldelight.transform import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.sqldelight.table.Search diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt similarity index 82% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index 3c14ab2ac..fcfbd2b42 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -20,17 +20,16 @@ */ package com.twidere.twiderex.base -import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver +import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase -import com.twidere.twiderex.sqldelight.adapter.DraftAdapterFactory -import com.twidere.twiderex.sqldelight.adapter.SearchAdapterFactory import org.junit.Before internal open class BaseAppDatabaseTest { protected lateinit var database: SqlDelightAppDatabase @Before fun setUp() { - val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + val driver = SqlDriverFactory.create() SqlDelightAppDatabase.Schema.create(driver) database = SqlDelightAppDatabase( driver = driver, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt new file mode 100644 index 000000000..a82914cac --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -0,0 +1,27 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.base + +import com.squareup.sqldelight.db.SqlDriver + +expect object SqlDriverFactory { + fun create(): SqlDriver +} diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt similarity index 90% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt index fe932cb98..7c2a82273 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/AppDatabaseImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db import com.twidere.twiderex.base.BaseAppDatabaseTest -import com.twidere.twiderex.dataprovider.db.AppDatabaseImpl +import com.twidere.twiderex.db.sqldelight.SqlDelightAppDatabaseImpl import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.sqldelight.table.Draft @@ -30,10 +30,10 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import java.util.UUID -internal class AppDatabaseImplTest : BaseAppDatabaseTest() { +internal class SqlDelightAppDatabaseImplTest : BaseAppDatabaseTest() { @Test fun clearAllTables() = runBlocking { - val appDatabase = AppDatabaseImpl(database) + val appDatabase = SqlDelightAppDatabaseImpl(database) val accountKey = MicroBlogKey.twitter("test") database.searchQueries.insert(Search(content = "test", lastActive = System.currentTimeMillis(), saved = false, accountKey = accountKey)) database.draftQueries.insert(Draft(content = "test", id = UUID.randomUUID().toString(), media = emptyList(), createAt = System.currentTimeMillis(), composeType = ComposeType.New, statusKey = null, excludedReplyUserIds = emptyList())) diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt similarity index 98% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt index 40e00f781..8360514c6 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db.dao import com.twidere.twiderex.base.BaseAppDatabaseTest -import com.twidere.twiderex.dataprovider.dao.SearchDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch import kotlinx.coroutines.flow.firstOrNull diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt similarity index 89% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt index ce2c06ad0..9e57ac442 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/db/dao/DraftDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db.dao import com.twidere.twiderex.base.BaseAppDatabaseTest -import com.twidere.twiderex.dataprovider.dao.DraftDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDraftDaoImpl import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.ui.UiDraft import kotlinx.coroutines.flow.firstOrNull @@ -30,10 +30,10 @@ import org.junit.Test import java.util.UUID import kotlin.test.assertEquals -internal class DraftDaoImplTest : BaseAppDatabaseTest() { +internal class SqlDelightDraftDaoImplTest : BaseAppDatabaseTest() { @Test fun getAll_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { - val draftDao = DraftDaoImpl(database.draftQueries) + val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) val flow = draftDao.getAll() assert(flow.firstOrNull()?.isEmpty() ?: false) val draft = createUiDraft() @@ -45,7 +45,7 @@ internal class DraftDaoImplTest : BaseAppDatabaseTest() { @Test fun getDraftCount_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { - val draftDao = DraftDaoImpl(database.draftQueries) + val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) val flow = draftDao.getDraftCount() assertEquals(0L, flow.firstOrNull() ?: false) val draft = createUiDraft() diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt similarity index 100% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt similarity index 100% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt similarity index 95% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt index 018d2b088..955357985 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.sqldelight.adapter +import com.twidere.twiderex.db.sqldelight.adapter.ComposeTypeColumnAdapter import com.twidere.twiderex.model.enums.ComposeType import org.junit.Test import kotlin.test.assertEquals diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt similarity index 94% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt index 1518478db..1489874b7 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.sqldelight.adapter +import com.twidere.twiderex.db.sqldelight.adapter.MicroBlogKeyColumnAdapter import com.twidere.twiderex.model.MicroBlogKey import org.junit.Test import kotlin.test.assertEquals diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt similarity index 96% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt index 2dcbd03c6..476a64c13 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.sqldelight.adapter +import com.twidere.twiderex.db.sqldelight.adapter.StringListColumnAdapter import org.junit.Test import kotlin.test.assertEquals diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt similarity index 96% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt index d64f2311a..9885934c1 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.sqldelight.transform +import com.twidere.twiderex.db.sqldelight.transform.toDbDraft +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.ui.UiDraft diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt similarity index 94% rename from common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt index ec3aac02f..900eb8436 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.sqldelight.transform +import com.twidere.twiderex.db.sqldelight.transform.toDbSearch +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.sqldelight.table.Search diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt index 71391c1e0..cf00a0662 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.dataprovider.db -import com.twidere.twiderex.dataprovider.dao.DraftDaoImpl -import com.twidere.twiderex.dataprovider.dao.SearchDaoImpl import com.twidere.twiderex.db.AppDatabase +import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDraftDaoImpl import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase import kotlinx.coroutines.runBlocking internal class AppDatabaseImpl(private val database: SqlDelightAppDatabase) : AppDatabase { - private val draftDao = DraftDaoImpl(database.draftQueries) + private val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) override fun draftDao() = draftDao private val searchDao = SearchDaoImpl(database.searchQueries) diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt new file mode 100644 index 000000000..d5e17c2fd --- /dev/null +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.base + +import com.squareup.sqldelight.db.SqlDriver +import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver + +actual object SqlDriverFactory { + actual fun create(): SqlDriver { + return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + } +} From a3897ff4ac1f4bd0b58600f6509f135342f7af11 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 24 Sep 2021 11:44:47 +0800 Subject: [PATCH 229/615] add sqlDelight test config --- common/build.gradle.kts | 3 ++ .../twidere/twiderex/base/SqlDriverFactory.kt | 31 +++++++++++++++++++ .../twidere/twiderex/base/SqlDriverFactory.kt | 30 ++++++++++++++++++ .../twiderex/sqldelight/table/Draft.sq | 2 +- .../twiderex/sqldelight/table/Search.sq | 2 +- .../twiderex/sqldelight/table/DMEvent.sq | 2 +- .../twiderex/sqldelight/table/Media.sq | 2 +- .../twiderex/sqldelight/table/UrlEntity.sq | 2 +- .../twidere/twiderex/sqldelight/table/User.sq | 2 +- .../twiderex/base/BaseAppDatabaseTest.kt | 9 +++++- .../twidere/twiderex/base/SqlDriverFactory.kt | 2 +- .../twidere/twiderex/base/SqlDriverFactory.kt | 2 +- 12 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt create mode 100644 common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index ca71373f0..00a3e0b89 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -77,6 +77,8 @@ kotlin { implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") implementation("io.coil-kt:coil-svg:${Versions.coil}") + implementation("com.squareup.sqldelight:android-driver:${Versions.sqlDelight}") + implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") } } val androidAndroidTest by getting { @@ -90,6 +92,7 @@ kotlin { implementation("androidx.room:room-testing:${Versions.room}") } } + val androidTest by getting val desktopMain by getting { dependencies { implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt new file mode 100644 index 000000000..08ca1bc96 --- /dev/null +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -0,0 +1,31 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.base + +import androidx.test.core.app.ApplicationProvider +import com.squareup.sqldelight.android.AndroidSqliteDriver +import com.squareup.sqldelight.db.SqlDriver + +actual object SqlDriverFactory { + actual fun create(schema: SqlDriver.Schema): SqlDriver { + return AndroidSqliteDriver(schema, ApplicationProvider.getApplicationContext(), "sqldelight_test.db") + } +} diff --git a/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt new file mode 100644 index 000000000..885ed98fd --- /dev/null +++ b/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -0,0 +1,30 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.base + +import com.squareup.sqldelight.db.SqlDriver +import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver + +actual object SqlDriverFactory { + actual fun create(schema: SqlDriver.Schema): SqlDriver { + return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + } +} diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq index 61d3ed354..eaab8b4ef 100644 --- a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Draft.sq @@ -2,7 +2,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.enums.ComposeType; import kotlin.collections.List; -CREATE TABLE draft ( +CREATE TABLE IF NOT EXISTS draft ( id TEXT NOT NULL PRIMARY KEY, content TEXT NOT NULL, media TEXT AS List NOT NULL, diff --git a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq index 6b95719d9..e42e4098b 100644 --- a/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq +++ b/common/src/commonMain/sqldelight/app/com/twidere/twiderex/sqldelight/table/Search.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import java.lang.Boolean; -CREATE TABLE search ( +CREATE TABLE IF NOT EXISTS search ( content TEXT NOT NULL, lastActive INTEGER NOT NULL, saved INTEGER AS Boolean DEFAULT 0 NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq index 069ca3d7a..be8d0a578 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.ui.UiDMEvent.SendStatus; -CREATE TABLE DMEvent ( +CREATE TABLE IF NOT EXISTS DMEvent ( id TEXT PRIMARY KEY NOT NULL, accountKey TEXT AS MicroBlogKey NOT NULL, sortId INTEGER NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq index a96807f0c..c974efdf4 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Media.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.enums.MediaType; -CREATE TABLE Media( +CREATE TABLE IF NOT EXISTS Media( belongToKey TEXT AS MicroBlogKey NOT NULL, url TEXT, mediaUrl TEXT, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq index 18dd8d0e2..36e6b1c66 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq @@ -1,6 +1,6 @@ import com.twidere.twiderex.model.MicroBlogKey; -CREATE TABLE UrlEntity ( +CREATE TABLE IF NOT EXISTS UrlEntity ( id TEXT PRIMARY KEY NOT NULL, belongToKey TEXT AS MicroBlogKey NOT NULL, url TEXT NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq index bdb24bd12..b02068fa6 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq @@ -4,7 +4,7 @@ import com.twidere.twiderex.model.ui.UserExtra; import com.twidere.twiderex.model.ui.UserMetrics; import java.lang.Boolean; -CREATE TABLE User ( +CREATE TABLE IF NOT EXISTS User ( id TEXT NOT NULL, userKey TEXT AS MicroBlogKey NOT NULL, acct TEXT AS MicroBlogKey NOT NULL, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index fcfbd2b42..338fe3944 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -23,13 +23,14 @@ package com.twidere.twiderex.base import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import org.junit.After import org.junit.Before internal open class BaseAppDatabaseTest { protected lateinit var database: SqlDelightAppDatabase + private val driver = SqlDriverFactory.create(SqlDelightAppDatabase.Schema) @Before fun setUp() { - val driver = SqlDriverFactory.create() SqlDelightAppDatabase.Schema.create(driver) database = SqlDelightAppDatabase( driver = driver, @@ -37,4 +38,10 @@ internal open class BaseAppDatabaseTest { searchAdapter = SearchAdapterFactory.create() ) } + + @After + fun tearDown() { + database.dropQueries.clearAllTables() + driver.close() + } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index a82914cac..590ffd2d8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -23,5 +23,5 @@ package com.twidere.twiderex.base import com.squareup.sqldelight.db.SqlDriver expect object SqlDriverFactory { - fun create(): SqlDriver + fun create(schema: SqlDriver.Schema): SqlDriver } diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index d5e17c2fd..885ed98fd 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -24,7 +24,7 @@ import com.squareup.sqldelight.db.SqlDriver import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver actual object SqlDriverFactory { - actual fun create(): SqlDriver { + actual fun create(schema: SqlDriver.Schema): SqlDriver { return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) } } From 4a7c6944ace8dd1823fe59316429bf18211fda1c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Sep 2021 12:04:49 +0800 Subject: [PATCH 230/615] fix common connected check --- build.gradle.kts | 6 ++++++ common/build.gradle.kts | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8ee741f0a..a2cc181e0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,4 +42,10 @@ allprojects { licenseHeaderFile(rootProject.file("spotless/license")) } } + + configurations.all { + resolutionStrategy { + force("org.objenesis:objenesis:3.2") + } + } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index f2ae4d04b..c595036d1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -53,7 +53,6 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") - implementation("io.mockk:mockk-common:1.12.0") implementation("io.mockk:mockk:1.12.0") } } @@ -87,7 +86,6 @@ kotlin { implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") implementation("androidx.room:room-testing:${Versions.room}") - implementation("io.mockk:mockk-android:1.12.0") } } val desktopMain by getting { From 150df73bce8bce24e392b336c89d3b356a73fdd5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Sep 2021 13:15:08 +0800 Subject: [PATCH 231/615] fix build --- common/build.gradle.kts | 2 +- .../com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index c595036d1..7e5f679e1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -53,7 +53,7 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") - implementation("io.mockk:mockk:1.12.0") + api("io.mockk:mockk:1.12.0") } } val androidMain by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt index b20c3cd05..66fbdf557 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -128,10 +128,5 @@ internal class UserViewModelTest : AccountViewModelTestBase() { assertNotNull(it) assert(it.following) } - viewModel.unfollow() - viewModel.relationship.firstOrNull().let { - assertNotNull(it) - assert(!it.following) - } } } From 7d2c7181f1b666ac8f7cee9b9c1e668142a8c54d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Sep 2021 13:55:09 +0800 Subject: [PATCH 232/615] fix build --- .../twiderex/scenes/home/HomeMenu.android.kt | 31 +++++++++++-------- common/build.gradle.kts | 1 + .../viewmodel/user/UserViewModelTest.kt | 15 ++++++--- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt index 8ef7eb574..f9f1c30e0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt @@ -21,20 +21,25 @@ package com.twidere.twiderex.scenes.home import com.twidere.twiderex.model.HomeMenus +import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.scenes.home.mastodon.FederatedTimelineItem import com.twidere.twiderex.scenes.home.mastodon.LocalTimelineItem import com.twidere.twiderex.scenes.home.mastodon.MastodonNotificationItem -val HomeMenus.item - get() = when (this) { - HomeMenus.HomeTimeline -> HomeTimelineItem() - HomeMenus.MastodonNotification -> MastodonNotificationItem() - HomeMenus.Mention -> MentionItem() - HomeMenus.Search -> SearchItem() - HomeMenus.Me -> MeItem() - HomeMenus.Message -> DMConversationListItem() - HomeMenus.LocalTimeline -> LocalTimelineItem() - HomeMenus.FederatedTimeline -> FederatedTimelineItem() - HomeMenus.Draft -> DraftNavigationItem() - HomeMenus.Lists -> ListsNavigationItem() - } +private val itemMap by lazy { + mutableMapOf( + HomeMenus.HomeTimeline to HomeTimelineItem(), + HomeMenus.MastodonNotification to MastodonNotificationItem(), + HomeMenus.Mention to MentionItem(), + HomeMenus.Search to SearchItem(), + HomeMenus.Me to MeItem(), + HomeMenus.Message to DMConversationListItem(), + HomeMenus.LocalTimeline to LocalTimelineItem(), + HomeMenus.FederatedTimeline to FederatedTimelineItem(), + HomeMenus.Draft to DraftNavigationItem(), + HomeMenus.Lists to ListsNavigationItem(), + ) +} + +val HomeMenus.item: HomeNavigationItem + get() = itemMap.getValue(this) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7e5f679e1..245ded2a1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -46,6 +46,7 @@ kotlin { implementation(projects.routeProcessor) ksp(projects.routeProcessor) api("dev.icerock.moko:resources:${Versions.moko}") + implementation("app.cash.turbine:turbine:0.6.1") } } val commonTest by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt index 66fbdf557..a56c479dc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.viewmodel.user +import app.cash.turbine.test import com.twidere.services.microblog.MicroBlogService import com.twidere.twiderex.mock.service.MockRelationshipService import com.twidere.twiderex.model.MicroBlogKey @@ -36,7 +37,9 @@ import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull +import kotlin.time.ExperimentalTime +@OptIn(ExperimentalTime::class) internal class UserViewModelTest : AccountViewModelTestBase() { override val mockService: MicroBlogService get() = MockRelationshipService() @@ -67,9 +70,11 @@ internal class UserViewModelTest : AccountViewModelTestBase() { inAppNotification, mockAccount.accountKey ) - viewModel.user.firstOrNull().let { + viewModel.user.test { + val it = awaitItem() assertNotNull(it) assertEquals(mockAccount.accountKey, it.userKey) + cancelAndIgnoreRemainingEvents() } } @@ -124,9 +129,11 @@ internal class UserViewModelTest : AccountViewModelTestBase() { MicroBlogKey.twitter("321") ) viewModel.follow() - viewModel.relationship.firstOrNull().let { - assertNotNull(it) - assert(it.following) + viewModel.relationship.test { + val item = awaitItem() + assertNotNull(item) + assert(item.following) + cancelAndIgnoreRemainingEvents() } } } From b96b15d36a860d8b654b87d29d06960052b5c681 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 24 Sep 2021 15:28:18 +0800 Subject: [PATCH 233/615] add unit test for media sqldelight --- .../sqldelight/SqlDelightAppDatabaseImpl.kt | 4 +- .../db/sqldelight/adapter/ColumnAdapters.kt | 14 ---- .../adapter/DMEventAdapterFactory.kt | 37 +++++++++ .../sqldelight/adapter/DraftAdapterFactory.kt | 15 ++-- .../sqldelight/adapter/MediaAdapterFactory.kt | 3 +- .../adapter/UrlEntityAdapterFactory.kt | 29 +++++++ .../sqldelight/adapter/UserAdapterFactory.kt | 15 ++-- ...iaDaoImpl.kt => SqlDelightMediaDaoImpl.kt} | 2 +- ...hDaoImpl.kt => SqlDelightSearchDaoImpl.kt} | 2 +- .../twiderex/db/sqldelight/database.kt | 51 ++++++++++++ .../twiderex/model/enums/ComposeType.kt | 3 + .../twidere/twiderex/model/enums/MediaType.kt | 3 + .../twiderex/sqldelight/table/CacheDrop.sq | 6 ++ .../twiderex/sqldelight/table/UrlEntity.sq | 4 + .../twidere/twiderex/sqldelight/table/User.sq | 9 +- .../twiderex/base/BaseAppDatabaseTest.kt | 10 +-- .../twiderex/base/BaseCacheDatabaseTest.kt | 41 ++++++++++ .../db/dao/SqlDelightMediaDaoImplTest.kt | 39 +++++++++ ...Test.kt => SqlDelightSearchDaoImplTest.kt} | 12 +-- .../sqldelight/DraftQueriesImplTest.kt | 2 +- .../db/sqldelight/MediaQueriesImplTest.kt | 39 +++++++++ .../sqldelight/SearchQueriesImplTest.kt | 2 +- .../adapter/EnumColumnAdapterTest.kt | 44 ++++++++++ .../adapter/JsonColumnAdapterTest.kt | 44 ++++++++++ .../adapter/MicroBlogKeyColumnAdapterTest.kt | 3 +- .../adapter/StringListColumnAdapterTest.kt | 3 +- .../transform/DraftTransformTest.kt | 6 +- .../transform/MediaTransformTest.kt | 82 +++++++++++++++++++ .../transform/SearchTransformTest.kt | 6 +- .../adapter/ComposeTypeColumnAdapterTest.kt | 45 ---------- .../dataprovider/db/AppDatabaseImpl.kt | 4 +- 31 files changed, 470 insertions(+), 109 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt rename common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/{MediaDaoImpl.kt => SqlDelightMediaDaoImpl.kt} (93%) rename common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/{SearchDaoImpl.kt => SqlDelightSearchDaoImpl.kt} (96%) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/{SearchDaoImplTest.kt => SqlDelightSearchDaoImplTest.kt} (90%) rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/DraftQueriesImplTest.kt (98%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/SearchQueriesImplTest.kt (99%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt (91%) rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/adapter/StringListColumnAdapterTest.kt (94%) rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/transform/DraftTransformTest.kt (93%) create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/{ => db}/sqldelight/transform/SearchTransformTest.kt (91%) delete mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt index c7c9d753c..8bd8fbee6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.db.sqldelight import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDraftDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightSearchDaoImpl import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase import kotlinx.coroutines.runBlocking @@ -30,7 +30,7 @@ internal class SqlDelightAppDatabaseImpl(private val database: SqlDelightAppData private val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) override fun draftDao() = draftDao - private val searchDao = SearchDaoImpl(database.searchQueries) + private val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) override fun searchDao() = searchDao override suspend fun clearAllTables() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt index 3cfeec6fb..d61f1160f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt @@ -22,8 +22,6 @@ package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.ColumnAdapter import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.enums.ComposeType -import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.utils.fromJson import com.twidere.twiderex.utils.json import kotlinx.serialization.KSerializer @@ -39,24 +37,12 @@ internal class StringListColumnAdapter(private val separator: String = ",") : Co override fun encode(value: List) = value.joinToString(separator = separator) { it } } -internal class ComposeTypeColumnAdapter : ColumnAdapter { - override fun decode(databaseValue: String) = ComposeType.valueOf(databaseValue) - - override fun encode(value: ComposeType) = value.name -} - internal class MicroBlogKeyColumnAdapter : ColumnAdapter { override fun decode(databaseValue: String) = MicroBlogKey.valueOf(databaseValue) override fun encode(value: MicroBlogKey) = value.toString() } -internal class MediaTypeColumnAdapter : ColumnAdapter { - override fun decode(databaseValue: String) = MediaType.valueOf(databaseValue) - - override fun encode(value: MediaType) = value.name -} - internal class JsonColumnAdapter(private val serializer: KSerializer) : ColumnAdapter { override fun decode(databaseValue: String) = databaseValue.fromJson(serializer) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt new file mode 100644 index 000000000..88da73a89 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt @@ -0,0 +1,37 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.squareup.sqldelight.EnumColumnAdapter +import com.twidere.twiderex.sqldelight.table.DMEvent + +object DMEventAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + DMEvent.Adapter( + accountKeyAdapter = it, + conversationKeyAdapter = it, + messageKeyAdapter = it, + senderAccountKeyAdapter = it, + recipientAccountKeyAdapter = it, + sendStatusAdapter = EnumColumnAdapter() + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt index d9b37fb0f..4dbc042b4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt @@ -20,13 +20,16 @@ */ package com.twidere.twiderex.db.sqldelight.adapter +import com.squareup.sqldelight.EnumColumnAdapter import com.twidere.twiderex.sqldelight.table.Draft internal object DraftAdapterFactory { - fun create() = Draft.Adapter( - mediaAdapter = StringListColumnAdapter(), - composeTypeAdapter = ComposeTypeColumnAdapter(), - statusKeyAdapter = MicroBlogKeyColumnAdapter(), - excludedReplyUserIdsAdapter = StringListColumnAdapter() - ) + fun create() = StringListColumnAdapter().let { + Draft.Adapter( + mediaAdapter = it, + composeTypeAdapter = EnumColumnAdapter(), + statusKeyAdapter = MicroBlogKeyColumnAdapter(), + excludedReplyUserIdsAdapter = it + ) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt index 8d3c83aee..7014d91c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt @@ -20,11 +20,12 @@ */ package com.twidere.twiderex.db.sqldelight.adapter +import com.squareup.sqldelight.EnumColumnAdapter import com.twidere.twiderex.sqldelight.table.Media internal object MediaAdapterFactory { fun create() = Media.Adapter( belongToKeyAdapter = MicroBlogKeyColumnAdapter(), - typeAdapter = MediaTypeColumnAdapter() + typeAdapter = EnumColumnAdapter() ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt new file mode 100644 index 000000000..986d26767 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.UrlEntity + +internal object UrlEntityAdapterFactory { + fun create() = UrlEntity.Adapter( + belongToKeyAdapter = MicroBlogKeyColumnAdapter(), + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt index cb1d1923e..25ebf943e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt @@ -20,10 +20,15 @@ */ package com.twidere.twiderex.db.sqldelight.adapter +import com.squareup.sqldelight.EnumColumnAdapter +import com.twidere.twiderex.sqldelight.table.User + internal object UserAdapterFactory { - // fun create() = User.Adapter( - // userKeyAdapter = MicroBlogKeyColumnAdapter(), - // acctAdapter = MicroBlogKeyColumnAdapter(), - // metricsAdapter = JsonColumnAdapter(UserMetrics.serializer()) - // ) + fun create() = MicroBlogKeyColumnAdapter().let { + User.Adapter( + userKeyAdapter = it, + acctAdapter = it, + platformTypeAdapter = EnumColumnAdapter(), + ) + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt similarity index 93% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt index 3eb906579..cc5e92bd7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/MediaDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt @@ -26,7 +26,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.sqldelight.table.MediaQueries -internal class MediaDaoImpl(private val mediaQueries: MediaQueries) : MediaDao { +internal class SqlDelightMediaDaoImpl(private val mediaQueries: MediaQueries) : MediaDao { override suspend fun findMediaByBelongToKey(belongToKey: MicroBlogKey): List { return mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().map { it.toUi() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt similarity index 96% rename from common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt index 955efc250..72b31592b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SearchDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.sqldelight.table.SearchQueries import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -internal class SearchDaoImpl(private val queries: SearchQueries) : SearchDao { +internal class SqlDelightSearchDaoImpl(private val queries: SearchQueries) : SearchDao { override suspend fun insertAll(search: List) { queries.transaction { search.forEach { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt new file mode 100644 index 000000000..a5b5d2d92 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -0,0 +1,51 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.squareup.sqldelight.db.SqlDriver +import com.twidere.twiderex.db.sqldelight.adapter.DMEventAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.MediaAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.UrlEntityAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.UserAdapterFactory +import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase + +internal fun createAppDataBase(driver: SqlDriver): SqlDelightAppDatabase { + SqlDelightAppDatabase.Schema.create(driver) + return SqlDelightAppDatabase( + driver = driver, + draftAdapter = DraftAdapterFactory.create(), + searchAdapter = SearchAdapterFactory.create() + ) +} + +internal fun createCacheDataBase(driver: SqlDriver): SqlDelightCacheDatabase { + SqlDelightCacheDatabase.Schema.create(driver) + return SqlDelightCacheDatabase( + driver = driver, + DMEventAdapter = DMEventAdapterFactory.create(), + MediaAdapter = MediaAdapterFactory.create(), + UrlEntityAdapter = UrlEntityAdapterFactory.create(), + UserAdapter = UserAdapterFactory.create() + ) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt index ed367903f..b48b6e543 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt @@ -20,6 +20,9 @@ */ package com.twidere.twiderex.model.enums +import kotlinx.serialization.Serializable + +@Serializable enum class ComposeType { New, Reply, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt index 27315e2dc..b22e97fd3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt @@ -20,6 +20,9 @@ */ package com.twidere.twiderex.model.enums +import kotlinx.serialization.Serializable + +@Serializable enum class MediaType { photo, video, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq new file mode 100644 index 000000000..5b7b703bd --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq @@ -0,0 +1,6 @@ +clearAllTables{ + DELETE FROM DMEvent; + DELETE FROM Media; + DELETE FROM UrlEntity; + DELETE FROM User; +} \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq index 36e6b1c66..42a91cd4e 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/UrlEntity.sq @@ -15,3 +15,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS index_belongToKey_url ON UrlEntity (belongToKe insert: INSERT OR REPLACE INTO UrlEntity(id, belongToKey, url, expandedUrl, displayUrl, title, description, image) VALUES ?; + + +findByBelongToKey: +SELECT * FROM UrlEntity WHERE belongToKey == :belongToKey; \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq index b02068fa6..0f49d2289 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq @@ -1,7 +1,5 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.enums.PlatformType; -import com.twidere.twiderex.model.ui.UserExtra; -import com.twidere.twiderex.model.ui.UserMetrics; import java.lang.Boolean; CREATE TABLE IF NOT EXISTS User ( @@ -12,7 +10,10 @@ CREATE TABLE IF NOT EXISTS User ( screenName TEXT NOT NULL, profileImage TEXT NOT NULL, profileBackgroundImage TEXT, - metrics TEXT AS UserMetrics NOT NULL, + fans INTEGER NOT NULL, + follow INTEGER NOT NULL, + status INTEGER NOT NULL, + listed INTEGER NOT NULL, rawDesc TEXT NOT NULL, htmlDesc TEXT NOT NULL, website TEXT, @@ -20,5 +21,5 @@ CREATE TABLE IF NOT EXISTS User ( verified INTEGER AS Boolean NOT NULL, protected INTEGER AS Boolean NOT NULL, platformType TEXT AS PlatformType NOT NULL, - extra TEXT AS UserExtra + extra TEXT ); \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index 338fe3944..698013f78 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -20,8 +20,7 @@ */ package com.twidere.twiderex.base -import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory -import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory +import com.twidere.twiderex.db.sqldelight.createAppDataBase import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase import org.junit.After import org.junit.Before @@ -31,12 +30,7 @@ internal open class BaseAppDatabaseTest { private val driver = SqlDriverFactory.create(SqlDelightAppDatabase.Schema) @Before fun setUp() { - SqlDelightAppDatabase.Schema.create(driver) - database = SqlDelightAppDatabase( - driver = driver, - draftAdapter = DraftAdapterFactory.create(), - searchAdapter = SearchAdapterFactory.create() - ) + database = createAppDataBase(driver) } @After diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt new file mode 100644 index 000000000..fc61e3414 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt @@ -0,0 +1,41 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.base + +import com.twidere.twiderex.db.sqldelight.createCacheDataBase +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase +import org.junit.After +import org.junit.Before + +internal open class BaseCacheDatabaseTest { + protected lateinit var database: SqlDelightCacheDatabase + private val driver = SqlDriverFactory.create(SqlDelightCacheDatabase.Schema) + @Before + fun setUp() { + database = createCacheDataBase(driver) + } + + @After + fun tearDown() { + database.cacheDropQueries.clearAllTables() + driver.close() + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt new file mode 100644 index 000000000..77456ddf7 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightMediaDaoImpl +import com.twidere.twiderex.db.sqldelight.transform.toDbMedia +import com.twidere.twiderex.mock.model.mockUiMedia +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class SqlDelightMediaDaoImplTest : BaseCacheDatabaseTest() { + @Test + fun returnUiMediaWithGivenBelongToKey() = runBlocking { + val belongToKey = MicroBlogKey.valueOf("test") + val dao = SqlDelightMediaDaoImpl(database.mediaQueries) + database.mediaQueries.insert(mockUiMedia(belongToKey = belongToKey).toDbMedia()) + assert(dao.findMediaByBelongToKey(belongToKey = belongToKey).isNotEmpty()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt similarity index 90% rename from common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt index 8360514c6..b615353ef 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SearchDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.db.dao import com.twidere.twiderex.base.BaseAppDatabaseTest -import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightSearchDaoImpl import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch import kotlinx.coroutines.flow.firstOrNull @@ -30,11 +30,11 @@ import org.junit.Test import java.util.UUID import kotlin.test.assertEquals -internal class SearchDaoImplTest : BaseAppDatabaseTest() { +internal class SqlDelightSearchDaoImplTest : BaseAppDatabaseTest() { @Test fun getAll_ReturnsFlowAndUpdateAfterDbUpdated() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val searchDao = SearchDaoImpl(database.searchQueries) + val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) val flow = searchDao.getAll(accountKey) assert(flow.firstOrNull()?.isEmpty() ?: false) searchDao.insertAll(createSearchList(accountKey = accountKey, count = 1)) @@ -44,7 +44,7 @@ internal class SearchDaoImplTest : BaseAppDatabaseTest() { @Test fun insertAll_InsertAllDataInGivenList() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val searchDao = SearchDaoImpl(database.searchQueries) + val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) val flow = searchDao.getAll(accountKey) assert(flow.firstOrNull()?.isEmpty() ?: false) val count = 10 @@ -55,7 +55,7 @@ internal class SearchDaoImplTest : BaseAppDatabaseTest() { @Test fun getHistories_ReturnsFlowWithNotSavedSearchAndUpdateAfterDbUpdated() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val searchDao = SearchDaoImpl(database.searchQueries) + val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) val flow = searchDao.getAllHistory(accountKey) assert(flow.firstOrNull()?.isEmpty() ?: false) searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey)) @@ -73,7 +73,7 @@ internal class SearchDaoImplTest : BaseAppDatabaseTest() { @Test fun getSaved_ReturnsFlowWithSavedSearchAndUpdateAfterDbUpdated() = runBlocking { val accountKey = MicroBlogKey.twitter("test") - val searchDao = SearchDaoImpl(database.searchQueries) + val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) val flow = searchDao.getAllSaved(accountKey) assert(flow.firstOrNull()?.isEmpty() ?: false) searchDao.insertAll(createSearchList(count = 5, accountKey = accountKey)) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt similarity index 98% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt index 2af1f5221..49332a22e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/DraftQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight +package com.twidere.twiderex.db.sqldelight import com.twidere.twiderex.base.BaseAppDatabaseTest import com.twidere.twiderex.model.enums.ComposeType diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt new file mode 100644 index 000000000..d95b5b9c5 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt @@ -0,0 +1,39 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.db.sqldelight.transform.toDbMedia +import com.twidere.twiderex.mock.model.mockUiMedia +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test + +internal class MediaQueriesImplTest : BaseCacheDatabaseTest() { + @Test + fun insertMediaAndReturnResultWithGivenBelongToKey() = runBlocking { + val belongToKey = MicroBlogKey.valueOf("test") + assert(database.mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().isEmpty()) + database.mediaQueries.insert(mockUiMedia(belongToKey = belongToKey).toDbMedia()) + assert(database.mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().isNotEmpty()) + assert(database.mediaQueries.findMediaByBelongToKey(MicroBlogKey.valueOf("test_not_insert")).executeAsList().isEmpty()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt similarity index 99% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt index 8bd8b8c1f..e348ffc37 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/SearchQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight +package com.twidere.twiderex.db.sqldelight import com.twidere.twiderex.base.BaseAppDatabaseTest import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt new file mode 100644 index 000000000..4a65a4ff7 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.squareup.sqldelight.EnumColumnAdapter +import kotlinx.serialization.Serializable +import org.junit.Test +import kotlin.test.assertEquals + +@Serializable +private enum class JsonEnum { + ENUM_ONE, + ENUM_TWO, + ENUM_THREE +} +class EnumColumnAdapterTest { + + @Test + fun decodeAndEncodeEnumClass() { + val adapter = EnumColumnAdapter() + val origin = JsonEnum.ENUM_TWO + val string = adapter.encode(origin) + val obj = adapter.decode(string) + assertEquals(origin, obj) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt new file mode 100644 index 000000000..6e8d02a57 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import kotlinx.serialization.Serializable +import org.junit.Test +import kotlin.test.assertEquals +@Serializable +private data class JsonObject( + val arg1: String, + val arg2: Int, + val arg3: Boolean +) +class JsonColumnAdapterTest { + + @Test + fun decodeAndEncodeDataClass() { + val adapter = JsonColumnAdapter(JsonObject.serializer()) + val origin = JsonObject(arg1 = "test", arg2 = 2, arg3 = true) + val string = adapter.encode(origin) + val obj = adapter.decode(string) + assertEquals(origin.arg1, obj.arg1) + assertEquals(origin.arg2, obj.arg2) + assertEquals(origin.arg3, obj.arg3) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt similarity index 91% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt index 1489874b7..f02476653 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt @@ -18,9 +18,8 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter -import com.twidere.twiderex.db.sqldelight.adapter.MicroBlogKeyColumnAdapter import com.twidere.twiderex.model.MicroBlogKey import org.junit.Test import kotlin.test.assertEquals diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt similarity index 94% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt index 476a64c13..d6a513f4e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/StringListColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt @@ -18,9 +18,8 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.adapter +package com.twidere.twiderex.db.sqldelight.adapter -import com.twidere.twiderex.db.sqldelight.adapter.StringListColumnAdapter import org.junit.Test import kotlin.test.assertEquals diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt similarity index 93% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt index 9885934c1..c19a2778d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/DraftTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt @@ -18,10 +18,8 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.transform +package com.twidere.twiderex.db.sqldelight.transform -import com.twidere.twiderex.db.sqldelight.transform.toDbDraft -import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.ui.UiDraft @@ -31,7 +29,7 @@ import java.util.UUID import kotlin.test.assertContentEquals import kotlin.test.assertEquals -class DraftTransformTest { +internal class DraftTransformTest { @Test fun draftToUi() { val draft = Draft( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt new file mode 100644 index 000000000..82fe72a1b --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt @@ -0,0 +1,82 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiMedia +import com.twidere.twiderex.sqldelight.table.Media +import org.junit.Test +import kotlin.test.assertEquals + +internal class MediaTransformTest { + @Test + fun mediaToUi() { + val db = Media( + belongToKey = MicroBlogKey.valueOf("test"), + url = "url", + mediaUrl = "mediaUrl", + previewUrl = "previewUrl", + type = MediaType.video, + width = 100, + height = 100, + pageUrl = "pageUrl", + altText = "altText", + orderIndex = 5 + ) + val ui = db.toUi() + assertEquals(db.belongToKey, ui.belongToKey) + assertEquals(db.url, ui.url) + assertEquals(db.mediaUrl, ui.mediaUrl) + assertEquals(db.type, ui.type) + assertEquals(db.width, ui.width) + assertEquals(db.height, ui.height) + assertEquals(db.pageUrl, ui.pageUrl) + assertEquals(db.altText, ui.altText) + assertEquals(db.orderIndex.toInt(), ui.order) + } + + @Test + fun uiToMedia() { + val ui = UiMedia( + belongToKey = MicroBlogKey.valueOf("test"), + url = "url", + mediaUrl = "mediaUrl", + previewUrl = "previewUrl", + type = MediaType.video, + width = 100, + height = 100, + pageUrl = "pageUrl", + altText = "altText", + order = 5 + ) + val db = ui.toDbMedia() + assertEquals(db.belongToKey, ui.belongToKey) + assertEquals(db.url, ui.url) + assertEquals(db.mediaUrl, ui.mediaUrl) + assertEquals(db.type, ui.type) + assertEquals(db.width, ui.width) + assertEquals(db.height, ui.height) + assertEquals(db.pageUrl, ui.pageUrl) + assertEquals(db.altText, ui.altText) + assertEquals(db.orderIndex.toInt(), ui.order) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt similarity index 91% rename from common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt index 900eb8436..eff094b00 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/transform/SearchTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt @@ -18,17 +18,15 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.sqldelight.transform +package com.twidere.twiderex.db.sqldelight.transform -import com.twidere.twiderex.db.sqldelight.transform.toDbSearch -import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.sqldelight.table.Search import org.junit.Test import kotlin.test.assertEquals -class SearchTransformTest { +internal class SearchTransformTest { @Test fun searchToUi() { val search = Search( diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt deleted file mode 100644 index 955357985..000000000 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/sqldelight/adapter/ComposeTypeColumnAdapterTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.sqldelight.adapter - -import com.twidere.twiderex.db.sqldelight.adapter.ComposeTypeColumnAdapter -import com.twidere.twiderex.model.enums.ComposeType -import org.junit.Test -import kotlin.test.assertEquals - -internal class ComposeTypeColumnAdapterTest { - private val adapter = ComposeTypeColumnAdapter() - @Test - fun decode_TypeMatchesToName() { - assertEquals(ComposeType.New, adapter.decode("New")) - assertEquals(ComposeType.Quote, adapter.decode("Quote")) - assertEquals(ComposeType.Reply, adapter.decode("Reply")) - assertEquals(ComposeType.Thread, adapter.decode("Thread")) - } - - @Test - fun encode_NameMatchesToType() { - assertEquals("New", adapter.encode(ComposeType.New)) - assertEquals("Quote", adapter.encode(ComposeType.Quote)) - assertEquals("Reply", adapter.encode(ComposeType.Reply)) - assertEquals("Thread", adapter.encode(ComposeType.Thread)) - } -} diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt index cf00a0662..2f46ab8a1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.dataprovider.db import com.twidere.twiderex.db.AppDatabase -import com.twidere.twiderex.db.sqldelight.dao.SearchDaoImpl import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDraftDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightSearchDaoImpl import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase import kotlinx.coroutines.runBlocking @@ -30,7 +30,7 @@ internal class AppDatabaseImpl(private val database: SqlDelightAppDatabase) : Ap private val draftDao = SqlDelightDraftDaoImpl(database.draftQueries) override fun draftDao() = draftDao - private val searchDao = SearchDaoImpl(database.searchQueries) + private val searchDao = SqlDelightSearchDaoImpl(database.searchQueries) override fun searchDao() = searchDao override suspend fun clearAllTables() { From 8abd23df219c9f44bf9270a75ada55e655e4cb08 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 24 Sep 2021 15:43:59 +0800 Subject: [PATCH 234/615] add unit test for url entity sqlDelight --- .../transform/UrlEntityTransform.kt | 46 +++++++++++++ .../db/sqldelight/MediaQueriesImplTest.kt | 9 +++ .../db/sqldelight/UrlEntityQueriesImplTest.kt | 48 +++++++++++++ .../transform/UrlEntityTransformTest.kt | 67 +++++++++++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 15 ++++- 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt new file mode 100644 index 000000000..1861879b8 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUrlEntity +import com.twidere.twiderex.sqldelight.table.UrlEntity +import java.util.UUID + +fun UiUrlEntity.toDbUrlEntity(belongToKey: MicroBlogKey, id: String = UUID.randomUUID().toString()) = UrlEntity( + id = id, + url = url, + belongToKey = belongToKey, + expandedUrl = expandedUrl, + displayUrl = displayUrl, + title = title, + description = description, + image = image +) + +fun UrlEntity.toUi() = UiUrlEntity( + url = url, + expandedUrl = expandedUrl, + displayUrl = displayUrl, + title = title, + description = description, + image = image +) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt index d95b5b9c5..b5c78ed28 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.mock.model.mockUiMedia import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking import org.junit.Test +import kotlin.test.assertEquals internal class MediaQueriesImplTest : BaseCacheDatabaseTest() { @Test @@ -36,4 +37,12 @@ internal class MediaQueriesImplTest : BaseCacheDatabaseTest() { assert(database.mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().isNotEmpty()) assert(database.mediaQueries.findMediaByBelongToKey(MicroBlogKey.valueOf("test_not_insert")).executeAsList().isEmpty()) } + + @Test + fun insert_ReplaceWhenBelongToKeyAndUrlAndOrderEquals() = runBlocking { + val belongToKey = MicroBlogKey.valueOf("test") + database.mediaQueries.insert(mockUiMedia(url = "url", belongToKey = belongToKey, order = 0).toDbMedia()) + database.mediaQueries.insert(mockUiMedia(url = "url", belongToKey = belongToKey, order = 0).toDbMedia()) + assertEquals(1, database.mediaQueries.findMediaByBelongToKey(belongToKey).executeAsList().size) + } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt new file mode 100644 index 000000000..eb0784286 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt @@ -0,0 +1,48 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.db.sqldelight.transform.toDbUrlEntity +import com.twidere.twiderex.mock.model.mockUiUrlEntity +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class UrlEntityQueriesImplTest : BaseCacheDatabaseTest() { + @Test + fun insertUrlAndReturnResultWithGivenBelongToKey() = runBlocking { + val belongToKey = MicroBlogKey.valueOf("test") + assert(database.urlEntityQueries.findByBelongToKey(belongToKey).executeAsList().isEmpty()) + database.urlEntityQueries.insert(mockUiUrlEntity().toDbUrlEntity(belongToKey = belongToKey)) + assert(database.urlEntityQueries.findByBelongToKey(belongToKey).executeAsList().isNotEmpty()) + assert(database.urlEntityQueries.findByBelongToKey(MicroBlogKey.valueOf("test_not_insert")).executeAsList().isEmpty()) + } + + @Test + fun insert_ReplaceWhenBelongToKeyAndUrlEquals() = runBlocking { + val belongToKey = MicroBlogKey.valueOf("test") + database.urlEntityQueries.insert(mockUiUrlEntity(url = "url").toDbUrlEntity(belongToKey = belongToKey)) + database.urlEntityQueries.insert(mockUiUrlEntity(url = "url").toDbUrlEntity(belongToKey = belongToKey)) + assertEquals(1, database.urlEntityQueries.findByBelongToKey(belongToKey).executeAsList().size) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt new file mode 100644 index 000000000..a2e87e0b8 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt @@ -0,0 +1,67 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.mock.model.mockUiUrlEntity +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.sqldelight.table.UrlEntity +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class UrlEntityTransformTest { + @Test + fun dbToUi() { + val db = UrlEntity( + belongToKey = MicroBlogKey.valueOf("test"), + url = "url", + displayUrl = "displayUrl", + id = UUID.randomUUID().toString(), + expandedUrl = "expandedUrl", + title = "title", + description = "description", + image = "image" + ) + val ui = db.toUi() + assertEquals(db.url, ui.url) + assertEquals(db.url, ui.url) + assertEquals(db.displayUrl, ui.displayUrl) + assertEquals(db.expandedUrl, ui.expandedUrl) + assertEquals(db.title, ui.title) + assertEquals(db.description, ui.description) + } + + @Test + fun uiToDb() { + val belongToKey = MicroBlogKey.valueOf("test") + val id = UUID.randomUUID().toString() + val ui = mockUiUrlEntity(url = "url") + val db = ui.toDbUrlEntity(belongToKey = belongToKey, id = id) + assertEquals(db.url, ui.url) + assertEquals(db.url, ui.url) + assertEquals(db.displayUrl, ui.displayUrl) + assertEquals(db.expandedUrl, ui.expandedUrl) + assertEquals(db.title, ui.title) + assertEquals(db.description, ui.description) + assertEquals(id, db.id) + assertEquals(belongToKey, db.belongToKey) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index fff05b2bd..5091b7fd6 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -54,12 +54,13 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch +import com.twidere.twiderex.model.ui.UiUrlEntity import org.jetbrains.annotations.TestOnly import java.util.Date import java.util.UUID @TestOnly -fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty) = UiMedia( +fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty, order: Int = 0) = UiMedia( url = url, belongToKey = belongToKey, mediaUrl = url, @@ -69,7 +70,7 @@ fun mockUiMedia(url: String = "", belongToKey: MicroBlogKey = MicroBlogKey.Empty height = 100, pageUrl = "", altText = "", - order = 0 + order = order ) @TestOnly @@ -220,3 +221,13 @@ fun mockIDirectMessage(id: String = UUID.randomUUID().toString(), accountId: Str ) ) } + +@TestOnly +fun mockUiUrlEntity(url: String = "") = UiUrlEntity( + url = url, + displayUrl = "displayUrl", + expandedUrl = "expandedUrl", + title = "title", + description = "description", + image = "image" +) From df4bbb265ee54cc778742f8b441648841f24e35b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Sep 2021 18:03:02 +0800 Subject: [PATCH 235/615] fix build --- common/build.gradle.kts | 4 +- .../crud/MemoryCachePagingMediatorTest.kt | 2 +- .../crud/MemoryCachePagingSourceTest.kt | 2 +- .../paging/crud/PagingMemoryCacheTest.kt | 2 +- .../paging/dm/DMConversationMediatorTest.kt | 2 +- .../twiderex/paging/dm/DMEventMediatorTest.kt | 2 +- .../paging/lists/ListMembersMediatorTest.kt | 2 +- .../paging/lists/ListsMediatorTest.kt | 2 +- .../lists/ListsUserPagingMediatorTest.kt | 2 +- .../paging/PagingTimelineMediatorBaseTest.kt | 2 +- .../paging/PagingWithGapMediatorTest.kt | 2 +- .../paging/search/SearchMediaMediatorTest.kt | 2 +- .../paging/search/SearchStatusMediatorTest.kt | 2 +- .../source/FollowersPagingSourceTest.kt | 2 +- .../source/FollowingPagingSourceTest.kt | 2 +- .../ListsSubscribersPagingSourceTest.kt | 2 +- .../source/SearchUserPagingSourceTest.kt | 2 +- .../paging/source/UserPagingSourceTest.kt | 2 +- .../timeline/HomeTimelineMediatorTest.kt | 2 +- .../timeline/MentionTimelineMediatorTest.kt | 2 +- .../NotificationTimelineMediatorTest.kt | 2 +- .../timeline/UserFavoriteMediatorTest.kt | 2 +- .../paging/timeline/UserMediaMediatorTest.kt | 2 +- .../paging/timeline/UserStatusMediatorTest.kt | 2 +- .../paging/trend/TrendMediatorTest.kt | 2 +- .../repository/CacheRepositoryTest.kt | 2 +- .../repository/DirectMessageRepositoryTest.kt | 2 +- .../repository/DraftRepositoryTest.kt | 2 +- .../repository/ListsRepositoryTest.kt | 2 +- .../repository/ListsUsersRepositoryTest.kt | 2 +- .../repository/MediaRepositoryTest.kt | 2 +- .../repository/NotificationRepositoryTest.kt | 2 +- .../repository/ReactionRepositoryTest.kt | 2 +- .../repository/SearchRepositoryTest.kt | 2 +- .../repository/StatusRepositoryTest.kt | 2 +- .../viewmodel/ActiveAccountViewModelTest.kt | 2 +- .../twiderex/viewmodel/DraftViewModelTest.kt | 2 +- .../twiderex/viewmodel/MediaViewModelTest.kt | 2 +- .../viewmodel/PureMediaViewModelTest.kt | 2 +- .../twiderex/viewmodel/StatusViewModelTest.kt | 2 +- .../lists/ListsCreateViewModelTest.kt | 65 ++++++++----------- .../viewmodel/lists/ListsViewModelTest.kt | 2 +- .../viewmodel/user/FollowersViewModelTest.kt | 2 +- .../viewmodel/user/FollowingViewModelTest.kt | 2 +- .../UserFavouriteTimelineViewModelTest.kt | 2 +- .../user/UserMediaTimelineViewModelTest.kt | 2 +- .../user/UserTimelineViewModelTest.kt | 2 +- .../viewmodel/user/UserViewModelTest.kt | 14 ++-- .../precompose/navigation/RouteParserTest.kt | 2 +- 49 files changed, 83 insertions(+), 92 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 245ded2a1..a0ed99003 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -54,7 +54,7 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") - api("io.mockk:mockk:1.12.0") + implementation("io.mockk:mockk:1.11.0") } } val androidMain by getting { @@ -87,13 +87,13 @@ kotlin { implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") implementation("androidx.room:room-testing:${Versions.room}") + implementation("io.mockk:mockk-android:1.11.0") } } val desktopMain by getting { dependencies { } } - val desktopTest by getting } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt index 1b2d41ac3..ac0d6d6ec 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt @@ -25,7 +25,7 @@ import androidx.paging.PagingConfig import androidx.paging.PagingSource import androidx.paging.PagingState import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals class TestMemoryCachePagingMediator(pagingMemoryCache: PagingMemoryCache) : MemoryCachePagingMediator(pagingMemoryCache) { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt index 07dc542de..5ca075c28 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.paging.crud import androidx.paging.PagingSource import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals class MemoryCachePagingSourceTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index 1dfab18af..2794a4fff 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.paging.crud -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals class PagingMemoryCacheTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt index a36d59001..d3531a8aa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage import com.twidere.twiderex.paging.mediator.dm.DMConversationMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test internal class DMConversationMediatorTest { private val accountKey = MicroBlogKey.twitter("123") diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt index 240f17ce9..deb07df50 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.paging.mediator.dm.DMEventMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test /** * instead of testing pagination, we should focus on our code logic diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt index b78532bc2..70d19c604 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.PagingMemoryCache import com.twidere.twiderex.paging.mediator.list.ListsMembersMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class ListMembersMediatorTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index 73c7a5aea..f1d39fe08 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList import com.twidere.twiderex.paging.mediator.list.ListsMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test /** * instead of testing pagination, we should focus on our code logic diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt index 0cc63c40b..06e4b0089 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.paging.crud.PagingMemoryCache import com.twidere.twiderex.paging.mediator.list.ListsUserPagingMediator import kotlinx.coroutines.runBlocking import org.junit.Assert -import org.junit.Test +import kotlin.test.Test /** * instead of testing pagination, we should focus on our code logic diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt index 372a7c52c..93cd2ac11 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.IPagination import com.twidere.twiderex.paging.mediator.paging.PagingTimelineMediatorBase import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class PagingTimelineMediatorBaseTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt index 3e7ce7c34..5e4b8ffac 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt @@ -38,7 +38,7 @@ import com.twidere.twiderex.model.paging.saveToDb import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class PagingWithGapMediatorTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt index b16847466..0d3509f7e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.search.SearchMediaMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test internal class SearchMediaMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt index 4ebe1dc22..048b35877 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test internal class SearchStatusMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt index aae5d1491..64d55466c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import com.twidere.twiderex.mock.service.MockRelationshipService import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class FollowersPagingSourceTest { @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt index 25c732086..b256fe894 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import com.twidere.twiderex.mock.service.MockRelationshipService import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class FollowingPagingSourceTest { @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt index f818d5cf6..15bd798d1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt @@ -24,7 +24,7 @@ import androidx.paging.PagingSource import com.twidere.twiderex.mock.service.MockListsService import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class ListsSubscribersPagingSourceTest { @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt index 9db6336d2..e74a9c41c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.mock.service.MockSearchService import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt index 199a2b704..b52f33007 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.mock.model.toIPaging import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class UserPagingSourceTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt index faf2fa82e..616664a32 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.timeline.HomeTimelineMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class HomeTimelineMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt index bef3f6e4c..fbb2fe97a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.timeline.MentionTimelineMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class MentionTimelineMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt index 78f27a56d..41d50d31b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class NotificationTimelineMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt index 7b63452a8..811ec5420 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.user.UserFavouriteMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class UserFavoriteMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt index b105835e8..3b6bf97eb 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.user.UserMediaMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class UserMediaMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt index b2eae44b0..d00e21634 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus import com.twidere.twiderex.paging.mediator.user.UserStatusMediator import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class UserStatusMediatorTest { @OptIn(ExperimentalPagingApi::class) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt index b21c8e0f8..4ef37b990 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt @@ -34,7 +34,7 @@ import com.twidere.twiderex.model.ui.UiTrend import com.twidere.twiderex.paging.mediator.trend.TrendMediator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test /** * instead of testing pagination, we should focus on our code logic diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt index 0ec8ed405..befa35099 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test class CacheRepositoryTest { @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt index 81f759738..742dc2ee3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.mock.service.MockLookUpService import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt index f43f99dfc..715281be1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class DraftRepositoryTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt index 1f7beeeb8..7d2a50376 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt @@ -26,7 +26,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiList import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt index e4ddb30b4..44a0bf7a9 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt @@ -27,7 +27,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.crud.PagingMemoryCache import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class ListsUsersRepositoryTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt index b2be66a2c..d5eaf4a65 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.mock.db.dao.MockMediaDao import com.twidere.twiderex.mock.model.mockUiMedia import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test internal class MediaRepositoryTest { @Test diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt index 11792d565..7969c1c9f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt @@ -26,7 +26,7 @@ import com.twidere.twiderex.mock.service.MockTimelineService import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.NotificationCursorType import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt index 3568bec7e..f9b417f30 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.model.mockIStatus import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class ReactionRepositoryTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt index 8966feb03..c52d262b1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt @@ -25,7 +25,7 @@ import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.model.MicroBlogKey import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class SearchRepositoryTest { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt index 36d708800..3c75f0639 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.paging.saveToDb import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt index 43002ca8a..1068d9f55 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt @@ -26,7 +26,7 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.verify -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt index 6941957f7..aa48d7855 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt @@ -29,7 +29,7 @@ import io.mockk.verify import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt index 8b22e9213..3d230dcd5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt @@ -32,7 +32,7 @@ import io.mockk.verify import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt index 66fb8260d..f3fcdea17 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt @@ -28,7 +28,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt index 195ab5dfe..60f76f0c3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt @@ -31,7 +31,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt index c88fc3e33..a90986955 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt @@ -20,8 +20,8 @@ */ package com.twidere.twiderex.viewmodel.lists +import app.cash.turbine.test import com.twidere.services.microblog.MicroBlogService -import com.twidere.twiderex.mock.Observer import com.twidere.twiderex.mock.db.MockCacheDatabase import com.twidere.twiderex.mock.service.MockListsService import com.twidere.twiderex.notification.InAppNotification @@ -30,16 +30,14 @@ import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.viewmodel.AccountViewModelTestBase import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.verify -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.time.ExperimentalTime +@OptIn(ExperimentalTime::class) internal class ListsCreateViewModelTest : AccountViewModelTestBase() { override val mockService: MicroBlogService get() = MockListsService() @@ -49,15 +47,10 @@ internal class ListsCreateViewModelTest : AccountViewModelTestBase() { @MockK private lateinit var mockAppNotification: InAppNotification - @MockK - private lateinit var mockLoadingObserver: Observer - private var errorNotification: NotificationEvent? = null private lateinit var createViewModel: ListsCreateViewModel - private val scope = CoroutineScope(Dispatchers.Main) - override fun setUp() { super.setUp() createViewModel = ListsCreateViewModel( @@ -69,41 +62,35 @@ internal class ListsCreateViewModelTest : AccountViewModelTestBase() { errorNotification = arg(0) } errorNotification = null - scope.launch { - createViewModel.loading.collect { - mockLoadingObserver.onChanged(it) - } - } } @Test - fun createList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver) - val result = createViewModel.createList(title = "title", private = false) - assertNotNull(result) - verifySuccessAndLoadingAfter(mockLoadingObserver) + fun createList_successExpectTrue(): Unit = runBlocking { + assertNull(errorNotification) + createViewModel.loading.test { + assert(!awaitItem()) + launch { + val result = createViewModel.createList(title = "title", private = false) + assertNotNull(result) + } + assert(awaitItem()) + assert(!awaitItem()) + } + assertNull(errorNotification) } @Test - fun createList_failedExpectFalseAndShowNotification(): Unit = runBlocking(Dispatchers.Main) { - verifySuccessAndLoadingBefore(mockLoadingObserver) + fun createList_failedExpectFalseAndShowNotification(): Unit = runBlocking { assertNull(errorNotification) - val result = createViewModel.createList(title = "error", private = false) - assertNull(result) - verifySuccessAndLoadingAfter(mockLoadingObserver) + createViewModel.loading.test { + assert(!awaitItem()) + launch { + val result = createViewModel.createList(title = "error", private = false) + assertNull(result) + } + assert(awaitItem()) + assert(!awaitItem()) + } assertNotNull(errorNotification) } - - private fun verifySuccessAndLoadingBefore( - loadingObserver: Observer, - ) { - verify(exactly = 1) { loadingObserver.onChanged(false) } - } - - private fun verifySuccessAndLoadingAfter( - loadingObserver: Observer, - ) { - verify(exactly = 1) { loadingObserver.onChanged(true) } - verify(exactly = 1) { loadingObserver.onChanged(false) } - } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt index 0eb811647..b1ef1836c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class ListsViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt index f3a11d361..b19c6acf2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt @@ -33,7 +33,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull internal class FollowersViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt index abe259c70..3681909f6 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt @@ -33,7 +33,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull internal class FollowingViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt index ea15d1dbc..ad95ca981 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt @@ -33,7 +33,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull internal class UserFavouriteTimelineViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt index 4d87e75fe..aa0d8628f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt @@ -34,7 +34,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull internal class UserMediaTimelineViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt index 7680b461f..4ee4bcfa8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt @@ -33,7 +33,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertNotNull internal class UserTimelineViewModelTest : AccountViewModelTestBase() { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt index a56c479dc..decf47bd3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -34,7 +34,7 @@ import io.mockk.mockk import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.time.ExperimentalTime @@ -93,9 +93,8 @@ internal class UserViewModelTest : AccountViewModelTestBase() { inAppNotification, mockAccount.accountKey ) - viewModel.isMe.firstOrNull().let { - assertNotNull(it) - assert(it) + viewModel.isMe.test { + assert(awaitItem()) } } @@ -128,7 +127,12 @@ internal class UserViewModelTest : AccountViewModelTestBase() { inAppNotification, MicroBlogKey.twitter("321") ) - viewModel.follow() + viewModel.loadingRelationship.test { + assert(!awaitItem()) + viewModel.follow() + assert(awaitItem()) + assert(!awaitItem()) + } viewModel.relationship.test { val item = awaitItem() assertNotNull(item) diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt index 25d64b8ff..1874f90aa 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt @@ -22,7 +22,7 @@ package moe.tlaster.precompose.navigation import moe.tlaster.precompose.navigation.route.SceneRoute import org.junit.Assert -import org.junit.Test +import kotlin.test.Test class RouteParserTest { @Test From fe4d1a869f6ee3596301336c60c4db6355350a56 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 24 Sep 2021 18:18:13 +0800 Subject: [PATCH 236/615] add SqlDelight table/queries for UiUser and add unit test --- .../sqldelight/dao/SqlDelightUserDaoImpl.kt | 53 +++++++++++ .../db/sqldelight/transform/UserTransform.kt | 90 +++++++++++++++++++ .../twiderex/model/ui/UiEmojiCategory.kt | 4 + .../twidere/twiderex/model/ui/UiUrlEntity.kt | 2 + .../model/ui/mastodon/MastodonUserExtra.kt | 3 + .../model/ui/twitter/TwitterUserExtra.kt | 2 + .../twidere/twiderex/sqldelight/table/User.sq | 15 +++- .../db/dao/SqlDelightUserDaoImplTest.kt | 46 ++++++++++ .../db/sqldelight/UserQueriesImplTest.kt | 43 +++++++++ .../sqldelight/transform/UserTransformTest.kt | 90 +++++++++++++++++++ 10 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt new file mode 100644 index 000000000..b56dd6f17 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt @@ -0,0 +1,53 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.dao + +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull +import com.twidere.twiderex.db.dao.UserDao +import com.twidere.twiderex.db.sqldelight.transform.toDbUser +import com.twidere.twiderex.db.sqldelight.transform.toUi +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.sqldelight.table.UserQueries +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class SqlDelightUserDaoImpl(private val userQueries: UserQueries) : UserDao { + override suspend fun findWithUserKey(userKey: MicroBlogKey): UiUser? { + return userQueries.findWithUserKey(userKey).executeAsOneOrNull()?.toUi() + } + + override suspend fun insertAll(listOf: List) { + userQueries.transaction { + listOf.forEach { + userQueries.insert(it.toDbUser()) + } + } + } + + override fun findWithUserKeyFlow(userKey: MicroBlogKey): Flow { + return userQueries.findWithUserKey(userKey) + .asFlow() + .mapToOneOrNull() + .map { it?.toUi() } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt new file mode 100644 index 000000000..3ebd77726 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt @@ -0,0 +1,90 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.model.ui.UserExtra +import com.twidere.twiderex.model.ui.UserMetrics +import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra +import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra +import com.twidere.twiderex.sqldelight.table.User +import com.twidere.twiderex.utils.fromJson +import com.twidere.twiderex.utils.json + +fun UiUser.toDbUser() = User( + id = id, + userKey = userKey, + acct = acct, + name = name, + screenName = screenName, + profileImage = profileImage.toString(), + profileBackgroundImage = profileBackgroundImage, + fans = metrics.fans, + follow = metrics.follow, + status = metrics.status, + listed = metrics.listed, + rawDesc = rawDesc, + htmlDesc = htmlDesc, + website = website, + location = location, + verified = verified, + protected_ = protected, + platformType = platformType, + extra = extra?.toDbUserExtra() +) + +fun UserExtra.toDbUserExtra(): String { + return when (this) { + is TwitterUserExtra -> { json() } + is MastodonUserExtra -> { json() } + else -> toString() + } +} + +fun User.toUi() = UiUser( + id = id, + userKey = userKey, + acct = acct, + name = name, + screenName = screenName, + profileImage = profileImage, + profileBackgroundImage = profileBackgroundImage, + metrics = UserMetrics( + fans = fans, + follow = follow, + status = status, + listed = listed, + ), + rawDesc = rawDesc, + htmlDesc = htmlDesc, + website = website, + location = location, + verified = verified, + protected = protected_, + platformType = platformType, + extra = when (platformType) { + PlatformType.Twitter -> extra?.fromJson() + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> extra?.fromJson() + } +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt index d041c6290..7c7461d34 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt @@ -20,11 +20,15 @@ */ package com.twidere.twiderex.model.ui +import kotlinx.serialization.Serializable + +@Serializable data class UiEmojiCategory( val category: String?, val emoji: List ) +@Serializable data class UiEmoji( val shortcode: String? = null, val url: String? = null, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt index aad89523d..46d0f7abc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt @@ -21,8 +21,10 @@ package com.twidere.twiderex.model.ui import androidx.compose.runtime.Immutable +import kotlinx.serialization.Serializable @Immutable +@Serializable data class UiUrlEntity( val url: String, val expandedUrl: String, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt index b5d00ada8..550bab953 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt @@ -22,7 +22,9 @@ package com.twidere.twiderex.model.ui.mastodon import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.model.ui.UserExtra +import kotlinx.serialization.Serializable +@Serializable data class MastodonUserExtra( val fields: List, val emoji: List, @@ -30,6 +32,7 @@ data class MastodonUserExtra( val locked: Boolean, ) : UserExtra +@Serializable data class Field( val name: String?, val value: String? diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt index 8a51144da..3b61231eb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt @@ -22,7 +22,9 @@ package com.twidere.twiderex.model.ui.twitter import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.model.ui.UserExtra +import kotlinx.serialization.Serializable +@Serializable data class TwitterUserExtra( val pinned_tweet_id: String?, val url: List, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq index 0f49d2289..1da4b4e69 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/User.sq @@ -4,7 +4,7 @@ import java.lang.Boolean; CREATE TABLE IF NOT EXISTS User ( id TEXT NOT NULL, - userKey TEXT AS MicroBlogKey NOT NULL, + userKey TEXT AS MicroBlogKey NOT NULL PRIMARY KEY, acct TEXT AS MicroBlogKey NOT NULL, name TEXT NOT NULL, screenName TEXT NOT NULL, @@ -22,4 +22,15 @@ CREATE TABLE IF NOT EXISTS User ( protected INTEGER AS Boolean NOT NULL, platformType TEXT AS PlatformType NOT NULL, extra TEXT -); \ No newline at end of file +); + +insert: +INSERT OR REPLACE INTO User( + id, userKey, acct, name, screenName, profileImage, + profileBackgroundImage, fans, follow, status, listed, + rawDesc, htmlDesc, website,location, verified, protected, + platformType, extra +) VALUES ?; + +findWithUserKey: +SELECT * FROM User WHERE userKey == :userKey; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt new file mode 100644 index 000000000..00ea3ac9a --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightUserDaoImpl +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +internal class SqlDelightUserDaoImplTest : BaseCacheDatabaseTest() { + @Test + fun findWithUserKeyFlow_FlowUpdatesWhenDatabaseUpdates() = runBlocking { + val dao = SqlDelightUserDaoImpl(database.userQueries) + val user = mockIUser(name = "insert").toUi(MicroBlogKey.twitter("account")) + val flow = dao.findWithUserKeyFlow(user.userKey) + assertNull(flow.firstOrNull()) + dao.insertAll(listOf(user)) + assertEquals("insert", flow.firstOrNull()?.name) + dao.insertAll(listOf(user.copy(name = "replace"))) + assertEquals("replace", flow.firstOrNull()?.name) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt new file mode 100644 index 000000000..d1dd3eb1f --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt @@ -0,0 +1,43 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbUser +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class UserQueriesImplTest : BaseCacheDatabaseTest() { + @Test + fun insert_ReplaceWhenUserKeyEquals() = runBlocking { + val queries = database.userQueries + val accountKey = MicroBlogKey.twitter("account") + val user = mockIUser(name = "insert").toUi(accountKey) + queries.insert(user.toDbUser()) + assertEquals("insert", queries.findWithUserKey(user.userKey).executeAsOneOrNull()?.name) + queries.insert(user.copy(name = "replace").toDbUser()) + assertEquals("replace", queries.findWithUserKey(user.userKey).executeAsOneOrNull()?.name) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt new file mode 100644 index 000000000..0b4c4979c --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt @@ -0,0 +1,90 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.mock.model.mockUiUrlEntity +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.PlatformType +import com.twidere.twiderex.model.ui.UiUser +import com.twidere.twiderex.model.ui.UserMetrics +import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra +import com.twidere.twiderex.sqldelight.table.User +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class UserTransformTest { + @Test + fun transform() { + val ui = UiUser( + userKey = MicroBlogKey.valueOf("userKey@twitter.com"), + id = UUID.randomUUID().toString(), + acct = MicroBlogKey.valueOf("acct"), + name = "name", + screenName = "screenName", + profileImage = "profileImage", + profileBackgroundImage = "profileBackgroundImage", + metrics = UserMetrics( + fans = 1, + follow = 2, + status = 3, + listed = 4 + ), + rawDesc = "rawDesc", + htmlDesc = "htmlDesc", + website = "website", + location = "location", + verified = true, + protected = false, + platformType = PlatformType.Twitter, + extra = TwitterUserExtra( + pinned_tweet_id = "pinned_tweet_id", + url = listOf(mockUiUrlEntity()) + ) + ) + val db = ui.toDbUser() + assertSuccess(db, ui) + + val uiFromDb = db.toUi() + assertSuccess(db, uiFromDb) + } + + private fun assertSuccess(db: User, ui: UiUser) { + assertEquals(db.userKey, ui.userKey) + assertEquals(db.id, ui.id) + assertEquals(db.acct, ui.acct) + assertEquals(db.name, ui.name) + assertEquals(db.screenName, ui.screenName) + assertEquals(db.profileImage, ui.profileImage) + assertEquals(db.profileBackgroundImage, ui.profileBackgroundImage) + assertEquals(db.fans, ui.metrics.fans) + assertEquals(db.follow, ui.metrics.follow) + assertEquals(db.status, ui.metrics.status) + assertEquals(db.listed, ui.metrics.listed) + assertEquals(db.rawDesc, ui.rawDesc) + assertEquals(db.htmlDesc, ui.htmlDesc) + assertEquals(db.location, ui.location) + assertEquals(db.verified, ui.verified) + assertEquals(db.protected_, ui.protected) + assertEquals(db.platformType, ui.platformType) + assertEquals(db.extra, ui.extra?.toDbUserExtra()) + } +} From 26290f18b71f4230246b76d79813ed9d61bafd3b Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 24 Sep 2021 21:26:39 +0800 Subject: [PATCH 237/615] fix android player --- .../com/twidere/twiderex/scenes/MediaScene.kt | 1 - .../foundation/AndroidVideoPlayer.kt | 25 +++++---- .../component/foundation/VideoPlayer.kt | 21 +++++--- .../utils/video/CustomVideoControl.kt | 51 +++++++------------ .../{ic_volume_unmute.svg => ic_volume.svg} | 0 .../foundation/DesktopVideoPlayer.kt | 8 +-- 6 files changed, 49 insertions(+), 57 deletions(-) rename common/src/commonMain/resources/MR/files/svg/{ic_volume_unmute.svg => ic_volume.svg} (100%) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index f25ebce75..f16f5c0d1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -63,7 +63,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt index 0fac6e4b9..9becbeb46 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/AndroidVideoPlayer.kt @@ -41,6 +41,7 @@ import com.twidere.twiderex.preferences.LocalHttpConfig import com.twidere.twiderex.utils.video.CacheDataSourceFactory import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -109,16 +110,21 @@ actual class NativePlayerView actual constructor( addListener(object : Player.Listener { override fun onPlaybackStateChanged(state: Int) { playerCallBack?.showThumb(state != Player.STATE_READY) + if (state == Player.STATE_READY) { + playerCallBack?.onprepare() + } } override fun onIsPlayingChanged(isPlaying: Boolean) { playerCallBack?.setPlaying(isPlaying) job?.cancel() if (isPlaying) { - job = CoroutineScope(EmptyCoroutineContext).launch { + job = CoroutineScope(EmptyCoroutineContext).launch( + Dispatchers.Main + ) { while (true) { delay(1000) - playerProgressCallBack?.onTimeChanged(contentPosition) + playerProgressCallBack?.onTimeChanged(contentPosition()) } } } @@ -141,6 +147,10 @@ actual class NativePlayerView actual constructor( private fun realPlayerView() = player as StyledPlayerView actual var playWhenReady: Boolean = false + set(value) { + realPlayerView().player?.playWhenReady = value + field = value + } actual fun resume() { realPlayerView().onResume() @@ -150,12 +160,13 @@ actual class NativePlayerView actual constructor( realPlayerView().onPause() } - actual fun contentPosition(): Long = realPlayerView().player?.contentPosition ?: 0 + actual fun contentPosition(): Long = 0L.coerceAtLeast((realPlayerView().player?.currentPosition) ?: 0) actual fun update() { } actual fun setVolume(volume: Float) { + realPlayerView().player?.volume = volume } actual fun release() { @@ -170,12 +181,8 @@ actual class NativePlayerView actual constructor( realPlayerView().player?.seekTo(time) } - actual fun mute() { - realPlayerView().player?.volume = 0f - } - - actual fun unMute() { - realPlayerView().player?.volume = 1f + actual fun setMute(mute: Boolean) { + realPlayerView().player?.volume = if (mute) 0f else 1f } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index d96eb2cc8..89345c2d1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -23,11 +23,11 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.LocalContentAlpha import androidx.compose.material.MaterialTheme @@ -51,6 +51,7 @@ import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalVideoPlayback +import com.twidere.twiderex.utils.video.CustomVideoControl import com.twidere.twiderex.utils.video.VideoPool import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -106,11 +107,10 @@ fun VideoPlayer( mediaPrepared = true } } + setVolume(volume) } } - nativePlayerView.setVolume(volume) - fun updateState() { autoPlay = nativePlayerView.playWhenReady VideoPool.set(url, 0L.coerceAtLeast(nativePlayerView.contentPosition())) @@ -155,6 +155,9 @@ fun VideoPlayer( var isMostCenter by remember(url) { mutableStateOf(false) } + var playEnabled by remember(url) { + mutableStateOf(true) + } var debounceJob: Job? = null Column( modifier = Modifier.onGloballyPositioned { coordinates -> @@ -187,7 +190,7 @@ fun VideoPlayer( nativePLayerView = nativePlayerView, modifier = modifier, ) { - if (isResume && isMostCenter) { + if (isResume && isMostCenter && playEnabled) { if (isListItem) { it.playWhenReady = autoPlay } @@ -200,8 +203,11 @@ fun VideoPlayer( } } if (mediaPrepared) { - Divider(Modifier.height(30.dp)) - customControl?.invoke(nativePlayerView) + Spacer(Modifier.height(30.dp)) + // customControl?.invoke(nativePlayerView) + CustomVideoControl(nativePlayerView) { + playEnabled = it + } } } } @@ -265,8 +271,7 @@ expect class NativePlayerView( fun seekTo(time: Long) fun update() fun setVolume(volume: Float) - fun mute() - fun unMute() + fun setMute(mute: Boolean) fun release() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt index 758f30da0..edebbca63 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -38,13 +39,15 @@ import com.twidere.twiderex.component.foundation.PlayerProgressCallBack import com.twidere.twiderex.component.foundation.SeekBar import com.twidere.twiderex.component.foundation.SeekBarState import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource @Composable fun CustomVideoControl( player: NativePlayerView, playing: Boolean = true, mute: Boolean = false, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onPlayPause: ((Boolean) -> Unit)? = null ) { val seekBarState = remember { SeekBarState( @@ -74,25 +77,16 @@ fun CustomVideoControl( IconButton( onClick = { - if (!isPlaying) { - player.resume() - } else { - player.pause() + isPlaying = (!isPlaying).apply { + onPlayPause?.invoke(this) } - isPlaying = !isPlaying }, ) { - if (isPlaying) { - Icon( - painter = painterResource(res = MR.files.ic_player_pause), - "" - ) - } else { - Icon( - painter = painterResource(res = MR.files.ic_player_play), - "" - ) - } + Icon( + painter = painterResource(res = if (isPlaying) MR.files.ic_player_pause else MR.files.ic_player_play), + contentDescription = stringResource(res = MR.strings.accessibility_common_video_play), + tint = MaterialTheme.colors.onSurface + ) } Box(modifier.weight(1f)) { @@ -103,25 +97,16 @@ fun CustomVideoControl( IconButton( onClick = { - if (!isMute) { - player.mute() - } else { - player.unMute() + isMute = (!isMute).apply { + player.setMute(this) } - isMute = !isMute }, ) { - if (isMute) { - Icon( - painter = painterResource(res = MR.files.ic_volume_mute), - "" - ) - } else { - Icon( - painter = painterResource(res = MR.files.ic_volume_unmute), - "" - ) - } + Icon( + painter = painterResource(res = if (isMute) MR.files.ic_volume_mute else MR.files.ic_volume), + contentDescription = stringResource(res = MR.strings.accessibility_common_video_play), + tint = MaterialTheme.colors.onSurface + ) } } } diff --git a/common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg b/common/src/commonMain/resources/MR/files/svg/ic_volume.svg similarity index 100% rename from common/src/commonMain/resources/MR/files/svg/ic_volume_unmute.svg rename to common/src/commonMain/resources/MR/files/svg/ic_volume.svg diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt index 33604093e..584094ecb 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopVideoPlayer.kt @@ -102,12 +102,8 @@ actual class NativePlayerView actual constructor( player.mediaPlayer().controls().setTime(time) } - actual fun mute() { - player.mediaPlayer().audio().isMute = true - } - - actual fun unMute() { - player.mediaPlayer().audio().isMute = false + actual fun setMute(mute: Boolean) { + player.mediaPlayer().audio().isMute = mute } } From f45d5e67f78b15ca4e177e5f5cefee79c3c077e0 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sun, 26 Sep 2021 16:23:01 +0800 Subject: [PATCH 238/615] add queries for table DMEvent and add unit test --- .../SqlDelightDirectMessageEventDaoImpl.kt | 53 +++++++- .../model/DbDMEventWithAttachments.kt | 33 +++++ .../sqldelight/transform/DMEventTransform.kt | 65 ++++++++++ .../twiderex/sqldelight/table/DMEvent.sq | 9 ++ .../SqlDelightDirectMessageEventDaoImpl.kt | 57 +++++++++ .../transform/DMEventQueriesImplTest.kt | 121 ++++++++++++++++++ .../transform/DMEventTransformTest.kt | 79 ++++++++++++ 7 files changed, 410 insertions(+), 7 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index 158a298da..5e236b991 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -22,10 +22,23 @@ package com.twidere.twiderex.db.sqldelight.dao import androidx.paging.PagingSource import com.twidere.twiderex.db.dao.DirectMessageEventDao +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments +import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.sqldelight.table.DMEvent +import com.twidere.twiderex.sqldelight.table.DMEventQueries +import com.twidere.twiderex.sqldelight.table.MediaQueries +import com.twidere.twiderex.sqldelight.table.UrlEntityQueries +import com.twidere.twiderex.sqldelight.table.UserQueries -internal class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { +internal class SqlDelightDirectMessageEventDaoImpl( + private val dmEventQueries: DMEventQueries, + private val urlEntityQueries: UrlEntityQueries, + private val mediaQueries: MediaQueries, + private val userQueries: UserQueries +) : DirectMessageEventDao { override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey @@ -37,22 +50,48 @@ internal class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { accountKey: MicroBlogKey, conversationKey: MicroBlogKey, messageKey: MicroBlogKey - ): UiDMEvent? { - TODO("Not yet implemented") - } + ): UiDMEvent? = dmEventQueries.findWithMessageKey( + accountKey = accountKey, + conversationKey = conversationKey, + messageKey = messageKey + ).executeAsOneOrNull()?.withAttachments()?.toUi() override suspend fun delete(message: UiDMEvent) { - TODO("Not yet implemented") + dmEventQueries.delete( + accountKey = message.accountKey, + conversationKey = message.conversationKey, + messageKey = message.messageKey + ) } override suspend fun getMessageCount( accountKey: MicroBlogKey, conversationKey: MicroBlogKey ): Long { - TODO("Not yet implemented") + return dmEventQueries.getMessageCount(accountKey = accountKey, conversationKey = conversationKey).executeAsOne() } override suspend fun insertAll(events: List) { - TODO("Not yet implemented") + dmEventQueries.transaction { + events.map { it.toDbEventWithAttachments() }.let { list -> + list.forEach { + dmEventQueries.insert(it.event) + it.media.forEach { media -> mediaQueries.insert(media) } + userQueries.insert(it.sender) + it.url.forEach { url -> urlEntityQueries.insert(url) } + } + } + } + } + + private fun DMEvent.withAttachments(): DbDMEventWithAttachments { + return dmEventQueries.transactionWithResult { + DbDMEventWithAttachments( + event = this@withAttachments, + url = urlEntityQueries.findByBelongToKey(messageKey).executeAsList(), + media = mediaQueries.findMediaByBelongToKey(messageKey).executeAsList(), + sender = userQueries.findWithUserKey(senderAccountKey).executeAsOne() + ) + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt new file mode 100644 index 000000000..22c887331 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.model + +import com.twidere.twiderex.sqldelight.table.DMEvent +import com.twidere.twiderex.sqldelight.table.Media +import com.twidere.twiderex.sqldelight.table.UrlEntity +import com.twidere.twiderex.sqldelight.table.User + +internal data class DbDMEventWithAttachments( + val event: DMEvent, + val media: List, + val url: List, + val sender: User +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt new file mode 100644 index 000000000..766944ef3 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt @@ -0,0 +1,65 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.model.ui.UiDMEvent +import com.twidere.twiderex.sqldelight.table.DMEvent +import java.util.UUID + +internal fun UiDMEvent.toDbEventWithAttachments(dbId: String = UUID.randomUUID().toString()) = DbDMEventWithAttachments( + event = DMEvent( + id = dbId, + accountKey = accountKey, + sortId = sortId, + conversationKey = conversationKey, + messageId = messageId, + messageKey = messageKey, + htmlText = htmlText, + originText = originText, + createdTimestamp = createdTimestamp, + messageType = messageType, + senderAccountKey = senderAccountKey, + recipientAccountKey = recipientAccountKey, + sendStatus = sendStatus + ), + media = media.map { it.toDbMedia() }, + url = urlEntity.map { it.toDbUrlEntity(messageKey) }, + sender = sender.toDbUser() +) + +internal fun DbDMEventWithAttachments.toUi() = UiDMEvent( + accountKey = event.accountKey, + sortId = event.sortId, + conversationKey = event.conversationKey, + messageId = event.messageId, + messageKey = event.messageKey, + htmlText = event.htmlText, + originText = event.originText, + createdTimestamp = event.createdTimestamp, + messageType = event.messageType, + senderAccountKey = event.senderAccountKey, + recipientAccountKey = event.recipientAccountKey, + sendStatus = event.sendStatus, + media = media.map { it.toUi() }, + urlEntity = url.map { it.toUi() }, + sender = sender.toUi() +) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq index be8d0a578..ec736ed5f 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq @@ -26,3 +26,12 @@ INSERT OR REPLACE INTO DMEvent( messageKey, htmlText, originText, createdTimestamp, messageType, senderAccountKey, recipientAccountKey, sendStatus ) VALUES ?; + +findWithMessageKey: +SELECT * FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey AND messageKey == :messageKey; + +delete: +DELETE FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey AND messageKey == :messageKey; + +getMessageCount: +SELECT COUNT(*) FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt new file mode 100644 index 000000000..eb33cf008 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -0,0 +1,57 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent + +class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { + override fun getPagingSource( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): PagingSource { + TODO("Not yet implemented") + } + + override suspend fun findWithMessageKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey, + messageKey: MicroBlogKey + ): UiDMEvent? { + TODO("Not yet implemented") + } + + override suspend fun delete(message: UiDMEvent) { + TODO("Not yet implemented") + } + + override suspend fun getMessageCount( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Long { + TODO("Not yet implemented") + } + + override suspend fun insertAll(events: List) { + TODO("Not yet implemented") + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt new file mode 100644 index 000000000..fdf929243 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt @@ -0,0 +1,121 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class DMEventQueriesImplTest : BaseCacheDatabaseTest() { + @Test + fun insert_ReplaceWhenUniqueIndexEquals() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val insert = mockIDirectMessage(accountId = accountKey.id, inCome = false).toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + database.dMEventQueries.insert(insert.copy(htmlText = "insert").toDbEventWithAttachments().event) + assertEquals( + "insert", + database.dMEventQueries.findWithMessageKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ).executeAsOneOrNull()?.htmlText + ) + + database.dMEventQueries.insert(insert.copy(htmlText = "replace").toDbEventWithAttachments().event) + + assertEquals( + "replace", + database.dMEventQueries.findWithMessageKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ).executeAsOneOrNull()?.htmlText + ) + } + + @Test + fun delete_DeleteWithUniqueIndex() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val insert = mockIDirectMessage(accountId = accountKey.id, inCome = false).toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + database.dMEventQueries.insert(insert.toDbEventWithAttachments().event) + assertNotNull( + database.dMEventQueries.findWithMessageKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ).executeAsOneOrNull() + ) + database.dMEventQueries.delete( + accountKey = insert.accountKey, + conversationKey = MicroBlogKey.twitter("random"), + messageKey = MicroBlogKey.twitter("random"), + ) + database.dMEventQueries.delete( + accountKey = MicroBlogKey.twitter("random"), + conversationKey = insert.conversationKey, + messageKey = MicroBlogKey.twitter("random"), + ) + database.dMEventQueries.delete( + accountKey = MicroBlogKey.twitter("random"), + conversationKey = MicroBlogKey.twitter("random"), + messageKey = insert.messageKey, + ) + assertNotNull( + database.dMEventQueries.findWithMessageKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ).executeAsOneOrNull() + ) + database.dMEventQueries.delete( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ) + assertNull( + database.dMEventQueries.findWithMessageKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + messageKey = insert.messageKey + ).executeAsOneOrNull() + ) + } + + @Test + fun getMessageCount_ReturnsCountMatchesAccountKeyAndConversationKey() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val insert = mockIDirectMessage(accountId = accountKey.id, inCome = false).toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("1")).toDbEventWithAttachments().event) + database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("2")).toDbEventWithAttachments().event) + database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("3")).toDbEventWithAttachments().event) + + assertEquals(3, database.dMEventQueries.getMessageCount(accountKey = accountKey, conversationKey = insert.conversationKey).executeAsOne()) + + assertEquals(0, database.dMEventQueries.getMessageCount(accountKey = MicroBlogKey.twitter("empty"), conversationKey = insert.conversationKey).executeAsOne()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt new file mode 100644 index 000000000..df6b554b1 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt @@ -0,0 +1,79 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.mockUiMedia +import com.twidere.twiderex.mock.model.mockUiUrlEntity +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMEvent +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class DMEventTransformTest { + @Test + fun transform() { + val ui = UiDMEvent( + accountKey = MicroBlogKey.twitter("account"), + sender = mockIUser().toUi(MicroBlogKey.twitter("sender")), + sortId = System.currentTimeMillis(), + conversationKey = MicroBlogKey.twitter("conversation"), + messageId = System.currentTimeMillis().toString(), + messageKey = MicroBlogKey.twitter("messageKey"), + htmlText = "htmlText", + originText = "originText", + createdTimestamp = System.currentTimeMillis(), + messageType = "create", + senderAccountKey = MicroBlogKey.twitter("sender"), + recipientAccountKey = MicroBlogKey.twitter("recipient"), + sendStatus = UiDMEvent.SendStatus.SUCCESS, + media = listOf(mockUiMedia()), + urlEntity = listOf(mockUiUrlEntity()) + ) + val id = UUID.randomUUID().toString() + val db = ui.toDbEventWithAttachments(dbId = id) + assertSuccess(db, ui) + + val uiFromDb = db.toUi() + assertSuccess(db, uiFromDb) + } + + private fun assertSuccess(db: DbDMEventWithAttachments, ui: UiDMEvent) { + assertEquals(db.event.accountKey, ui.accountKey) + assertEquals(db.sender.userKey, ui.sender.userKey) + assertEquals(db.event.sortId, ui.sortId) + assertEquals(db.event.conversationKey, ui.conversationKey) + assertEquals(db.event.messageId, ui.messageId) + assertEquals(db.event.messageKey, ui.messageKey) + assertEquals(db.event.htmlText, ui.htmlText) + assertEquals(db.event.originText, ui.originText) + assertEquals(db.event.createdTimestamp, ui.createdTimestamp) + assertEquals(db.event.messageType, ui.messageType) + assertEquals(db.event.senderAccountKey, ui.senderAccountKey) + assertEquals(db.event.recipientAccountKey, ui.recipientAccountKey) + assertEquals(db.event.sendStatus, ui.sendStatus) + assertEquals(db.media.first().url, ui.media.first().url) + assertEquals(db.url.first().url, ui.urlEntity.first().url) + } +} From 0ab8c1b4be7e8b8f9f2872361cfced7795ff0ef8 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sun, 26 Sep 2021 17:30:01 +0800 Subject: [PATCH 239/615] update after and before test annotation --- .../kotlin/com/twidere/twiderex/MainThreadTestBase.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt index 51afdeaa3..b6fb938f3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt @@ -26,19 +26,20 @@ import kotlinx.coroutines.ObsoleteCoroutinesApi import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Before +import kotlin.test.AfterTest +import kotlin.test.BeforeTest + @OptIn(ExperimentalCoroutinesApi::class) internal open class MainThreadTestBase { @OptIn(ObsoleteCoroutinesApi::class) private val mainThreadSurrogate = newSingleThreadContext("UI thread") - @Before + @BeforeTest open fun setUp() { Dispatchers.setMain(mainThreadSurrogate) } - @After + @AfterTest open fun tearDown() { Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher mainThreadSurrogate.close() From 2beda7f2eae062756ca535d419a9fe8dacd9d85a Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sun, 26 Sep 2021 17:30:36 +0800 Subject: [PATCH 240/615] remove common test from android connected check --- common/build.gradle.kts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index a0ed99003..b56c0d5b5 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -54,7 +54,7 @@ kotlin { implementation(kotlin("test")) implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") - implementation("io.mockk:mockk:1.11.0") + implementation("io.mockk:mockk:1.12.0") } } val androidMain by getting { @@ -80,14 +80,12 @@ kotlin { } val androidAndroidTest by getting { dependencies { - implementation(kotlin("test-junit")) implementation("androidx.arch.core:core-testing:2.1.0") implementation("androidx.test:core:${Versions.androidx_test}") implementation("androidx.test:runner:${Versions.androidx_test}") implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") implementation("androidx.room:room-testing:${Versions.room}") - implementation("io.mockk:mockk-android:1.11.0") } } val desktopMain by getting { @@ -117,6 +115,7 @@ android { minSdk = AndroidSdk.min targetSdk = AndroidSdk.target testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments["notPackage"] = "com.twidere.twiderex.viewmodel" javaCompileOptions { annotationProcessorOptions { From 0c06d249afa23137c8cf8064128cf3110477019d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sun, 26 Sep 2021 17:46:16 +0800 Subject: [PATCH 241/615] remove unused assisted processor --- android/build.gradle.kts | 2 - assistedProcessor/.gitignore | 1 - assistedProcessor/build.gradle.kts | 16 --- .../main/kotlin/AssistedViewModelProcessor.kt | 112 ------------------ ...ols.ksp.processing.SymbolProcessorProvider | 1 - settings.gradle.kts | 2 +- 6 files changed, 1 insertion(+), 133 deletions(-) delete mode 100644 assistedProcessor/.gitignore delete mode 100644 assistedProcessor/build.gradle.kts delete mode 100644 assistedProcessor/src/main/kotlin/AssistedViewModelProcessor.kt delete mode 100644 assistedProcessor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 6888dd91f..44a624ee1 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -152,7 +152,6 @@ android { } // TODO: workaround for https://github.com/google/ksp/issues/518 -evaluationDependsOn(":assistedProcessor") evaluationDependsOn(":routeProcessor") dependencies { @@ -161,7 +160,6 @@ dependencies { kotlinCoroutines() implementation(projects.services) implementation(projects.common) - // ksp(projects.assistedProcessor) implementation(projects.routeProcessor) ksp(projects.routeProcessor) compose() diff --git a/assistedProcessor/.gitignore b/assistedProcessor/.gitignore deleted file mode 100644 index 42afabfd2..000000000 --- a/assistedProcessor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/assistedProcessor/build.gradle.kts b/assistedProcessor/build.gradle.kts deleted file mode 100644 index 17926e017..000000000 --- a/assistedProcessor/build.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -plugins { - kotlin("jvm") -} - -repositories { - mavenCentral() - google() -} - -dependencies { - kspApi() -} - -sourceSets.main { - java.srcDirs("src/main/kotlin") -} diff --git a/assistedProcessor/src/main/kotlin/AssistedViewModelProcessor.kt b/assistedProcessor/src/main/kotlin/AssistedViewModelProcessor.kt deleted file mode 100644 index 13957cf78..000000000 --- a/assistedProcessor/src/main/kotlin/AssistedViewModelProcessor.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.assisted - -import com.google.devtools.ksp.processing.CodeGenerator -import com.google.devtools.ksp.processing.Dependencies -import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.processing.SymbolProcessorEnvironment -import com.google.devtools.ksp.processing.SymbolProcessorProvider -import com.google.devtools.ksp.symbol.KSAnnotated -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSNode -import com.google.devtools.ksp.visitor.KSEmptyVisitor -import java.io.OutputStream - -private fun OutputStream.appendText(str: String) { - this.write(str.toByteArray()) -} - -class AssistedViewModelProcessor( - val codeGenerator: CodeGenerator, -) : SymbolProcessor { - - override fun process(resolver: Resolver): List { - val factorySymbol = - resolver.getSymbolsWithAnnotation("dagger.assisted.AssistedFactory") - val holder = - resolver.getSymbolsWithAnnotation("dagger.hilt.android.AndroidEntryPoint") - val factory = factorySymbol.filterIsInstance().toList() - holder.forEach { it.accept(HolderVisitor(), factory) } - return emptyList() - } - - inner class HolderVisitor : KSEmptyVisitor, Unit>() { - override fun defaultHandler(node: KSNode, data: List) { - if (node !is KSClassDeclaration) { - return - } - val packageName = node.packageName.asString() - val className = "${node.qualifiedName?.getShortName()}AssistedViewModelHolder" - codeGenerator.createNewFile( - Dependencies( - true, - *(data.mapNotNull { it.containingFile } + listOfNotNull(node.containingFile)).toTypedArray() - ), - packageName, - className - ).use { outputStream -> - outputStream.appendText("package $packageName${System.lineSeparator()}") - outputStream.appendText(System.lineSeparator()) - outputStream.appendText("public class $className @javax.inject.Inject constructor(${System.lineSeparator()}") - - data.forEach { - val name = it.qualifiedName?.asString() ?: "" - outputStream.appendText( - " ${ - name.replace( - ".", - "_" - ) - }: $name,${System.lineSeparator()}" - ) - } - - outputStream.appendText(") {${System.lineSeparator()}") - - outputStream.appendText(" val factory = listOf(${System.lineSeparator()}") - data.forEach { - val name = it.qualifiedName?.asString() ?: "" - outputStream.appendText( - " ${ - name.replace( - ".", - "_" - ) - },${System.lineSeparator()}" - ) - } - outputStream.appendText(" )${System.lineSeparator()}") - - outputStream.appendText("}${System.lineSeparator()}") - } - } - } -} - -class AssistedViewModelProcessorProvider : SymbolProcessorProvider { - override fun create( - environment: SymbolProcessorEnvironment - ): SymbolProcessor { - return AssistedViewModelProcessor(environment.codeGenerator) - } -} diff --git a/assistedProcessor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/assistedProcessor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider deleted file mode 100644 index b9e0a6ec1..000000000 --- a/assistedProcessor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ /dev/null @@ -1 +0,0 @@ -com.twidere.assisted.AssistedViewModelProcessorProvider diff --git a/settings.gradle.kts b/settings.gradle.kts index 32fd2202b..5671efc4b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ pluginManagement { } rootProject.name = "TwidereX" -include(":android", ":services", ":common", ":desktop", ":assistedProcessor", ":routeProcessor") +include(":android", ":services", ":common", ":desktop", ":routeProcessor") enableFeaturePreview("VERSION_CATALOGS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") From 48f365a2ba93ba0b911bf81164f430a4d3eaeaad Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sun, 26 Sep 2021 18:05:37 +0800 Subject: [PATCH 242/615] add flatmap extension for sqldelight query --- .../SqlDelightDirectMessageEventDaoImpl.kt | 21 +++++++++++++++++-- .../db/sqldelight/query/QueryExtensions.kt | 16 ++++++++++++++ ...qlDelightDirectMessageEventDaoImplTest.kt} | 2 +- .../{transform => }/DMEventQueriesImplTest.kt | 3 ++- 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt rename common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/{SqlDelightDirectMessageEventDaoImpl.kt => SqlDelightDirectMessageEventDaoImplTest.kt} (97%) rename common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/{transform => }/DMEventQueriesImplTest.kt (97%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index 5e236b991..d4421b1ff 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -21,8 +21,10 @@ package com.twidere.twiderex.db.sqldelight.dao import androidx.paging.PagingSource +import com.squareup.sqldelight.android.paging3.QueryPagingSource import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.db.sqldelight.query.flatMap import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey @@ -32,6 +34,7 @@ import com.twidere.twiderex.sqldelight.table.DMEventQueries import com.twidere.twiderex.sqldelight.table.MediaQueries import com.twidere.twiderex.sqldelight.table.UrlEntityQueries import com.twidere.twiderex.sqldelight.table.UserQueries +import kotlinx.coroutines.Dispatchers internal class SqlDelightDirectMessageEventDaoImpl( private val dmEventQueries: DMEventQueries, @@ -42,8 +45,22 @@ internal class SqlDelightDirectMessageEventDaoImpl( override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey - ): PagingSource { - TODO("Not yet implemented") + ): PagingSource { + return QueryPagingSource( + countQuery = dmEventQueries.getMessageCount(accountKey = accountKey, conversationKey = conversationKey), + transacter = dmEventQueries, + dispatcher = Dispatchers.IO, + queryProvider = {limit, offset -> + dmEventQueries.getMessagesByConversation( + accountKey = accountKey, + conversationKey = conversationKey, + limit = limit, + offset = offset + ).flatMap { + it.withAttachments().toUi() + } + } + ) } override suspend fun findWithMessageKey( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt new file mode 100644 index 000000000..17ed643a0 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt @@ -0,0 +1,16 @@ +package com.twidere.twiderex.db.sqldelight.query + +import com.squareup.sqldelight.Query +import com.squareup.sqldelight.internal.copyOnWriteList + +internal fun Query.flatMap(map: (T) -> R):Query { + return object : Query( + queries = copyOnWriteList(), + mapper = { + val db = mapper.invoke(it) + map.invoke(db) + } + ) { + override fun execute() = this@flatMap.execute() + } +} \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt similarity index 97% rename from common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt index eb33cf008..2c7b1b843 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt @@ -28,7 +28,7 @@ class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey - ): PagingSource { + ): PagingSource { TODO("Not yet implemented") } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt similarity index 97% rename from common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt rename to common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt index fdf929243..7f6f0ecbb 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt @@ -18,10 +18,11 @@ * You should have received a copy of the GNU General Public License * along with Twidere X. If not, see . */ -package com.twidere.twiderex.db.sqldelight.transform +package com.twidere.twiderex.db.sqldelight import com.twidere.twiderex.base.BaseCacheDatabaseTest import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments import com.twidere.twiderex.mock.model.mockIDirectMessage import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.model.MicroBlogKey From fae673074f6aaa3da46453e5aa9cdda3f8f7579f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sun, 26 Sep 2021 18:29:24 +0800 Subject: [PATCH 243/615] fix ListsModifyViewModelTest scope --- common/build.gradle.kts | 1 + .../twiderex/viewmodel/lists/ListsModifyViewModelTest.kt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b56c0d5b5..dee1bd929 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -35,6 +35,7 @@ kotlin { api(compose.foundation) api(compose.material) implementation(projects.services) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}") api("androidx.paging:paging-common:${Versions.paging}") api("androidx.datastore:datastore-core:${Versions.datastore}") api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt index 25ef55ca1..5a42263c5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt @@ -61,7 +61,9 @@ internal class ListsModifyViewModelTest : AccountViewModelTestBase() { private lateinit var modifyViewModel: ListsModifyViewModel - private val scope = CoroutineScope(Dispatchers.Main) + private val scope by lazy { + CoroutineScope(Dispatchers.Main) + } @Test fun updateList_successExpectTrue(): Unit = runBlocking(Dispatchers.Main) { From 3e305be47662c63577c9141d7264a08bb668a6af Mon Sep 17 00:00:00 2001 From: itsMimao Date: Sun, 26 Sep 2021 19:02:24 +0800 Subject: [PATCH 244/615] add paging support for sqldelight and add test for SqlDelightDirectMessageDaoImpl --- .../SqlDelightDirectMessageEventDaoImpl.kt | 8 +- .../paging/OffsetQueryPagingSource.kt | 70 ++++++++++++++ .../sqldelight/paging/QueryPagingSourceKt.kt | 75 +++++++++++++++ .../db/sqldelight/query/QueryExtensions.kt | 46 +++++++-- .../twiderex/sqldelight/table/DMEvent.sq | 5 +- .../twiderex/base/BaseCacheDatabaseTest.kt | 4 +- ...SqlDelightDirectMessageEventDaoImplTest.kt | 95 ++++++++++++++----- 7 files changed, 265 insertions(+), 38 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index d4421b1ff..ab6e98d94 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -21,9 +21,9 @@ package com.twidere.twiderex.db.sqldelight.dao import androidx.paging.PagingSource -import com.squareup.sqldelight.android.paging3.QueryPagingSource import com.twidere.twiderex.db.dao.DirectMessageEventDao import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.db.sqldelight.paging.QueryPagingSource import com.twidere.twiderex.db.sqldelight.query.flatMap import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments import com.twidere.twiderex.db.sqldelight.transform.toUi @@ -45,13 +45,13 @@ internal class SqlDelightDirectMessageEventDaoImpl( override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey - ): PagingSource { + ): PagingSource { return QueryPagingSource( countQuery = dmEventQueries.getMessageCount(accountKey = accountKey, conversationKey = conversationKey), transacter = dmEventQueries, dispatcher = Dispatchers.IO, - queryProvider = {limit, offset -> - dmEventQueries.getMessagesByConversation( + queryProvider = { limit, offset -> + dmEventQueries.getMessageByConversationKey( accountKey = accountKey, conversationKey = conversationKey, limit = limit, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt new file mode 100644 index 000000000..02d459248 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.paging + +import androidx.paging.PagingState +import com.squareup.sqldelight.Query +import com.squareup.sqldelight.Transacter +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +internal class OffsetQueryPagingSource( + private val queryProvider: (limit: Long, offset: Long) -> Query, + private val countQuery: Query, + private val transacter: Transacter, + private val dispatcher: CoroutineDispatcher, +) : QueryPagingSource() { + + override val jumpingSupported get() = true + + override suspend fun load( + params: LoadParams + ): LoadResult = withContext(dispatcher) { + try { + val key = params.key ?: 0 + transacter.transactionWithResult { + val count = countQuery.executeAsOne() + if (count != 0L && key.toLong() >= count) throw IndexOutOfBoundsException() + + val loadSize = if (key < 0) params.loadSize + key else params.loadSize + + val data = queryProvider(loadSize.toLong(), maxOf(0, key).toLong()) + .also { currentQuery = it } + .executeAsList() + + LoadResult.Page( + data = data, + // allow one, and only one negative prevKey in a paging set. This is done for + // misaligned prepend queries to avoid duplicates. + prevKey = if (key <= 0) null else key - params.loadSize, + nextKey = if (key + params.loadSize >= count) null else key + params.loadSize, + itemsBefore = maxOf(0, key), + itemsAfter = maxOf(0, (count - (key + params.loadSize))).toInt() + ) + } + } catch (e: Exception) { + if (e is IndexOutOfBoundsException) throw e + LoadResult.Error(e) + } + } + + override fun getRefreshKey(state: PagingState) = state.anchorPosition +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt new file mode 100644 index 000000000..4b07f57ef --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt @@ -0,0 +1,75 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.paging + +import androidx.paging.PagingSource +import com.squareup.sqldelight.Query +import com.squareup.sqldelight.Transacter +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlin.properties.Delegates + +internal abstract class QueryPagingSource : + PagingSource(), + Query.Listener { + + protected var currentQuery: Query? by Delegates.observable(null) { _, old, new -> + old?.removeListener(this) + new?.addListener(this) + } + + init { + registerInvalidatedCallback { + currentQuery?.removeListener(this) + currentQuery = null + } + } + + final override fun queryResultsChanged() = invalidate() +} + +/** + * Create a [PagingSource] that pages through results according to queries generated by + * [queryProvider]. Queries returned by [queryProvider] should expect to do SQL offset/limit + * based paging. For that reason, [countQuery] is required to calculate pages and page offsets. + * + * An example query returned by [queryProvider] could look like: + * + * ```sql + * SELECT value FROM numbers + * LIMIT 10 + * OFFSET 100; + * ``` + * + * Queries will be executed on [dispatcher]. + */ +@Suppress("FunctionName") +fun QueryPagingSource( + countQuery: Query, + transacter: Transacter, + dispatcher: CoroutineDispatcher = Dispatchers.IO, + queryProvider: (limit: Long, offset: Long) -> Query, +): PagingSource = OffsetQueryPagingSource( + queryProvider, + countQuery, + transacter, + dispatcher +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt index 17ed643a0..231069e6b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt @@ -1,16 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.twiderex.db.sqldelight.query import com.squareup.sqldelight.Query import com.squareup.sqldelight.internal.copyOnWriteList -internal fun Query.flatMap(map: (T) -> R):Query { - return object : Query( - queries = copyOnWriteList(), - mapper = { - val db = mapper.invoke(it) - map.invoke(db) +internal fun Query.flatMap(map: (T) -> R): Query { + return object : + Query( + queries = copyOnWriteList(), + mapper = { + val db = mapper.invoke(it) + map.invoke(db) + } + ), + Query.Listener { + init { + // flatMap should also dispatch previous query's events + this@flatMap.addListener(this) } - ) { override fun execute() = this@flatMap.execute() + + override fun queryResultsChanged() { + notifyDataChanged() + } } -} \ No newline at end of file +} diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq index ec736ed5f..a658000e4 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq @@ -34,4 +34,7 @@ delete: DELETE FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey AND messageKey == :messageKey; getMessageCount: -SELECT COUNT(*) FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey; \ No newline at end of file +SELECT COUNT(*) FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey; + +getMessageByConversationKey: +SELECT * FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey LIMIT :limit OFFSET :offset; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt index fc61e3414..e0af90633 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt @@ -29,12 +29,12 @@ internal open class BaseCacheDatabaseTest { protected lateinit var database: SqlDelightCacheDatabase private val driver = SqlDriverFactory.create(SqlDelightCacheDatabase.Schema) @Before - fun setUp() { + open fun setUp() { database = createCacheDataBase(driver) } @After - fun tearDown() { + open fun tearDown() { database.cacheDropQueries.clearAllTables() driver.close() } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt index 2c7b1b843..d44479180 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt @@ -21,37 +21,86 @@ package com.twidere.twiderex.db.dao import androidx.paging.PagingSource +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDirectMessageEventDaoImpl +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiDMEvent +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals -class SqlDelightDirectMessageEventDaoImpl : DirectMessageEventDao { - override fun getPagingSource( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): PagingSource { - TODO("Not yet implemented") +internal class SqlDelightDirectMessageEventDaoImplTest : BaseCacheDatabaseTest() { + private lateinit var dao: SqlDelightDirectMessageEventDaoImpl + private val accountKey = MicroBlogKey.twitter("account") + override fun setUp() { + super.setUp() + dao = SqlDelightDirectMessageEventDaoImpl( + dmEventQueries = database.dMEventQueries, + urlEntityQueries = database.urlEntityQueries, + mediaQueries = database.mediaQueries, + userQueries = database.userQueries + ) } - override suspend fun findWithMessageKey( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey, - messageKey: MicroBlogKey - ): UiDMEvent? { - TODO("Not yet implemented") + @Test + fun insertAll_InsertBothEventAndAttachments() = runBlocking { + val insert = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + dao.insertAll(listOf(insert)) + val result = dao.findWithMessageKey(accountKey = accountKey, conversationKey = insert.conversationKey, messageKey = insert.messageKey) + assertEquals(insert.messageKey, result?.messageKey) + assertEquals(insert.sender.userKey, result?.sender?.userKey) + assertEquals(insert.media.first().url, result?.media?.first()?.url) + assertEquals(insert.urlEntity.first().url, result?.urlEntity?.first()?.url) } - override suspend fun delete(message: UiDMEvent) { - TODO("Not yet implemented") - } + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)), + ) + dao.insertAll(list) + val pagingSource = dao.getPagingSource( + accountKey = accountKey, + conversationKey = list.first().conversationKey + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) - override suspend fun getMessageCount( - accountKey: MicroBlogKey, - conversationKey: MicroBlogKey - ): Long { - TODO("Not yet implemented") + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) } - override suspend fun insertAll(events: List) { - TODO("Not yet implemented") + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpDate() = runBlocking { + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + var invalidate = false + dao.getPagingSource( + accountKey = accountKey, + conversationKey = message.conversationKey + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) + } + dao.insertAll(listOf(message)) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) } } From e5e10fc15164f36348e2d65e28a509e4ab35a2f6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 11:53:23 +0800 Subject: [PATCH 245/615] update usage of asStateIn --- .../com/twidere/twiderex/viewmodel/AccountViewModel.kt | 4 +--- .../com/twidere/twiderex/viewmodel/MediaViewModel.kt | 4 +--- .../com/twidere/twiderex/viewmodel/StatusViewModel.kt | 2 +- .../viewmodel/compose/ComposeSearchUserViewModel.kt | 3 +-- .../twidere/twiderex/viewmodel/compose/ComposeViewModel.kt | 2 +- .../compose/MastodonComposeSearchHashtagViewModel.kt | 3 +-- .../twiderex/viewmodel/dm/DMConversationViewModel.kt | 3 +-- .../com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt | 3 +-- .../twiderex/viewmodel/dm/DMNewConversationViewModel.kt | 3 +-- .../twiderex/viewmodel/lists/ListsAddMemberViewModel.kt | 3 +-- .../twiderex/viewmodel/lists/ListsSearchUserViewModel.kt | 3 +-- .../twiderex/viewmodel/lists/ListsTimelineViewModel.kt | 3 +-- .../twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt | 3 +-- .../com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt | 7 +++---- .../viewmodel/mastodon/MastodonHashtagViewModel.kt | 3 +-- .../viewmodel/mastodon/MastodonSearchHashtagViewModel.kt | 3 +-- .../twiderex/viewmodel/search/SearchInputViewModel.kt | 3 +-- .../twiderex/viewmodel/search/SearchSaveViewModel.kt | 3 +-- .../twiderex/viewmodel/search/SearchTweetsViewModel.kt | 3 +-- .../twiderex/viewmodel/search/SearchUserViewModel.kt | 3 +-- .../viewmodel/settings/AccountNotificationViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/settings/LayoutViewModel.kt | 3 +-- .../twiderex/viewmodel/timeline/HomeTimelineViewModel.kt | 2 +- .../viewmodel/timeline/MentionsTimelineViewModel.kt | 2 +- .../viewmodel/timeline/NotificationTimelineViewModel.kt | 2 +- .../timeline/mastodon/FederatedTimelineViewModel.kt | 2 +- .../viewmodel/timeline/mastodon/LocalTimelineViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt | 3 +-- .../twitter/search/TwitterSearchMediaViewModel.kt | 3 +-- .../viewmodel/twitter/user/TwitterUserViewModel.kt | 4 +--- .../twidere/twiderex/viewmodel/user/FollowersViewModel.kt | 3 +-- .../twidere/twiderex/viewmodel/user/FollowingViewModel.kt | 3 +-- .../viewmodel/user/UserFavouriteTimelineViewModel.kt | 3 +-- .../twiderex/viewmodel/user/UserMediaTimelineViewModel.kt | 3 +-- .../twiderex/viewmodel/user/UserTimelineViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/user/UserViewModel.kt | 3 +-- .../twidere/twiderex/viewmodel/user/UserViewModelTest.kt | 1 + 37 files changed, 39 insertions(+), 68 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt index 8cef5b50d..1ea1d8c37 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt @@ -20,16 +20,14 @@ */ package com.twidere.twiderex.viewmodel -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope abstract class AccountViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { protected val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 55f9a7b2d..23c65387a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel import com.twidere.twiderex.action.MediaAction -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.repository.AccountRepository @@ -32,7 +31,6 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class MediaViewModel( private val repository: StatusRepository, @@ -41,7 +39,7 @@ class MediaViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } suspend fun saveFile(currentMedia: UiMedia, target: String) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index bd01ab90a..ee5351113 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -37,7 +37,7 @@ class StatusViewModel( private val statusKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index f1cc40264..7e885a5b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,7 +41,7 @@ class ComposeSearchUserViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val text = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 047a905b4..d251ab6a6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -162,7 +162,7 @@ open class ComposeViewModel( val composeType: ComposeType, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } open val draftId: String = UUID.randomUUID().toString() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index d12f6b76c..77ef547a3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,7 +41,7 @@ class MastodonComposeSearchHashtagViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val text = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 7a3aeaed0..1f4eb6261 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.viewmodel.dm import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.DirectMessageRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,7 +36,7 @@ class DMConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 5f2cc19b0..92b305858 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -24,7 +24,6 @@ import androidx.paging.cachedIn import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.twiderex.action.DirectMessageAction -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData @@ -49,7 +48,7 @@ class DMEventViewModel( private val conversationKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index 7c1a9513d..b2056102b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.paging.source.SearchUserPagingSource @@ -48,7 +47,7 @@ class DMNewConversationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val input = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt index 27a1b83b5..09b03c274 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.compose.runtime.mutableStateMapOf import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.notification.InAppNotification @@ -43,7 +42,7 @@ class ListsAddMemberViewModel( private val listId: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 330f5ed5c..14fde01cd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -43,7 +42,7 @@ class ListsSearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val text = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index ab7472575..947368693 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -38,7 +37,7 @@ class ListsTimelineViewModel( listKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index c759c485b..c0aee439e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.ListsUsersRepository @@ -43,7 +42,7 @@ class ListsUserViewModel( private val viewMembers: Boolean = true, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index e2f836d42..d408275db 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.viewmodel.lists import androidx.paging.cachedIn import androidx.paging.filter import com.twidere.services.microblog.ListsService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.ListsMode import com.twidere.twiderex.model.ui.UiList @@ -46,7 +45,7 @@ class ListsViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) @@ -112,7 +111,7 @@ class ListsCreateViewModel( private val accountRepository: AccountRepository, ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } suspend fun createList( @@ -151,7 +150,7 @@ class ListsModifyViewModel( private val listKey: MicroBlogKey, ) : ListsOperatorViewModel(inAppNotification) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val editName = MutableStateFlow("") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 6f7b63d10..4aaf9d9ee 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.mastodon import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,7 +36,7 @@ class MastodonHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 2057fe977..48c820497 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.MastodonSearchHashtagPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -39,7 +38,7 @@ class MastodonSearchHashtagViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index f76adc3c2..3c0406fe0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository @@ -38,7 +37,7 @@ class SearchInputViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index 304aab5b7..a4c5fe253 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.search -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.flow.MutableStateFlow @@ -36,7 +35,7 @@ class SearchSaveViewModel( private val content: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val loading = MutableStateFlow(false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index 13fe7530e..f4c967992 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -24,7 +24,6 @@ import androidx.paging.cachedIn import androidx.paging.map import com.twidere.services.microblog.SearchService import com.twidere.twiderex.db.CacheDatabase -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.search.SearchStatusMediator import com.twidere.twiderex.repository.AccountRepository @@ -41,7 +40,7 @@ class SearchTweetsViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index b085b7b48..58b6246e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -25,7 +25,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService import com.twidere.twiderex.defaultLoadCount -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.paging.source.SearchUserPagingSource import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -40,7 +39,7 @@ class SearchUserViewModel( following: Boolean = false, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 70db85f95..f82129766 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -36,7 +36,7 @@ class AccountNotificationViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val preferences by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 75445baf0..2920dc053 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.viewmodel.settings -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.flow.firstOrNull @@ -37,7 +36,7 @@ class LayoutViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } fun updateHomeMenu(oldIndex: Int, newIndex: Int, menus: List) = viewModelScope.launch { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 0e72112d4..2a7b53ff8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -38,7 +38,7 @@ class HomeTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 0295e345c..28e53384f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -40,7 +40,7 @@ class MentionsTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } override val pagingMediator by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 9df3bf0ad..9bebee8e9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -40,7 +40,7 @@ class NotificationTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } override val pagingMediator by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index 766c05074..a0fbc5738 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -38,7 +38,7 @@ class FederatedTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } override val pagingMediator by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 016efb101..92e1d6113 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -39,7 +39,7 @@ class LocalTimelineViewModel( private val accountRepository: AccountRepository, ) : TimelineViewModel(dataStore) { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index bb43a4e49..af8ca8ef0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.trend import androidx.paging.cachedIn import com.twidere.services.microblog.TrendService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TrendRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -36,7 +35,7 @@ class TrendViewModel( private val accountRepository: AccountRepository, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index a5cf2c54e..9ee8d9c87 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.twitter.search import androidx.paging.cachedIn import com.twidere.services.microblog.SearchService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,7 +36,7 @@ class TwitterSearchMediaViewModel( keyword: String, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 290ec5108..69af0c801 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -21,7 +21,6 @@ package com.twidere.twiderex.viewmodel.twitter.user import com.twidere.services.microblog.LookupService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserRepository @@ -30,7 +29,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.viewModelScope class TwitterUserViewModel( private val repository: UserRepository, @@ -40,7 +38,7 @@ class TwitterUserViewModel( ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val error = MutableStateFlow(null) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index cab800ab3..ccf7cb6e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository @@ -37,7 +36,7 @@ class FollowersViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index be2299726..7bb5918c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.UserListRepository @@ -37,7 +36,7 @@ class FollowingViewModel( private val userKey: MicroBlogKey, ) : UserListViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 95535e01f..905036117 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.TimelineRepository @@ -38,7 +37,7 @@ class UserFavouriteTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index aefaa9469..9d4652e95 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -23,7 +23,6 @@ package com.twidere.twiderex.viewmodel.user import androidx.paging.PagingData import androidx.paging.cachedIn import com.twidere.services.microblog.TimelineService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiStatus @@ -42,7 +41,7 @@ class UserMediaTimelineViewModel( private val userKey: MicroBlogKey, ) : ViewModel() { private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index af7858ce1..4641b72af 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -42,7 +42,7 @@ class UserTimelineViewModel( private val _excludeReplies = MutableStateFlow(false) val excludeReplies = _excludeReplies.asStateIn(viewModelScope, false) private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } fun setExcludeReplies(value: Boolean) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index b08d19cba..df532ec4f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.viewmodel.user import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.RelationshipService -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository @@ -47,7 +46,7 @@ class UserViewModel( ) : ViewModel() { private val refreshFlow = MutableStateFlow(UUID.randomUUID()) private val account by lazy { - accountRepository.activeAccount.asStateIn(viewModelScope, null).mapNotNull { it } + accountRepository.activeAccount.mapNotNull { it } } val refreshing = MutableStateFlow(false) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt index decf47bd3..a28202035 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -95,6 +95,7 @@ internal class UserViewModelTest : AccountViewModelTestBase() { ) viewModel.isMe.test { assert(awaitItem()) + cancelAndIgnoreRemainingEvents() } } From de1c2af06883c652ae273cd3a8246b5056c1941d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 13:54:57 +0800 Subject: [PATCH 246/615] fix notification timeline viewmodel --- .../viewmodel/timeline/NotificationTimelineViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 9bebee8e9..98b910570 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -29,6 +29,7 @@ import com.twidere.twiderex.model.enums.NotificationCursorType import com.twidere.twiderex.paging.mediator.timeline.NotificationTimelineMediator import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NotificationRepository +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.viewModelScope @@ -41,6 +42,7 @@ class NotificationTimelineViewModel( ) : TimelineViewModel(dataStore) { private val account by lazy { accountRepository.activeAccount.mapNotNull { it } + .filter { it.service is NotificationService } } override val pagingMediator by lazy { From 2da57d36b799a770849e9e0d0c32fa4c18863ff9 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 13:58:17 +0800 Subject: [PATCH 247/615] fix timeline component restore scroll state lag --- .../twidere/twiderex/component/TimelineComponent.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 2f091a080..93e6f7db5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -58,18 +58,17 @@ fun TimelineComponent( val listState = rememberLazyListState() LaunchedEffect(Unit) { var inited = false + val scrollState = viewModel.provideScrollState() snapshotFlow { listState.layoutInfo.totalItemsCount } .distinctUntilChanged() .filter { it != 0 } .filter { !inited } .collect { inited = true - viewModel.provideScrollState().let { - listState.scrollToItem( - it.firstVisibleItemIndex, - it.firstVisibleItemScrollOffset - ) - } + listState.scrollToItem( + scrollState.firstVisibleItemIndex, + scrollState.firstVisibleItemScrollOffset + ) } } if (items.itemCount > 0) { From da12fefd4b867e1987d9b408531be0820fe2c7a0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 14:06:20 +0800 Subject: [PATCH 248/615] clean up --- .../twiderex/utils/TwitterErrorHandling.kt | 73 ------------------- .../twiderex/viewmodel/AccountViewModel.kt | 33 --------- 2 files changed, 106 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt delete mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt deleted file mode 100644 index 04dc270ab..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils - -// fun Throwable.generateNotificationEvent(): NotificationEvent? { -// return when (this) { -// is MicroBlogHttpException -> { -// when (this.httpCode) { -// HttpErrorCodes.TooManyRequests -> { -// return StringResNotificationEvent(messageId = R.string.common_alerts_too_many_requests_title) -// } -// else -> null -// } -// } -// is MicroBlogJsonException -> { -// microBlogErrorMessage?.let { StringNotificationEvent(it) } -// } -// is TwitterApiException -> { -// when (this.errors?.firstOrNull()?.code) { -// TwitterErrorCodes.TemporarilyLocked -> { -// StringResWithActionNotificationEvent( -// R.string.common_alerts_account_temporarily_locked_title, -// R.string.common_alerts_account_temporarily_locked_message, -// actionId = R.string.common_controls_actions_ok -// ) { -// context.startActivity( -// Intent( -// ACTION_VIEW, -// Uri.parse("https://twitter.com/login") -// ) -// ) -// } -// } -// TwitterErrorCodes.RateLimitExceeded -> null -// else -> microBlogErrorMessage?.let { StringNotificationEvent(it) } -// } -// } -// is TwitterApiExceptionV2 -> { -// detail?.let { StringNotificationEvent(it) } -// } -// is MastodonException -> { -// microBlogErrorMessage?.let { StringNotificationEvent(it) } -// } -// !is CancellationException -> { -// message?.let { StringNotificationEvent(it) } -// } -// else -> null -// } -// } -// -// fun Throwable.notify(notification: InAppNotification) { -// generateNotificationEvent()?.let { -// notification.show(it) -// } ?: throw this -// } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt deleted file mode 100644 index 1ea1d8c37..000000000 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/AccountViewModel.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.viewmodel - -import com.twidere.twiderex.repository.AccountRepository -import kotlinx.coroutines.flow.mapNotNull -import moe.tlaster.precompose.viewmodel.ViewModel - -abstract class AccountViewModel( - private val accountRepository: AccountRepository, -) : ViewModel() { - protected val account by lazy { - accountRepository.activeAccount.mapNotNull { it } - } -} From 95b48ac5d0b96dcdb7034d35107a62390b527d64 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 14:11:53 +0800 Subject: [PATCH 249/615] add remote navigator to NotificationWithActionEvent --- .../main/kotlin/com/twidere/twiderex/TwidereXActivity.kt | 8 ++++++-- .../component/foundation/InAppNotificationScaffold.kt | 7 +++---- common/src/commonMain/kotlin/com/twidere/twiderex/App.kt | 4 +++- .../kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt | 4 ++++ .../twidere/twiderex/notification/InAppNotification.kt | 4 ++-- .../com/twidere/twiderex/utils/TwitterErrorHandling.kt | 8 +------- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 56ce45aa0..71a00f551 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -28,7 +28,6 @@ import android.net.NetworkRequest import android.net.Uri import android.os.Bundle import android.view.WindowManager -import androidx.activity.compose.setContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn @@ -64,6 +63,8 @@ import com.twidere.twiderex.action.StatusActions import com.twidere.twiderex.component.foundation.LocalInAppNotification import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.kmp.LocalRemoteNavigator +import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.navigation.Router import com.twidere.twiderex.notification.InAppNotification @@ -115,6 +116,8 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { private val platformResolver: PlatformResolver by inject() + private val remoteNavigator: RemoteNavigator by inject() + @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -187,7 +190,8 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { LocalActiveAccountViewModel provides accountViewModel, LocalIsActiveNetworkMetered provides isActiveNetworkMetered, LocalPlatformResolver provides platformResolver, - LocalResLoader provides ResLoader(this) + LocalResLoader provides ResLoader(this), + LocalRemoteNavigator provides remoteNavigator, ) { ProvidePreferences( preferencesHolder, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt index 70b32fec3..66395da5a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp +import com.twidere.twiderex.kmp.LocalRemoteNavigator import com.twidere.twiderex.notification.EventActionContext import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.notification.NotificationWithActionEvent @@ -68,12 +69,10 @@ fun ApplyNotification( null } } - // val navigator = LocalNavigator.current + val remoteNavigator = LocalRemoteNavigator.current val actionContext = remember { EventActionContext( - // TODO: add navigator - TODO = Any() - // navigator = navigator, + remoteNavigator = remoteNavigator ) } LaunchedEffect(event) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 15aa68d62..690c53048 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -27,11 +27,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.di.ext.get +import com.twidere.twiderex.kmp.LocalRemoteNavigator @Composable fun App() { CompositionLocalProvider( - LocalResLoader provides get() + LocalResLoader provides get(), + LocalRemoteNavigator provides get(), ) { MaterialTheme { Scaffold { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index ceea6d6a5..6f94f3237 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -20,6 +20,8 @@ */ package com.twidere.twiderex.kmp +import androidx.compose.runtime.staticCompositionLocalOf + expect class RemoteNavigator { fun openDeepLink(deeplink: String, fromBackground: Boolean = false) @@ -27,3 +29,5 @@ expect class RemoteNavigator { fun shareText(content: String, fromBackground: Boolean = false) } + +val LocalRemoteNavigator = staticCompositionLocalOf { error("No RemoteNavigator") } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index a6dae4350..10de6480c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.notification import androidx.compose.runtime.Composable import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.kmp.RemoteNavigator import com.twidere.twiderex.utils.Event import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.flow.MutableStateFlow @@ -33,8 +34,7 @@ interface NotificationEvent { fun getMessage(): String } data class EventActionContext( - // TODO FIXME: 2021/9/23 Replace with native navigator - val TODO: Any, + val remoteNavigator: RemoteNavigator ) interface NotificationWithActionEvent : NotificationEvent { @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt index f1c0b5e44..cd284f616 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -62,13 +62,7 @@ fun Throwable.generateNotificationEvent(): NotificationEvent? { MR.strings.common_alerts_account_temporarily_locked_message, actionStr = MR.strings.common_controls_actions_ok ) { - // TODO FIXME: 2021/9/23 Implementation - // context.startActivity( - // Intent( - // Intent.ACTION_VIEW, - // Uri.parse("https://twitter.com/login") - // ) - // ) + remoteNavigator.openDeepLink("https://twitter.com/login") } } TwitterErrorCodes.RateLimitExceeded -> null From 28981444bdf91aa369e952dd294314d6dd7c7988 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 14:18:07 +0800 Subject: [PATCH 250/615] fix usage of flow.lastOrNull() --- .../twiderex/viewmodel/compose/ComposeViewModel.kt | 8 +++----- .../viewmodel/settings/AccountNotificationViewModel.kt | 4 ++-- .../twiderex/viewmodel/timeline/TimelineViewModel.kt | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index d251ab6a6..6a22a3568 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -50,10 +50,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn @@ -352,10 +352,8 @@ open class ComposeViewModel( ) fun putImages(value: List) = viewModelScope.launch { - val imageLimit = imageLimit.lastOrNull() ?: 4 - images.value.let { - value + it - }.take(imageLimit).let { + val imageLimit = imageLimit.firstOrNull() ?: 4 + (images.value + value).take(imageLimit).let { images.value = it } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index f82129766..195f6d468 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -23,9 +23,9 @@ package com.twidere.twiderex.viewmodel.settings import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.repository.AccountRepository import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch @@ -52,6 +52,6 @@ class AccountNotificationViewModel( } fun setIsNotificationEnabled(value: Boolean) = viewModelScope.launch { - preferences.lastOrNull()?.setIsNotificationEnabled(value) + preferences.firstOrNull()?.setIsNotificationEnabled(value) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 572764608..5e5dc93f0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.lastOrNull import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -85,7 +84,7 @@ abstract class TimelineViewModel( maxStatusKey: MicroBlogKey, sinceStatueKey: MicroBlogKey, ) = viewModelScope.launch { - pagingMediator.lastOrNull()?.loadBetween( + pagingMediator.firstOrNull()?.loadBetween( defaultLoadCount, maxStatusKey = maxStatusKey, sinceStatusKey = sinceStatueKey From 4972a13ad84248033da83a5048c0acd92e74c4ae Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 14:22:39 +0800 Subject: [PATCH 251/615] fix draft view model crash --- .../kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index fffe67836..c65993719 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -160,8 +160,7 @@ fun DraftComposeScene( data?.let { draft -> val viewModel: DraftComposeViewModel = getViewModel { parametersOf( - account, - draft + draft, ) } ComposeBody(viewModel = viewModel, account = account) From 572426be04099ff61844d69cb677becea6329196 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 27 Sep 2021 16:30:12 +0800 Subject: [PATCH 252/615] add sqlDelight table for UiDMConversation and implement dao with tests --- .../DirectMessageConversationDaoImplTest.kt | 14 +- .../adapter/DMConversationAdapterFactory.kt | 35 +++++ ...DelightDirectMessageConversationDaoImpl.kt | 102 ++++++++++++++ .../SqlDelightDirectMessageEventDaoImpl.kt | 47 ++----- .../twiderex/db/sqldelight/database.kt | 4 +- .../model/DbDMConversationWithEvent.kt | 46 +++++++ .../model/DbDMEventWithAttachments.kt | 27 +++- .../transform/DMConversationTransform.kt | 53 +++++++ .../twiderex/sqldelight/table/CacheDrop.sq | 1 + .../sqldelight/table/DMConversation.sq | 27 ++++ .../twiderex/sqldelight/table/DMEvent.sq | 27 +++- ...ghtDirectMessageConversationDaoImplTest.kt | 130 ++++++++++++++++++ ...SqlDelightDirectMessageEventDaoImplTest.kt | 5 +- .../DMConversationQueriesImplTest.kt | 79 +++++++++++ .../db/sqldelight/DMEventQueriesImplTest.kt | 45 +++++- .../twidere/twiderex/mock/model/MockModels.kt | 14 ++ 16 files changed, 595 insertions(+), 61 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt index d13fd3bf1..aafc07694 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt @@ -26,9 +26,8 @@ import com.twidere.twiderex.dataprovider.mapper.toUi import com.twidere.twiderex.db.base.CacheDatabaseDaoTest import com.twidere.twiderex.mock.model.mockIDirectMessage import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toConversation import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.model.ui.UiDMConversation -import com.twidere.twiderex.model.ui.UiDMEvent import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking import org.junit.Test @@ -38,17 +37,6 @@ import kotlin.test.assertNull internal class DirectMessageConversationDaoImplTest : CacheDatabaseDaoTest() { val accountKey = MicroBlogKey.twitter("test") - private fun UiDMEvent.toConversation() = UiDMConversation( - accountKey = accountKey, - conversationId = conversationKey.id, - conversationKey = conversationKey, - conversationAvatar = sender.profileImage.toString(), - conversationName = sender.name, - conversationSubName = sender.screenName, - conversationType = UiDMConversation.Type.ONE_TO_ONE, - recipientKey = conversationUserKey - ) - @Test fun delete() = runBlocking { val cacheDatabase = CacheDatabaseImpl(roomDatabase) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt new file mode 100644 index 000000000..94f5e3f1e --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt @@ -0,0 +1,35 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.squareup.sqldelight.EnumColumnAdapter +import com.twidere.twiderex.sqldelight.table.DMConversation + +object DMConversationAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + DMConversation.Adapter( + accountKeyAdapter = it, + conversationKeyAdapter = it, + recipientKeyAdapter = it, + conversationTypeAdapter = EnumColumnAdapter() + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt new file mode 100644 index 000000000..caabf288f --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt @@ -0,0 +1,102 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.dao + +import androidx.paging.PagingSource +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull +import com.twidere.twiderex.db.dao.DirectMessageConversationDao +import com.twidere.twiderex.db.sqldelight.model.DbDMConversationWithEvent.Companion.toDbDMConversationWithEvent +import com.twidere.twiderex.db.sqldelight.paging.QueryPagingSource +import com.twidere.twiderex.db.sqldelight.query.flatMap +import com.twidere.twiderex.db.sqldelight.transform.toDbDMConversation +import com.twidere.twiderex.db.sqldelight.transform.toUi +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class SqlDelightDirectMessageConversationDaoImpl( + private val database: SqlDelightCacheDatabase +) : DirectMessageConversationDao { + private val dmConversationQueries = database.dMConversationQueries + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return QueryPagingSource( + countQuery = database.dMEventQueries.getLatestMessagesInEachConversationPagingCount(accountKey = accountKey), + transacter = database.dMEventQueries, + queryProvider = { limit, offset -> + database.dMEventQueries.getLatestMessagesInEachConversationPagingList( + accountKey = accountKey, + limit = limit, + offset = offset + ).flatMap { + it.toDbDMConversationWithEvent(database).toUi() + } + } + ) + } + + override fun findWithConversationKeyFlow( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): Flow { + return dmConversationQueries.findWithConversationKey( + accountKey = accountKey, + conversationKey = conversationKey + ).asFlow() + .mapToOneOrNull() + .map { it?.toUi() } + } + + override suspend fun findWithConversationKey( + accountKey: MicroBlogKey, + conversationKey: MicroBlogKey + ): UiDMConversation? { + return dmConversationQueries.findWithConversationKey( + accountKey = accountKey, + conversationKey = conversationKey + ).executeAsOneOrNull() + ?.toUi() + } + + override suspend fun insertAll(listOf: List) { + dmConversationQueries.transaction { + listOf.forEach { + dmConversationQueries.insert(it.toDbDMConversation()) + } + } + } + + override suspend fun find(accountKey: MicroBlogKey): List { + return database.dMEventQueries.getLatestMessagesInEachConversation(accountKey = accountKey) + .executeAsList() + .map { it.toDbDMConversationWithEvent(database).toUi() } + } + + override suspend fun delete(conversation: UiDMConversation) { + dmConversationQueries.transaction { + dmConversationQueries.delete(accountKey = conversation.accountKey, conversationKey = conversation.conversationKey) + database.dMEventQueries.clearByConversationKey(accountKey = conversation.accountKey, conversationKey = conversation.conversationKey) + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index ab6e98d94..cc7f5c513 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -22,42 +22,37 @@ package com.twidere.twiderex.db.sqldelight.dao import androidx.paging.PagingSource import com.twidere.twiderex.db.dao.DirectMessageEventDao -import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments.Companion.saveToDb +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments.Companion.withAttachments import com.twidere.twiderex.db.sqldelight.paging.QueryPagingSource import com.twidere.twiderex.db.sqldelight.query.flatMap import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments import com.twidere.twiderex.db.sqldelight.transform.toUi import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiDMEvent -import com.twidere.twiderex.sqldelight.table.DMEvent -import com.twidere.twiderex.sqldelight.table.DMEventQueries -import com.twidere.twiderex.sqldelight.table.MediaQueries -import com.twidere.twiderex.sqldelight.table.UrlEntityQueries -import com.twidere.twiderex.sqldelight.table.UserQueries +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase import kotlinx.coroutines.Dispatchers internal class SqlDelightDirectMessageEventDaoImpl( - private val dmEventQueries: DMEventQueries, - private val urlEntityQueries: UrlEntityQueries, - private val mediaQueries: MediaQueries, - private val userQueries: UserQueries + private val database: SqlDelightCacheDatabase ) : DirectMessageEventDao { + private val dmEventQueries = database.dMEventQueries override fun getPagingSource( accountKey: MicroBlogKey, conversationKey: MicroBlogKey ): PagingSource { return QueryPagingSource( - countQuery = dmEventQueries.getMessageCount(accountKey = accountKey, conversationKey = conversationKey), + countQuery = dmEventQueries.getMessagesPagingCount(accountKey = accountKey, conversationKey = conversationKey), transacter = dmEventQueries, dispatcher = Dispatchers.IO, queryProvider = { limit, offset -> - dmEventQueries.getMessageByConversationKey( + dmEventQueries.getMessagesPagingList( accountKey = accountKey, conversationKey = conversationKey, limit = limit, offset = offset ).flatMap { - it.withAttachments().toUi() + it.withAttachments(database).toUi() } } ) @@ -71,7 +66,7 @@ internal class SqlDelightDirectMessageEventDaoImpl( accountKey = accountKey, conversationKey = conversationKey, messageKey = messageKey - ).executeAsOneOrNull()?.withAttachments()?.toUi() + ).executeAsOneOrNull()?.withAttachments(database)?.toUi() override suspend fun delete(message: UiDMEvent) { dmEventQueries.delete( @@ -85,30 +80,10 @@ internal class SqlDelightDirectMessageEventDaoImpl( accountKey: MicroBlogKey, conversationKey: MicroBlogKey ): Long { - return dmEventQueries.getMessageCount(accountKey = accountKey, conversationKey = conversationKey).executeAsOne() + return dmEventQueries.getMessagesPagingCount(accountKey = accountKey, conversationKey = conversationKey).executeAsOne() } override suspend fun insertAll(events: List) { - dmEventQueries.transaction { - events.map { it.toDbEventWithAttachments() }.let { list -> - list.forEach { - dmEventQueries.insert(it.event) - it.media.forEach { media -> mediaQueries.insert(media) } - userQueries.insert(it.sender) - it.url.forEach { url -> urlEntityQueries.insert(url) } - } - } - } - } - - private fun DMEvent.withAttachments(): DbDMEventWithAttachments { - return dmEventQueries.transactionWithResult { - DbDMEventWithAttachments( - event = this@withAttachments, - url = urlEntityQueries.findByBelongToKey(messageKey).executeAsList(), - media = mediaQueries.findMediaByBelongToKey(messageKey).executeAsList(), - sender = userQueries.findWithUserKey(senderAccountKey).executeAsOne() - ) - } + events.map { it.toDbEventWithAttachments() }.saveToDb(database) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt index a5b5d2d92..201cc3404 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.db.sqldelight import com.squareup.sqldelight.db.SqlDriver +import com.twidere.twiderex.db.sqldelight.adapter.DMConversationAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.DMEventAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.MediaAdapterFactory @@ -46,6 +47,7 @@ internal fun createCacheDataBase(driver: SqlDriver): SqlDelightCacheDatabase { DMEventAdapter = DMEventAdapterFactory.create(), MediaAdapter = MediaAdapterFactory.create(), UrlEntityAdapter = UrlEntityAdapterFactory.create(), - UserAdapter = UserAdapterFactory.create() + UserAdapter = UserAdapterFactory.create(), + DMConversationAdapter = DMConversationAdapterFactory.create() ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt new file mode 100644 index 000000000..cce6a8149 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt @@ -0,0 +1,46 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.model + +import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments.Companion.withAttachments +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase +import com.twidere.twiderex.sqldelight.table.DMConversation +import com.twidere.twiderex.sqldelight.table.DMEvent + +internal data class DbDMConversationWithEvent( + val event: DbDMEventWithAttachments, + val conversation: DMConversation +) { + companion object { + fun DMEvent.toDbDMConversationWithEvent(database: SqlDelightCacheDatabase): DbDMConversationWithEvent { + return withAttachments(database) + .let { + DbDMConversationWithEvent( + event = it, + conversation = database.dMConversationQueries.findWithConversationKey( + accountKey = it.event.accountKey, + conversationKey = it.event.conversationKey + ).executeAsOne() + ) + } + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt index 22c887331..b7a60e09d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.db.sqldelight.model +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase import com.twidere.twiderex.sqldelight.table.DMEvent import com.twidere.twiderex.sqldelight.table.Media import com.twidere.twiderex.sqldelight.table.UrlEntity @@ -30,4 +31,28 @@ internal data class DbDMEventWithAttachments( val media: List, val url: List, val sender: User -) +) { + companion object { + fun DMEvent.withAttachments(database: SqlDelightCacheDatabase): DbDMEventWithAttachments { + return database.transactionWithResult { + DbDMEventWithAttachments( + event = this@withAttachments, + url = database.urlEntityQueries.findByBelongToKey(messageKey).executeAsList(), + media = database.mediaQueries.findMediaByBelongToKey(messageKey).executeAsList(), + sender = database.userQueries.findWithUserKey(senderAccountKey).executeAsOne() + ) + } + } + + fun List.saveToDb(database: SqlDelightCacheDatabase) { + database.transaction { + forEach { + database.dMEventQueries.insert(it.event) + it.media.forEach { media -> database.mediaQueries.insert(media) } + database.userQueries.insert(it.sender) + it.url.forEach { url -> database.urlEntityQueries.insert(url) } + } + } + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt new file mode 100644 index 000000000..5213b48a8 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt @@ -0,0 +1,53 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.db.sqldelight.model.DbDMConversationWithEvent +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage +import com.twidere.twiderex.sqldelight.table.DMConversation + +internal fun UiDMConversation.toDbDMConversation() = DMConversation( + accountKey = accountKey, + conversationKey = conversationKey, + recipientKey = recipientKey, + conversationId = conversationId, + conversationAvatar = conversationAvatar, + conversationName = conversationName, + conversationSubName = conversationSubName, + conversationType = conversationType +) + +internal fun DMConversation.toUi() = UiDMConversation( + accountKey = accountKey, + conversationKey = conversationKey, + recipientKey = recipientKey, + conversationId = conversationId, + conversationAvatar = conversationAvatar, + conversationName = conversationName, + conversationSubName = conversationSubName, + conversationType = conversationType +) + +internal fun DbDMConversationWithEvent.toUi() = UiDMConversationWithLatestMessage( + conversation = conversation.toUi(), + latestMessage = event.toUi() +) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq index 5b7b703bd..2ca9c3c60 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq @@ -3,4 +3,5 @@ clearAllTables{ DELETE FROM Media; DELETE FROM UrlEntity; DELETE FROM User; + DELETE FROM DMConversation; } \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq new file mode 100644 index 000000000..766d60ae2 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq @@ -0,0 +1,27 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.ui.UiDMConversation; + +CREATE TABLE DMConversation ( + accountKey TEXT AS MicroBlogKey NOT NULL, + conversationKey TEXT AS MicroBlogKey NOT NULL, + recipientKey TEXT AS MicroBlogKey NOT NULL, + conversationId TEXT NOT NULL, + conversationAvatar TEXT NOT NULL, + conversationName TEXT NOT NULL, + conversationSubName TEXT NOT NULL, + conversationType TEXT AS UiDMConversation.Type NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_accountKey_conversationKey ON DMConversation (accountKey, conversationKey); + +insert: +INSERT OR REPLACE INTO DMConversation( + accountKey, conversationKey, recipientKey, conversationId, conversationAvatar, + conversationName, conversationSubName, conversationType +) VALUES ?; + +findWithConversationKey: +SELECT * FROM DMConversation WHERE accountKey == :accountKey AND conversationKey == :conversationKey; + +delete: +DELETE FROM DMConversation WHERE accountKey == :accountKey AND conversationKey == :conversationKey; diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq index a658000e4..9d8983ed3 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMEvent.sq @@ -33,8 +33,29 @@ SELECT * FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :co delete: DELETE FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey AND messageKey == :messageKey; -getMessageCount: +getMessagesPagingCount: SELECT COUNT(*) FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey; -getMessageByConversationKey: -SELECT * FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey LIMIT :limit OFFSET :offset; \ No newline at end of file +getMessagesPagingList: +SELECT * FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey ORDER BY sortId DESC LIMIT :limit OFFSET :offset; + +getLatestMessagesInEachConversation: +SELECT event.* FROM DMEvent AS event +JOIN (SELECT conversationKey, MAX(sortId) AS sortId FROM DMEvent WHERE accountKey == :accountKey GROUP BY conversationKey) AS conversation +ON event.conversationKey = conversation.conversationKey AND event.sortId = conversation.sortId +WHERE event.accountKey == :accountKey ORDER BY event.sortId DESC; + +getLatestMessagesInEachConversationPagingList: +SELECT event.* FROM DMEvent AS event +JOIN (SELECT conversationKey, MAX(sortId) AS sortId FROM DMEvent WHERE accountKey == :accountKey GROUP BY conversationKey) AS conversation +ON event.conversationKey = conversation.conversationKey AND event.sortId = conversation.sortId +WHERE event.accountKey == :accountKey ORDER BY event.sortId DESC LIMIT :limit OFFSET :offset; + +getLatestMessagesInEachConversationPagingCount: +SELECT COUNT(*) FROM DMEvent AS event +JOIN (SELECT conversationKey, MAX(sortId) AS sortId FROM DMEvent WHERE accountKey == :accountKey GROUP BY conversationKey) AS conversation +ON event.conversationKey = conversation.conversationKey AND event.sortId = conversation.sortId +WHERE event.accountKey == :accountKey; + +clearByConversationKey: +DELETE FROM DMEvent WHERE accountKey == :accountKey AND conversationKey == :conversationKey; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt new file mode 100644 index 000000000..aabfe205d --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt @@ -0,0 +1,130 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDirectMessageConversationDaoImpl +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightDirectMessageEventDaoImpl +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toConversation +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class SqlDelightDirectMessageConversationDaoImplTest : BaseCacheDatabaseTest() { + private lateinit var dao: SqlDelightDirectMessageConversationDaoImpl + private val accountKey = MicroBlogKey.twitter("account") + override fun setUp() { + super.setUp() + dao = SqlDelightDirectMessageConversationDaoImpl( + database = database + ) + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other2") + .toUi(accountKey, mockIUser(id = "other2").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other3") + .toUi(accountKey, mockIUser(id = "other3").toUi(accountKey)), + ) + val eventDao = SqlDelightDirectMessageEventDaoImpl(database) + eventDao.insertAll(list) + dao.insertAll(list.map { it.toConversation() }) + val pagingSource = dao.getPagingSource( + accountKey = accountKey, + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpdate() = runBlocking { + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)) + var invalidate = false + dao.getPagingSource( + accountKey = accountKey, + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) + } + val eventDao = SqlDelightDirectMessageEventDaoImpl(database) + eventDao.insertAll(listOf(message)) + dao.insertAll(listOf(message.toConversation())) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun findWithConversationKeyFlow_FlowUpdatesAfterDbUpdate() = runBlocking { + val message = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)).toConversation() + val flow = dao.findWithConversationKeyFlow(accountKey = accountKey, conversationKey = message.conversationKey) + assertNull(flow.firstOrNull()) + dao.insertAll(listOf(message)) + assertNotNull(flow.firstOrNull()) + dao.insertAll(listOf(message.copy(conversationName = "update"))) + assertEquals("update", flow.firstOrNull()?.conversationName) + } + + @Test + fun delete_DeleteConversationAndAllMessagesItContainsFromDb() = runBlocking { + val list = listOf( + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other1") + .toUi(accountKey, mockIUser(id = "other1").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other2") + .toUi(accountKey, mockIUser(id = "other2").toUi(accountKey)), + mockIDirectMessage(accountId = accountKey.id, otherUserID = "other3") + .toUi(accountKey, mockIUser(id = "other3").toUi(accountKey)), + ) + val eventDao = SqlDelightDirectMessageEventDaoImpl(database) + eventDao.insertAll(list) + dao.insertAll(list.map { it.toConversation() }) + assertEquals(1, database.dMEventQueries.getMessagesPagingCount(accountKey = accountKey, conversationKey = list.first().conversationKey).executeAsOne()) + assertEquals(3, dao.find(accountKey = accountKey).size) + dao.delete(list.first().toConversation()) + assertNull(dao.findWithConversationKey(accountKey = accountKey, conversationKey = list.first().conversationKey)) + assertEquals(0, database.dMEventQueries.getMessagesPagingCount(accountKey = accountKey, conversationKey = list.first().conversationKey).executeAsOne()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt index d44479180..314049d66 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt @@ -37,10 +37,7 @@ internal class SqlDelightDirectMessageEventDaoImplTest : BaseCacheDatabaseTest() override fun setUp() { super.setUp() dao = SqlDelightDirectMessageEventDaoImpl( - dmEventQueries = database.dMEventQueries, - urlEntityQueries = database.urlEntityQueries, - mediaQueries = database.mediaQueries, - userQueries = database.userQueries + database = database ) } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt new file mode 100644 index 000000000..385978c94 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt @@ -0,0 +1,79 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbDMConversation +import com.twidere.twiderex.mock.model.mockIDirectMessage +import com.twidere.twiderex.mock.model.mockIUser +import com.twidere.twiderex.mock.model.toConversation +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class DMConversationQueriesImplTest : BaseCacheDatabaseTest() { + @Test + fun insertAll_ReplaceWhenUniqueIndexEquals() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val insert = mockIDirectMessage(accountId = accountKey.id, otherUserID = "other") + .toUi(accountKey, mockIUser(id = "other").toUi(accountKey)).toConversation().toDbDMConversation() + database.dMConversationQueries.insert(insert.copy(conversationName = "insert")) + assertEquals("insert", database.dMConversationQueries.findWithConversationKey(accountKey = accountKey, conversationKey = insert.conversationKey).executeAsOneOrNull()?.conversationName) + database.dMConversationQueries.insert(insert.copy(conversationName = "replace")) + assertEquals("replace", database.dMConversationQueries.findWithConversationKey(accountKey = accountKey, conversationKey = insert.conversationKey).executeAsOneOrNull()?.conversationName) + } + + @Test + fun delete_DeleteWithUniqueIndex() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val insert = mockIDirectMessage(accountId = accountKey.id, inCome = false).toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + .toConversation().toDbDMConversation() + database.dMConversationQueries.insert(insert) + database.dMConversationQueries.delete( + accountKey = insert.accountKey, + conversationKey = MicroBlogKey.twitter("random"), + ) + database.dMConversationQueries.delete( + accountKey = MicroBlogKey.twitter("random"), + conversationKey = insert.conversationKey, + ) + assertNotNull( + database.dMConversationQueries.findWithConversationKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + ).executeAsOneOrNull() + ) + database.dMConversationQueries.delete( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + ) + assertNull( + database.dMConversationQueries.findWithConversationKey( + accountKey = insert.accountKey, + conversationKey = insert.conversationKey, + ).executeAsOneOrNull() + ) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt index 7f6f0ecbb..caa9bb270 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt @@ -26,6 +26,7 @@ import com.twidere.twiderex.db.sqldelight.transform.toDbEventWithAttachments import com.twidere.twiderex.mock.model.mockIDirectMessage import com.twidere.twiderex.mock.model.mockIUser import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Test import kotlin.test.assertEquals @@ -108,15 +109,53 @@ internal class DMEventQueriesImplTest : BaseCacheDatabaseTest() { } @Test - fun getMessageCount_ReturnsCountMatchesAccountKeyAndConversationKey() = runBlocking { + fun getMessagesPagingCount_ReturnsCountMatchesAccountKeyAndConversationKey() = runBlocking { val accountKey = MicroBlogKey.twitter("accountId") val insert = mockIDirectMessage(accountId = accountKey.id, inCome = false).toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("1")).toDbEventWithAttachments().event) database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("2")).toDbEventWithAttachments().event) database.dMEventQueries.insert(insert.copy(messageKey = MicroBlogKey.twitter("3")).toDbEventWithAttachments().event) - assertEquals(3, database.dMEventQueries.getMessageCount(accountKey = accountKey, conversationKey = insert.conversationKey).executeAsOne()) + assertEquals(3, database.dMEventQueries.getMessagesPagingCount(accountKey = accountKey, conversationKey = insert.conversationKey).executeAsOne()) - assertEquals(0, database.dMEventQueries.getMessageCount(accountKey = MicroBlogKey.twitter("empty"), conversationKey = insert.conversationKey).executeAsOne()) + assertEquals(0, database.dMEventQueries.getMessagesPagingCount(accountKey = MicroBlogKey.twitter("empty"), conversationKey = insert.conversationKey).executeAsOne()) + } + + @Test + fun getLatestMessagesInEachConversation_ReturnsLatestMessagesInEachConversationAndOrderBySortIdDesc() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val conversation1 = mockIDirectMessage(accountId = accountKey.id, inCome = false, otherUserID = "user1").toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + val conversation2 = mockIDirectMessage(accountId = accountKey.id, inCome = false, otherUserID = "user2").toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + database.dMEventQueries.insert(conversation1.copy(messageKey = MicroBlogKey.twitter("1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + delay(1) + database.dMEventQueries.insert(conversation1.copy(messageKey = MicroBlogKey.twitter("latest"), recipientAccountKey = MicroBlogKey.twitter("user1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + + database.dMEventQueries.insert(conversation2.copy(messageKey = MicroBlogKey.twitter("1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + delay(1) + database.dMEventQueries.insert(conversation2.copy(messageKey = MicroBlogKey.twitter("latest"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + + val result = database.dMEventQueries.getLatestMessagesInEachConversation(accountKey).executeAsList() + + assertEquals(2, result.size) + result.forEach { + print(it.toString()) + assertEquals("latest", it.messageKey.id) + } + } + + @Test + fun getLatestMessagesInEachConversation_PagingCount_ReturnsCountMatchesConversationCount() = runBlocking { + val accountKey = MicroBlogKey.twitter("accountId") + val conversation1 = mockIDirectMessage(accountId = accountKey.id, inCome = false, otherUserID = "user1").toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + val conversation2 = mockIDirectMessage(accountId = accountKey.id, inCome = false, otherUserID = "user2").toUi(accountKey, mockIUser(id = accountKey.id).toUi(accountKey)) + database.dMEventQueries.insert(conversation1.copy(messageKey = MicroBlogKey.twitter("1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + delay(1) + database.dMEventQueries.insert(conversation1.copy(messageKey = MicroBlogKey.twitter("latest"), recipientAccountKey = MicroBlogKey.twitter("user1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + + database.dMEventQueries.insert(conversation2.copy(messageKey = MicroBlogKey.twitter("1"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + delay(1) + database.dMEventQueries.insert(conversation2.copy(messageKey = MicroBlogKey.twitter("latest"), sortId = System.currentTimeMillis()).toDbEventWithAttachments().event) + + assertEquals(2, database.dMEventQueries.getLatestMessagesInEachConversationPagingCount(accountKey = accountKey).executeAsOne()) } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 5091b7fd6..5b54d7d0f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -51,6 +51,8 @@ import com.twidere.services.twitter.model.UserV2 import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MediaType +import com.twidere.twiderex.model.ui.UiDMConversation +import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiDraft import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch @@ -231,3 +233,15 @@ fun mockUiUrlEntity(url: String = "") = UiUrlEntity( description = "description", image = "image" ) + +@TestOnly +fun UiDMEvent.toConversation() = UiDMConversation( + accountKey = accountKey, + conversationId = conversationKey.id, + conversationKey = conversationKey, + conversationAvatar = sender.profileImage.toString(), + conversationName = sender.name, + conversationSubName = sender.screenName, + conversationType = UiDMConversation.Type.ONE_TO_ONE, + recipientKey = conversationUserKey +) From 78ef5aa56eac14be78602bbb6c0f1a67d8c09a78 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 27 Sep 2021 17:36:10 +0800 Subject: [PATCH 253/615] add sqldelight table for UiTrend and tests --- .../sqldelight/adapter/TrendAdapterFactory.kt | 42 +++++++ .../sqldelight/dao/SqlDelightTrendDaoImpl.kt | 62 ++++++++++ .../twiderex/db/sqldelight/database.kt | 6 +- .../db/sqldelight/model/DbTrendWithHistory.kt | 47 +++++++ .../db/sqldelight/transform/TrendTransform.kt | 65 ++++++++++ .../twiderex/sqldelight/table/Trend.sq | 27 ++++ .../twiderex/sqldelight/table/TrendHistory.sq | 22 ++++ .../db/dao/SqlDelightTrendDaoImplTest.kt | 115 ++++++++++++++++++ .../sqldelight/TrendHistoryQueriesImplTest.kt | 44 +++++++ .../db/sqldelight/TrendQueriesImplTest.kt | 58 +++++++++ .../transform/TrendTransformTest.kt | 68 +++++++++++ .../twidere/twiderex/mock/model/MockModels.kt | 4 +- 12 files changed, 557 insertions(+), 3 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt new file mode 100644 index 000000000..7d571cfc7 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt @@ -0,0 +1,42 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.Trend +import com.twidere.twiderex.sqldelight.table.TrendHistory + +object TrendAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + Trend.Adapter( + trendKeyAdapter = it, + accountKeyAdapter = it, + ) + } +} + +object TrendHistoryAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + TrendHistory.Adapter( + trendKeyAdapter = it, + accountKeyAdapter = it, + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt new file mode 100644 index 000000000..dc22c9c82 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt @@ -0,0 +1,62 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.db.dao.TrendDao +import com.twidere.twiderex.db.sqldelight.model.DbTrendWithHistory.Companion.saveToDb +import com.twidere.twiderex.db.sqldelight.model.DbTrendWithHistory.Companion.withHistory +import com.twidere.twiderex.db.sqldelight.paging.QueryPagingSource +import com.twidere.twiderex.db.sqldelight.query.flatMap +import com.twidere.twiderex.db.sqldelight.transform.toDbTrendWithHistory +import com.twidere.twiderex.db.sqldelight.transform.toUi +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase + +internal class SqlDelightTrendDaoImpl(private val database: SqlDelightCacheDatabase) : TrendDao { + override suspend fun insertAll(trends: List) { + trends.map { it.toDbTrendWithHistory() }.saveToDb(database) + } + + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return QueryPagingSource( + countQuery = database.trendQueries.getTrendPagingCount(accountKey = accountKey), + transacter = database.trendQueries, + queryProvider = { limit, offset -> + database.trendQueries.getTrendPagingList( + accountKey = accountKey, + limit = limit, + offset = offset + ).flatMap { + it.withHistory(database).toUi() + } + } + ) + } + + override suspend fun clear(accountKey: MicroBlogKey) { + database.transaction { + database.trendQueries.clear(accountKey = accountKey) + database.trendHistoryQueries.clear(accountKey = accountKey) + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt index 201cc3404..01ec115db 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -26,6 +26,8 @@ import com.twidere.twiderex.db.sqldelight.adapter.DMEventAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.MediaAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.TrendAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.TrendHistoryAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.UrlEntityAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.UserAdapterFactory import com.twidere.twiderex.sqldelight.SqlDelightAppDatabase @@ -48,6 +50,8 @@ internal fun createCacheDataBase(driver: SqlDriver): SqlDelightCacheDatabase { MediaAdapter = MediaAdapterFactory.create(), UrlEntityAdapter = UrlEntityAdapterFactory.create(), UserAdapter = UserAdapterFactory.create(), - DMConversationAdapter = DMConversationAdapterFactory.create() + DMConversationAdapter = DMConversationAdapterFactory.create(), + TrendAdapter = TrendAdapterFactory.create(), + TrendHistoryAdapter = TrendHistoryAdapterFactory.create() ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt new file mode 100644 index 000000000..e60f33319 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt @@ -0,0 +1,47 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.model + +import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase +import com.twidere.twiderex.sqldelight.table.Trend +import com.twidere.twiderex.sqldelight.table.TrendHistory + +internal data class DbTrendWithHistory( + val trend: Trend, + val history: List +) { + companion object { + fun List.saveToDb(database: SqlDelightCacheDatabase) { + database.transaction { + forEach { database.trendQueries.insert(it.trend) } + map { it.history }.flatten().forEach { database.trendHistoryQueries.insert(it) } + } + } + + fun Trend.withHistory(database: SqlDelightCacheDatabase) = DbTrendWithHistory( + trend = this, + history = database.trendHistoryQueries.findWithTrendKey( + trendKey = trendKey, + accountKey = accountKey + ).executeAsList() + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt new file mode 100644 index 000000000..497e89ed4 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt @@ -0,0 +1,65 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.db.sqldelight.model.DbTrendWithHistory +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.model.ui.UiTrendHistory +import com.twidere.twiderex.sqldelight.table.Trend +import com.twidere.twiderex.sqldelight.table.TrendHistory + +internal fun UiTrendHistory.toDbTrendHistory(accountKey: MicroBlogKey) = TrendHistory( + trendKey = trendKey, + accountKey = accountKey, + day = day, + uses = uses, + accounts = accounts +) + +internal fun UiTrend.toDbTrendWithHistory() = DbTrendWithHistory( + trend = Trend( + trendKey = trendKey, + accountKey = accountKey, + displayName = displayName, + url = url, + query = query, + volume = volume + ), + history = history.map { it.toDbTrendHistory(accountKey) } +) + +internal fun TrendHistory.toUi() = UiTrendHistory( + trendKey = trendKey, + day = day, + uses = uses, + accounts = accounts +) + +internal fun DbTrendWithHistory.toUi() = UiTrend( + trendKey = trend.trendKey, + accountKey = trend.accountKey, + displayName = trend.displayName, + url = trend.url, + query = trend.query, + volume = trend.volume, + history = history.map { it.toUi() } +) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq new file mode 100644 index 000000000..765b9ecb4 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq @@ -0,0 +1,27 @@ +import com.twidere.twiderex.model.MicroBlogKey; + +CREATE TABLE Trend ( + trendKey TEXT AS MicroBlogKey NOT NULL, + accountKey TEXT AS MicroBlogKey NOT NULL, + displayName TEXT NOT NULL, + url TEXT NOT NULL, + query TEXT NOT NULL, + volume INTEGER NOT NULL +); + + +CREATE UNIQUE INDEX IF NOT EXISTS index_trendKey_accountKey ON Trend (trendKey, accountKey); + +insert: +INSERT OR REPLACE INTO Trend( + trendKey, accountKey, displayName, url, query, volume +) VALUES ?; + +getTrendPagingList: +SELECT * FROM Trend WHERE accountKey == :accountKey LIMIT :limit OFFSET :offset; + +getTrendPagingCount: +SELECT COUNT(*) FROM Trend WHERE accountKey == :accountKey; + +clear: +DELETE FROM Trend WHERE accountKey == :accountKey; diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq new file mode 100644 index 000000000..0d329d3c7 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq @@ -0,0 +1,22 @@ +import com.twidere.twiderex.model.MicroBlogKey; + +CREATE TABLE TrendHistory ( + trendKey TEXT AS MicroBlogKey NOT NULL, + accountKey TEXT AS MicroBlogKey NOT NULL, + day Integer NOT NULL, + uses Integer NOT NULL, + accounts Integer NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_trendKey_accountKey_day ON TrendHistory (trendKey, day, accountKey); + +insert: +INSERT OR REPLACE INTO TrendHistory( + trendKey, accountKey, day, uses, accounts +) VALUES ?; + +findWithTrendKey: +SELECT * FROM TrendHistory WHERE trendKey == :trendKey AND accountKey == :accountKey ORDER BY day; + +clear: +DELETE FROM TrendHistory WHERE accountKey == :accountKey AND accountKey == :accountKey; diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt new file mode 100644 index 000000000..49aefe894 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt @@ -0,0 +1,115 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightTrendDaoImpl +import com.twidere.twiderex.mock.model.mockITrend +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class SqlDelightTrendDaoImplTest : BaseCacheDatabaseTest() { + private lateinit var dao: SqlDelightTrendDaoImpl + private val accountKey = MicroBlogKey.twitter("account") + override fun setUp() { + super.setUp() + dao = SqlDelightTrendDaoImpl( + database = database + ) + } + + @Test + fun insertAll_InsertBothTrendAndHistoryToDb() = runBlocking { + val list = listOf( + mockITrend(name = "trend1").toUi(accountKey), + mockITrend(name = "trend2").toUi(accountKey), + mockITrend(name = "trend3").toUi(accountKey), + ) + dao.insertAll(list) + val trends = database.trendQueries.getTrendPagingList(accountKey = accountKey, limit = 10, offset = 0).executeAsList() + assertEquals(3, trends.size) + trends.forEach { + val histories = database.trendHistoryQueries.findWithTrendKey(trendKey = it.trendKey, accountKey = accountKey).executeAsList() + assert(histories.isNotEmpty()) + } + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val list = listOf( + mockITrend(name = "trend1").toUi(accountKey), + mockITrend(name = "trend2").toUi(accountKey), + mockITrend(name = "trend3").toUi(accountKey), + ) + dao.insertAll(list) + val pagingSource = dao.getPagingSource( + accountKey = accountKey, + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpdate() = runBlocking { + val trend = mockITrend(name = "trend1").toUi(accountKey) + var invalidate = false + dao.getPagingSource( + accountKey = accountKey, + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) + } + dao.insertAll(listOf(trend)) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun clearAll_ClearAllTrendAndTrendHistoryWithMatchesGivenAccountKey() = runBlocking { + val list = listOf( + mockITrend(name = "trend1").toUi(accountKey), + mockITrend(name = "trend2").toUi(accountKey), + mockITrend(name = "trend3").toUi(accountKey), + ) + dao.insertAll(list) + dao.clear(accountKey) + list.forEach { + assert(database.trendHistoryQueries.findWithTrendKey(trendKey = it.trendKey, accountKey = accountKey).executeAsList().isEmpty()) + } + assertEquals(0, database.trendQueries.getTrendPagingCount(accountKey = accountKey).executeAsOne()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt new file mode 100644 index 000000000..2889e1ff2 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbTrendWithHistory +import com.twidere.twiderex.mock.model.mockITrend +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class TrendHistoryQueriesImplTest : BaseCacheDatabaseTest() { + private val accountKey = MicroBlogKey.twitter("account") + @Test + fun insert_ReplaceWhenUniqueKeyEquals() = runBlocking { + val insert = mockITrend().toUi(accountKey).toDbTrendWithHistory() + database.trendHistoryQueries.insert(insert.history.first().copy(uses = 10)) + assertEquals(10, database.trendHistoryQueries.findWithTrendKey(accountKey = accountKey, trendKey = insert.trend.trendKey).executeAsOne().uses) + database.trendHistoryQueries.insert(insert.history.first().copy(uses = 20)) + assertEquals(20, database.trendHistoryQueries.findWithTrendKey(accountKey = accountKey, trendKey = insert.trend.trendKey).executeAsOne().uses) + database.trendHistoryQueries.insert(insert.history.first().copy(day = 20)) + assertEquals(2, database.trendHistoryQueries.findWithTrendKey(accountKey = accountKey, trendKey = insert.trend.trendKey).executeAsList().size) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt new file mode 100644 index 000000000..272837485 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbTrendWithHistory +import com.twidere.twiderex.mock.model.mockITrend +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.sqldelight.table.Trend +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class TrendQueriesImplTest : BaseCacheDatabaseTest() { + private val accountKey = MicroBlogKey.twitter("account") + @Test + fun insert_ReplaceWhenUniqueKeyEquals() = runBlocking { + val insert = mockITrend().toUi(accountKey).toDbTrendWithHistory() + database.trendQueries.insert(insert.trend.copy(displayName = "insert")) + assertEquals("insert", database.trendQueries.getTrendPagingList(accountKey = accountKey, limit = 10, offset = 0).executeAsOne().displayName) + database.trendQueries.insert(insert.trend.copy(displayName = "replace")) + assertEquals("replace", database.trendQueries.getTrendPagingList(accountKey = accountKey, limit = 10, offset = 0).executeAsOne().displayName) + } + + @Test + fun getTrendPagingList_ReturnResultsWithGiveOffsetAndLimit() = runBlocking { + val list = mutableListOf() + for (i in 0 until 10) { + list.add(mockITrend().toUi(accountKey).toDbTrendWithHistory().trend.copy(trendKey = MicroBlogKey.valueOf(i.toString()), displayName = i.toString())) + } + database.trendQueries.transaction { + list.forEach { database.trendQueries.insert(it) } + } + assertEquals(10, database.trendQueries.getTrendPagingCount(accountKey = accountKey).executeAsOne()) + val result = database.trendQueries.getTrendPagingList(accountKey = accountKey, limit = 4, offset = 3).executeAsList() + assertEquals(4, result.size) + assertEquals(3, result.first().displayName.toInt()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt new file mode 100644 index 000000000..646302535 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt @@ -0,0 +1,68 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.db.sqldelight.model.DbTrendWithHistory +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiTrend +import com.twidere.twiderex.model.ui.UiTrendHistory +import org.junit.Test +import kotlin.test.assertEquals + +internal class TrendTransformTest { + @Test + fun transform() { + val ui = UiTrend( + accountKey = MicroBlogKey.twitter("account"), + trendKey = MicroBlogKey.twitter("trendKey"), + displayName = "displaName", + query = "query", + url = "url", + volume = 100, + history = listOf( + UiTrendHistory( + trendKey = MicroBlogKey.twitter("trendKey"), + day = 123, + uses = 321, + accounts = 111 + ) + ) + ) + val db = ui.toDbTrendWithHistory() + assertSuccess(db, ui) + + val uiFromDb = db.toUi() + assertSuccess(db, uiFromDb) + } + + private fun assertSuccess(db: DbTrendWithHistory, ui: UiTrend) { + assertEquals(db.trend.accountKey, ui.accountKey) + assertEquals(db.trend.trendKey, ui.trendKey) + assertEquals(db.trend.displayName, ui.displayName) + assertEquals(db.trend.query, ui.query) + assertEquals(db.trend.url, ui.url) + assertEquals(db.trend.volume, ui.volume) + assertEquals(db.history.first().trendKey, ui.history.first().trendKey) + assertEquals(db.history.first().day, ui.history.first().day) + assertEquals(db.history.first().uses, ui.history.first().uses) + assertEquals(db.history.first().accounts, ui.history.first().accounts) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 5b54d7d0f..4b91b0f1d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -113,9 +113,9 @@ fun mockIUser(id: String = UUID.randomUUID().toString(), name: String = ""): IUs } @TestOnly -fun mockITrend(): ITrend { +fun mockITrend(name: String = "trend timestamp:${System.currentTimeMillis()}"): ITrend { return Trend( - name = "trend timestamp:${System.currentTimeMillis()}", + name = name, url = "https://trend", history = mutableListOf( From 952bd8b0df5c4a1944b2b7383f163c03acda50cc Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 27 Sep 2021 17:43:05 +0800 Subject: [PATCH 254/615] fixed androidTest failed issue --- .../cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq | 2 ++ .../kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt | 3 +-- .../kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq index 2ca9c3c60..24438dbe4 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq @@ -4,4 +4,6 @@ clearAllTables{ DELETE FROM UrlEntity; DELETE FROM User; DELETE FROM DMConversation; + DELETE FROM Trend; + DELETE FROM TrendHistory; } \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index 698013f78..6c89038a8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -26,11 +26,10 @@ import org.junit.After import org.junit.Before internal open class BaseAppDatabaseTest { - protected lateinit var database: SqlDelightAppDatabase private val driver = SqlDriverFactory.create(SqlDelightAppDatabase.Schema) + protected val database: SqlDelightAppDatabase = createAppDataBase(driver) @Before fun setUp() { - database = createAppDataBase(driver) } @After diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt index e0af90633..bd960def5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt @@ -26,11 +26,10 @@ import org.junit.After import org.junit.Before internal open class BaseCacheDatabaseTest { - protected lateinit var database: SqlDelightCacheDatabase private val driver = SqlDriverFactory.create(SqlDelightCacheDatabase.Schema) + protected val database: SqlDelightCacheDatabase = createCacheDataBase(driver) @Before open fun setUp() { - database = createCacheDataBase(driver) } @After From 61bd423338ca463b4112e8850dc267dddb981c4c Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 27 Sep 2021 17:48:09 +0800 Subject: [PATCH 255/615] add implementation for desktop --- .../di/modules/PlatformModule.android.kt | 2 +- .../twidere/twiderex/kmp/LocationProvider.kt | 5 +- .../worker/status/UpdateStatusWorker.kt | 23 ++--- .../twidere/twiderex/di/modules/JobsModule.kt | 2 + .../extensions/CoroutineScopeExtensions.kt | 40 ++++++++ .../twiderex/jobs/draft/SaveDraftJob.kt | 1 - .../twiderex/jobs/status/UpdateStatusJob.kt | 54 +++++++++++ .../twidere/twiderex/kmp/LocationProvider.kt | 4 +- .../compose/ComposeSearchUserViewModel.kt | 34 +++---- .../viewmodel/compose/ComposeViewModel.kt | 13 +-- .../lists/ListsSearchUserViewModel.kt | 36 ++++--- .../viewmodel/timeline/TimelineViewModel.kt | 12 +-- .../twidere/twiderex/action/ComposeAction.kt | 35 ++++++- .../twiderex/action/DirectMessageAction.kt | 19 +++- .../twidere/twiderex/action/DraftAction.kt | 17 +++- .../twidere/twiderex/action/MediaAction.kt | 39 +++++++- .../twidere/twiderex/action/StatusActions.kt | 97 ++++++++++++++++++- .../di/modules/ActionsModule.desktop.kt | 10 ++ .../twiderex/di/modules/KmpModule.desktop.kt | 10 ++ .../di/modules/PlatformModule.desktop.kt | 6 +- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 10 +- .../com/twidere/twiderex/kmp/FileResolver.kt | 32 +++++- .../twidere/twiderex/kmp/LocationProvider.kt | 8 +- .../twidere/twiderex/kmp/RemoteNavigator.kt | 6 ++ .../model/AccountPreferencesFactory.kt | 21 +++- 25 files changed, 446 insertions(+), 90 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt index 207573625..cacc07b9b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt @@ -83,7 +83,7 @@ private fun Module.workManager() { worker { RetweetWorker(get(), get(), get()) } worker { UnLikeWorker(get(), get(), get()) } worker { UnRetweetWorker(get(), get(), get()) } - worker { UpdateStatusWorker(get(), get(), get(), get()) } + worker { UpdateStatusWorker(get(), get(), get()) } worker { RemoveDraftWorker(get(), get(), get()) } worker { SaveDraftWorker(get(), get(), get()) } worker { DirectMessageDeleteWorker(get(), get(), get()) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index ef383b6d7..21962dd87 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -27,15 +27,16 @@ import android.location.LocationManager import android.os.Bundle import androidx.annotation.RequiresPermission import com.twidere.twiderex.model.kmp.Location +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow actual class LocationProvider( private val locationManager: LocationManager, ) : LocationListener { private val _location = MutableStateFlow(null) - actual val location: SharedFlow = _location.asSharedFlow() + actual val location: Flow + get() = _location.asSharedFlow() @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION]) actual fun enable() { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index 23c4014f4..89cbbc4b2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -29,16 +29,14 @@ import androidx.work.setInputMerger import com.twidere.twiderex.db.transform.toWorkData import com.twidere.twiderex.extensions.getNullableBoolean import com.twidere.twiderex.extensions.getNullableLong +import com.twidere.twiderex.jobs.status.UpdateStatusJob import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.job.StatusResult -import com.twidere.twiderex.repository.ReactionRepository -import com.twidere.twiderex.repository.StatusRepository class UpdateStatusWorker( appContext: Context, params: WorkerParameters, - private val repository: ReactionRepository, - private val statusRepository: StatusRepository, + private val updateStatusJob: UpdateStatusJob, ) : CoroutineWorker(appContext, params) { companion object { fun create(statusResult: StatusResult? = null) = OneTimeWorkRequestBuilder() @@ -62,15 +60,14 @@ class UpdateStatusWorker( val retweeted = inputData.getNullableBoolean("retweeted") val retweetCount = inputData.getNullableLong("retweetCount") val likeCount = inputData.getNullableLong("likeCount") - repository.updateReaction(accountKey = accountKey, statusKey = statusKey, liked = liked, retweet = retweeted) - statusRepository.updateStatus(statusKey = statusKey, accountKey = accountKey) { - it.copy( - metrics = it.metrics.copy( - retweet = retweetCount ?: it.metrics.retweet, - like = likeCount ?: it.metrics.like - ) - ) - } + updateStatusJob.execute( + accountKey = accountKey, + statusKey = statusKey, + liked = liked, + likeCount = likeCount, + retweeted = retweeted, + retweetCount = retweetCount, + ) return Result.success() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt index 2c0bb41b0..5e64c2317 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt @@ -37,6 +37,7 @@ import com.twidere.twiderex.jobs.status.MastodonVoteJob import com.twidere.twiderex.jobs.status.RetweetStatusJob import com.twidere.twiderex.jobs.status.UnRetweetStatusJob import com.twidere.twiderex.jobs.status.UnlikeStatusJob +import com.twidere.twiderex.jobs.status.UpdateStatusJob import org.koin.core.module.Module import org.koin.dsl.module @@ -56,6 +57,7 @@ private fun Module.status() { single { RetweetStatusJob(get(), get(), get()) } single { UnlikeStatusJob(get(), get(), get()) } single { UnRetweetStatusJob(get(), get(), get()) } + single { UpdateStatusJob(get(), get()) } } private fun Module.draft() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt new file mode 100644 index 000000000..df461988d --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt @@ -0,0 +1,40 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.extensions + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +fun CoroutineScope.launchCatching( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +): Job = launch( + context, start +) { + runCatching { + block() + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt index 903cf4165..6af5fd044 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt @@ -40,7 +40,6 @@ class SaveDraftJob( draftId = draftId, excludedReplyUserIds = excludedReplyUserIds, ) - true } catch (e: Throwable) { inAppNotification.notifyError(e) throw e diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt new file mode 100644 index 000000000..db9dad7e9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt @@ -0,0 +1,54 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.jobs.status + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.repository.ReactionRepository +import com.twidere.twiderex.repository.StatusRepository + +class UpdateStatusJob( + private val repository: ReactionRepository, + private val statusRepository: StatusRepository, +) { + suspend fun execute( + accountKey: MicroBlogKey, + statusKey: MicroBlogKey, + liked: Boolean? = null, + likeCount: Long? = null, + retweeted: Boolean? = null, + retweetCount: Long? = null, + ) { + repository.updateReaction( + accountKey = accountKey, + statusKey = statusKey, + liked = liked, + retweet = retweeted + ) + statusRepository.updateStatus(statusKey = statusKey, accountKey = accountKey) { + it.copy( + metrics = it.metrics.copy( + retweet = retweetCount ?: it.metrics.retweet, + like = likeCount ?: it.metrics.like + ) + ) + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index 53521b7e9..86662bcfe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -21,10 +21,10 @@ package com.twidere.twiderex.kmp import com.twidere.twiderex.model.kmp.Location -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.Flow expect class LocationProvider { - val location: SharedFlow + val location: Flow fun enable() fun disable() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 7e885a5b7..54e540465 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel @@ -47,22 +47,20 @@ class ComposeSearchUserViewModel( val text = MutableStateFlow("") @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - val source = text.debounce(666L).flatMapLatest { - it.takeIf { it.isNotEmpty() }?.let { str -> - account.flatMapLatest { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - str, - account.service as SearchService - ) - }.flow - } - } ?: emptyFlow() + val source = text.debounce(666L).filterNot { it.isEmpty() }.flatMapLatest { str -> + account.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + str, + account.service as SearchService + ) + }.flow + } }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 6a22a3568..de1c0754c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow @@ -169,13 +169,10 @@ open class ComposeViewModel( @OptIn(ExperimentalCoroutinesApi::class) val emojis by lazy { - account.flatMapLatest { account -> - if (account.type == PlatformType.Mastodon) { - MastodonEmojiCache.get(account) - } else { - emptyFlow() - } - }.asStateIn(viewModelScope, emptyList()) + account + .filter { it.type == PlatformType.Mastodon } + .flatMapLatest { MastodonEmojiCache.get(it) } + .asStateIn(viewModelScope, emptyList()) } val draftCount by lazy { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index 14fde01cd..e42093cf5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull import moe.tlaster.precompose.viewmodel.ViewModel @@ -48,23 +48,21 @@ class ListsSearchUserViewModel( val text = MutableStateFlow("") @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - val source = text.debounce(666L).flatMapLatest { - it.takeIf { it.isNotEmpty() }?.let { - account.flatMapLatest { account -> - Pager( - config = PagingConfig( - pageSize = defaultLoadCount, - enablePlaceholders = false, - ) - ) { - SearchUserPagingSource( - accountKey = account.accountKey, - it, - account.service as SearchService, - following = following - ) - }.flow - } - } ?: emptyFlow() + val source = text.debounce(666L).filterNot { it.isEmpty() }.flatMapLatest { + account.flatMapLatest { account -> + Pager( + config = PagingConfig( + pageSize = defaultLoadCount, + enablePlaceholders = false, + ) + ) { + SearchUserPagingSource( + accountKey = account.accountKey, + it, + account.service as SearchService, + following = following + ) + }.flow + } }.cachedIn(viewModelScope) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 5e5dc93f0..3d4dc2f07 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -34,11 +34,9 @@ import com.twidere.twiderex.paging.mediator.paging.toUi import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emitAll -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.transformLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -51,16 +49,14 @@ abstract class TimelineViewModel( @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) val source by lazy { - pagingMediator.transformLatest { - it?.let { - emitAll(it.pager().toUi()) - } + pagingMediator.mapNotNull { it }.flatMapLatest { + it.pager().toUi() }.cachedIn(viewModelScope) } @OptIn(ExperimentalCoroutinesApi::class) val loadingBetween: Flow> by lazy { - pagingMediator.flatMapLatest { it?.loadingBetween ?: emptyFlow() } + pagingMediator.mapNotNull { it }.flatMapLatest { it.loadingBetween } .asStateIn(viewModelScope, emptyList()) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index d0b5aae18..9f3c319b7 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -20,11 +20,44 @@ */ package com.twidere.twiderex.action +import com.twidere.twiderex.extensions.launchCatching +import com.twidere.twiderex.jobs.compose.MastodonComposeJob +import com.twidere.twiderex.jobs.compose.TwitterComposeJob +import com.twidere.twiderex.jobs.draft.RemoveDraftJob +import com.twidere.twiderex.jobs.draft.SaveDraftJob +import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.ComposeData +import com.twidere.twiderex.repository.AccountRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull + +actual class ComposeAction( + private val repository: AccountRepository, + private val saveDraftJob: SaveDraftJob, + private val removeDraftJob: RemoveDraftJob, + private val twitterComposeJob: TwitterComposeJob, + private val mastodonComposeJob: MastodonComposeJob, +) { + + private val scope = CoroutineScope(Dispatchers.IO) -actual class ComposeAction { actual fun commit( data: ComposeData, ) { + scope.launchCatching { + repository.activeAccount.firstOrNull()?.toUi()?.let { account -> + val platformType = account.platformType + val accountKey = account.userKey + saveDraftJob.execute(data) + when (platformType) { + PlatformType.Twitter -> twitterComposeJob.execute(data, accountKey) + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> mastodonComposeJob.execute(data, accountKey) + } + removeDraftJob.execute(data.draftId) + } + } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt index 943f7c242..012214283 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -20,19 +20,36 @@ */ package com.twidere.twiderex.action +import com.twidere.twiderex.extensions.launchCatching +import com.twidere.twiderex.jobs.dm.DirectMessageDeleteJob +import com.twidere.twiderex.jobs.dm.TwitterDirectMessageSendJob import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.job.DirectMessageDeleteData import com.twidere.twiderex.model.job.DirectMessageSendData +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers -actual class DirectMessageAction { +actual class DirectMessageAction( + private val twitterDirectMessageSendJob: TwitterDirectMessageSendJob, + private val directMessageDeleteJob: DirectMessageDeleteJob, +) { + private val scope = CoroutineScope(Dispatchers.IO) actual fun send( platformType: PlatformType, data: DirectMessageSendData, ) { + scope.launchCatching { + if (platformType == PlatformType.Twitter) { + twitterDirectMessageSendJob.execute(data, data.accountKey) + } + } } actual fun delete( data: DirectMessageDeleteData ) { + scope.launchCatching { + directMessageDeleteJob.execute(data, data.accountKey) + } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt index 52a1c01e1..56d8e001b 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -20,12 +20,27 @@ */ package com.twidere.twiderex.action +import com.twidere.twiderex.extensions.launchCatching +import com.twidere.twiderex.jobs.draft.RemoveDraftJob +import com.twidere.twiderex.jobs.draft.SaveDraftJob import com.twidere.twiderex.model.job.ComposeData +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers -actual class DraftAction { +actual class DraftAction( + private val removeDraftJob: RemoveDraftJob, + private val saveDraftJob: SaveDraftJob, +) { + private val scope = CoroutineScope(Dispatchers.IO) actual fun delete(id: String) { + scope.launchCatching { + removeDraftJob.execute(id) + } } actual fun save(composeData: ComposeData) { + scope.launchCatching { + saveDraftJob.execute(composeData) + } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index f44285658..bd617ee5e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -20,16 +20,53 @@ */ package com.twidere.twiderex.action +import com.twidere.twiderex.extensions.launchCatching +import com.twidere.twiderex.jobs.common.DownloadMediaJob +import com.twidere.twiderex.jobs.common.ShareMediaJob import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import java.io.File +import java.net.URI -actual class MediaAction { +actual class MediaAction( + private val downloadMediaJob: DownloadMediaJob, + private val shareMediaJob: ShareMediaJob, +) { + private val scope = CoroutineScope(Dispatchers.IO) actual fun download( source: String, target: String, accountKey: MicroBlogKey ) { + scope.launchCatching { + downloadMediaJob.execute( + target = target, + source = source, + accountKey = accountKey, + ) + } } actual fun share(source: String, accountKey: MicroBlogKey) { + scope.launchCatching { + val f = File(URI(source)) + val target = File.createTempFile( + f.nameWithoutExtension, + f.extension.let { + if (it.isEmpty()) { + null + } else { + ".$it" + } + }, + ).absolutePath + downloadMediaJob.execute( + target = target, + source = source, + accountKey = accountKey, + ) + shareMediaJob.execute(target) + } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt index 79193d0e5..987d2cf5c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -20,19 +20,114 @@ */ package com.twidere.twiderex.action +import com.twidere.twiderex.extensions.launchCatching +import com.twidere.twiderex.jobs.database.DeleteDbStatusJob +import com.twidere.twiderex.jobs.status.DeleteStatusJob +import com.twidere.twiderex.jobs.status.LikeStatusJob +import com.twidere.twiderex.jobs.status.MastodonVoteJob +import com.twidere.twiderex.jobs.status.RetweetStatusJob +import com.twidere.twiderex.jobs.status.UnRetweetStatusJob +import com.twidere.twiderex.jobs.status.UnlikeStatusJob +import com.twidere.twiderex.jobs.status.UpdateStatusJob import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.ui.UiStatus +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +actual class StatusActions( + private val deleteStatusJob: DeleteStatusJob, + private val deleteDbStatusJob: DeleteDbStatusJob, + private val updateStatusJob: UpdateStatusJob, + private val likeStatusJob: LikeStatusJob, + private val unlikeStatusJob: UnlikeStatusJob, + private val retweetStatusJob: RetweetStatusJob, + private val unRetweetStatusJob: UnRetweetStatusJob, + private val mastodonVoteJob: MastodonVoteJob, +) : IStatusActions { + private val scope = CoroutineScope(Dispatchers.IO) -actual class StatusActions : IStatusActions { actual override fun delete(status: UiStatus, account: AccountDetails) { + scope.launchCatching { + deleteStatusJob.execute(accountKey = account.accountKey, statusKey = status.statusKey) + deleteDbStatusJob.execute(statusKey = status.statusKey) + } } actual override fun like(status: UiStatus, account: AccountDetails) { + scope.launchCatching { + updateStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + liked = !status.liked + ) + val ( + _, + _, + retweeted, + liked, + retweetCount, + likeCount, + ) = if (status.liked) { + unlikeStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + ) + } else { + likeStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + ) + } + updateStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + liked = liked, + likeCount = likeCount, + retweeted = retweeted, + retweetCount = retweetCount, + ) + } } actual override fun retweet(status: UiStatus, account: AccountDetails) { + scope.launchCatching { + updateStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + retweeted = !status.retweeted + ) + val ( + _, + _, + retweeted, + liked, + retweetCount, + likeCount, + ) = if (status.retweeted) { + unRetweetStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + ) + } else { + retweetStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + ) + } + updateStatusJob.execute( + accountKey = account.accountKey, + statusKey = status.statusKey, + liked = liked, + likeCount = likeCount, + retweeted = retweeted, + retweetCount = retweetCount, + ) + } } actual override fun vote(status: UiStatus, account: AccountDetails, votes: List) { + scope.launchCatching { + mastodonVoteJob.execute(votes, account.accountKey, status.statusKey) + } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt index fef162556..a4ef1741d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt @@ -20,7 +20,17 @@ */ package com.twidere.twiderex.di.modules +import com.twidere.twiderex.action.ComposeAction +import com.twidere.twiderex.action.DirectMessageAction +import com.twidere.twiderex.action.DraftAction +import com.twidere.twiderex.action.MediaAction +import com.twidere.twiderex.action.StatusActions import org.koin.dsl.module actual val actionModule = module { + single { ComposeAction(get(), get(), get(), get(), get()) } + single { DirectMessageAction(get(), get()) } + single { DraftAction(get(), get()) } + single { MediaAction(get(), get()) } + single { StatusActions(get(), get(), get(), get(), get(), get(), get(), get()) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt index d8fb7b063..af28c1a81 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt @@ -20,7 +20,17 @@ */ package com.twidere.twiderex.di.modules +import com.twidere.twiderex.kmp.ExifScrambler +import com.twidere.twiderex.kmp.FileResolver +import com.twidere.twiderex.kmp.LocationProvider +import com.twidere.twiderex.kmp.RemoteNavigator +import com.twidere.twiderex.notification.AppNotificationManager import org.koin.dsl.module actual val kmpModule = module { + single { FileResolver() } + single { RemoteNavigator() } + single { LocationProvider() } + single { ExifScrambler() } + single { AppNotificationManager() } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt index 82a2740de..c108fb59c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt @@ -21,10 +21,10 @@ package com.twidere.twiderex.di.modules import com.twidere.twiderex.kmp.ResLoader +import com.twidere.twiderex.model.AccountPreferencesFactory import org.koin.dsl.module internal actual val platformModule = module { - single { - ResLoader() - } + single { ResLoader() } + single { AccountPreferencesFactory() } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 14f5d2120..38bfe2e94 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -20,11 +20,19 @@ */ package com.twidere.twiderex.kmp +import java.io.File +import javax.imageio.ImageIO + actual class ExifScrambler { actual fun removeExifData(file: String, compress: Int): String { - TODO("Not yet implemented") + val imgFile = File(file) + val image = ImageIO.read(imgFile) + val result = File.createTempFile(imgFile.name, null) + ImageIO.write(image, imgFile.extension, result) + return result.absolutePath } actual fun deleteCacheFile(file: String) { + File(file).takeIf { it.exists() && it.isFile }?.delete() } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index de0714204..ea976544d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -20,27 +20,49 @@ */ package com.twidere.twiderex.kmp +import java.io.File +import java.io.IOException import java.io.InputStream import java.io.OutputStream +import java.nio.file.Files +import javax.imageio.ImageIO +import javax.imageio.stream.FileImageInputStream actual class FileResolver { actual fun getMimeType(file: String): String? { - TODO("Not yet implemented") + return File(file).takeIf { it.exists() && it.isFile }?.let { + Files.probeContentType(it.toPath()) + } } actual fun getFileSize(file: String): Long? { - TODO("Not yet implemented") + return File(file).takeIf { it.exists() && it.isFile }?.length() } actual fun openInputStream(file: String): InputStream? { - TODO("Not yet implemented") + return File(file).takeIf { it.exists() && it.isFile }?.inputStream() } actual fun openOutputStream(file: String): OutputStream? { - TODO("Not yet implemented") + return File(file).takeIf { it.exists() && it.isFile }?.outputStream() } actual fun getMediaSize(file: String): MediaSize { - TODO("Not yet implemented") + val imgFile = File(file) + val iter = ImageIO.getImageReadersBySuffix(imgFile.extension) + while (iter.hasNext()) { + val reader = iter.next() + try { + val stream = FileImageInputStream(imgFile) + reader.input = stream + val width = reader.getWidth(reader.minIndex) + val height = reader.getHeight(reader.minIndex) + return MediaSize(width.toLong(), height.toLong()) + } catch (e: IOException) { + } finally { + reader.dispose() + } + } + throw IOException("Not a known image file: " + imgFile.absolutePath) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index 3bce50eed..39935b589 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -21,11 +21,13 @@ package com.twidere.twiderex.kmp import com.twidere.twiderex.model.kmp.Location -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +// TODO: implementation actual class LocationProvider { - actual val location: SharedFlow - get() = TODO("Not yet implemented") + actual val location: Flow + get() = flowOf(null) actual fun enable() { } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index 2c2275644..24cc2dfb2 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -20,8 +20,12 @@ */ package com.twidere.twiderex.kmp +import java.awt.Desktop +import java.net.URI + actual class RemoteNavigator { actual fun openDeepLink(deeplink: String, fromBackground: Boolean) { + Desktop.getDesktop().browse(URI(deeplink)) } actual fun shareMedia( @@ -29,8 +33,10 @@ actual class RemoteNavigator { mimeType: String, fromBackground: Boolean ) { + // TODO: Show native UI for sharing } actual fun shareText(content: String, fromBackground: Boolean) { + // TODO: Show native UI for sharing } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt index f4c3153cf..b78de5ef4 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt @@ -20,8 +20,27 @@ */ package com.twidere.twiderex.model +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import java.io.File + actual class AccountPreferencesFactory { actual fun create(accountKey: MicroBlogKey): AccountPreferences { - TODO("Not yet implemented") + val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + return AccountPreferences( + dataStore = PreferenceDataStoreFactory.create( + corruptionHandler = null, + migrations = listOf(), + scope = scope + ) { + File( + File(System.getProperty("user.home")), + "TwidereX/datastore/preferences/$accountKey" + ) + }, + scope = scope + ) } } From 3fee07264620a3f8a48e6188477d08af24cbb79e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 28 Sep 2021 12:26:22 +0800 Subject: [PATCH 256/615] revert localization submodule commit --- localization | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization b/localization index 9490971a1..3c7fba16b 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 9490971a1e0429701b15181dd3a1aa890a29a912 +Subproject commit 3c7fba16b14d5970003f8d946138450c19112e09 From 2d3625e691b0b73ac82cc0d56521dc128320c435 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 28 Sep 2021 12:35:40 +0800 Subject: [PATCH 257/615] fix get ListsMenberViewModel --- .../com/twidere/twiderex/scenes/lists/ListsMembersScene.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index 97ff5224a..d0386ef51 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -71,7 +71,7 @@ fun ListsMembersScene( ) { val navController = LocalNavController.current val viewModel: ListsUserViewModel = getViewModel { - parametersOf(listKey.id) + parametersOf(listKey.id, true) } val source = viewModel.source.collectAsLazyPagingItems() val navigator = LocalNavigator.current From eae23e9644952c38317591ddac6e91681597f506 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 28 Sep 2021 15:30:10 +0800 Subject: [PATCH 258/615] clean up --- .../twiderex/extensions/ComposeExtensions.kt | 19 ----------- .../twiderex/extensions/PagingExtensions.kt | 33 ------------------- 2 files changed, 52 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt index 26f878069..b6e1cdac7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt @@ -24,25 +24,6 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences -import moe.tlaster.precompose.viewmodel.ViewModel -import moe.tlaster.precompose.viewmodel.compose.viewModel - -@Composable -inline fun viewModel( - vararg dependsOn: Any, - noinline creator: () -> VM, -): VM { - return viewModel( - key = if (dependsOn.any()) { - dependsOn.joinToString { it.hashCode().toString() } + VM::class.java.canonicalName - } else { - null - }, - creator = { - creator.invoke() - } - ) -} @Composable fun isDarkTheme(): Boolean { diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt deleted file mode 100644 index 6cdc16119..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/PagingExtensions.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.extensions - -import androidx.paging.Pager -import androidx.paging.map -import com.twidere.twiderex.model.paging.PagingTimeLineWithStatus -import kotlinx.coroutines.flow.map - -fun Pager.toUi() = - this.flow.map { pagingData -> - pagingData.map { - it.status - } - } From 7e336f190fce7c6963459c353946fa533ce8a8d0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 28 Sep 2021 18:57:10 +0800 Subject: [PATCH 259/615] [WIP] migrate ui to common --- buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 2 + .../paging/compose/LazyPagingItems.kt | 310 ++++++++++++++++++ .../twidere/twiderex/component/LoginLogo.kt | 2 +- .../twiderex/component/TimelineComponent.kt | 0 .../twiderex/component/UserComponent.kt | 49 ++- .../twiderex/component/UserListComponent.kt | 0 .../foundation/AppBarNavigationButton.kt | 5 +- .../component/foundation/BlurImage.kt | 4 +- .../component/foundation/ErrorPlaceholder.kt | 5 +- .../foundation/InAppNotificationScaffold.kt | 0 .../component/foundation/ParallaxLayout.kt | 0 .../component/foundation/ReorderableColumn.kt | 0 .../component/foundation/SignInScaffold.kt | 7 +- .../component/foundation/TabsComponent.kt | 0 .../component/foundation/VideoPlayer.kt | 5 +- .../component/foundation/WebComponent.kt | 0 .../twiderex/component/lazy/LazyGrid.kt | 0 .../component/lazy/itemsGridIndexed.kt | 0 .../twiderex/component/lazy/itemsPaging.kt | 2 +- .../lazy/ui/LazyUiDMConversationList.kt | 3 +- .../component/lazy/ui/LazyUiDMEventList.kt | 9 +- .../twiderex/component/lazy/ui/LazyUiList.kt | 0 .../component/lazy/ui/LazyUiListsList.kt | 22 +- .../lazy/ui/LazyUiStatusImageList.kt | 0 .../component/lazy/ui/LazyUiStatusList.kt | 13 +- .../component/lazy/ui/LazyUiUserList.kt | 5 +- .../lists/MastodonListsModifyComponent.kt | 9 +- .../lists/TwitterListsModifyComponent.kt | 9 +- .../component/navigation/Navigator.kt | 0 .../placeholder/UiImagePlaceholder.kt | 0 .../placeholder/UiStatusPlaceholder.kt | 0 .../placeholder/UiUserPlaceholder.kt | 0 .../component/requireAuthorization.kt | 0 .../status/DetailedStatusComponent.kt | 23 +- .../twiderex/component/status/HtmlText.kt | 0 .../twiderex/component/status/LinkPreview.kt | 7 +- .../twiderex/component/status/MastodonPoll.kt | 15 +- .../twiderex/component/status/RoundAvatar.kt | 0 .../component/status/StatusActions.kt | 31 +- .../component/status/StatusDivider.kt | 0 .../component/status/StatusLineComponent.kt | 0 .../component/status/StatusMediaComponent.kt | 7 +- .../twiderex/component/status/StatusText.kt | 5 +- .../twiderex/component/status/StatusThread.kt | 7 +- .../status/TimelineStatusComponent.kt | 37 +-- .../twiderex/component/status/TweetHeader.kt | 11 +- .../twiderex/component/status/UserAvatar.kt | 7 +- .../component/status/UserScreenName.kt | 0 .../component/trend/MastodonTrendItem.kt | 5 +- .../component/trend/TwitterTrendItem.kt | 0 .../twiderex/extensions/ColorExtensions.kt | 0 .../extensions/LazyPagingItemsExtensions.kt | 0 .../com/twidere/twiderex/model/ui/UiMedia.kt | 6 +- .../com/twidere/twiderex/model/ui/UiStatus.kt | 2 +- .../com/twidere/twiderex/model/ui/UiUser.kt | 2 +- .../com/twidere/twiderex/navigation/Route.kt | 0 .../com/twidere/twiderex/navigation/Router.kt | 0 .../twidere/twiderex/scenes/DraftListScene.kt | 10 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 19 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 13 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 9 +- .../twidere/twiderex/scenes/SignInScene.kt | 29 +- .../twidere/twiderex/scenes/StatusScene.kt | 4 +- .../twiderex/scenes/compose/ComposeScene.kt | 97 +++--- .../compose/ComposeSearchHashtagScene.kt | 6 +- .../scenes/compose/ComposeSearchUserScene.kt | 6 +- .../scenes/dm/DMConversationListScene.kt | 11 +- .../twiderex/scenes/dm/DMConversationScene.kt | 25 +- .../scenes/dm/DMNewConversationScene.kt | 13 +- .../scenes/home/AllNotificationItem.kt | 11 +- .../scenes/home/DMConversationListItem.kt | 9 +- .../scenes/home/DraftNavigationItem.kt | 9 +- .../twiderex/scenes/home/HomeMenu.android.kt | 0 .../twiderex/scenes/home/HomeTimelineItem.kt | 15 +- .../scenes/home/ListsNavigationItem.kt | 9 +- .../twidere/twiderex/scenes/home/MeItem.kt | 11 +- .../twiderex/scenes/home/MentionItem.kt | 11 +- .../twiderex/scenes/home/NotificationItem.kt | 11 +- .../twiderex/scenes/home/SearchItem.kt | 29 +- .../home/mastodon/FederatedTimelineItem.kt | 9 +- .../scenes/home/mastodon/LocalTimelineItem.kt | 9 +- .../home/mastodon/MastodonNotificationItem.kt | 11 +- .../scenes/lists/ListsAddMembersScene.kt | 19 +- .../scenes/lists/ListsMembersScene.kt | 17 +- .../twiderex/scenes/lists/ListsScene.kt | 13 +- .../scenes/lists/ListsSubscribersScene.kt | 4 +- .../scenes/lists/ListsTimelineScene.kt | 33 +- .../platform/MastodonListsCreateDialog.kt | 5 +- .../lists/platform/MastodonListsEditDialog.kt | 4 +- .../lists/platform/TwitterListsCreateScene.kt | 7 +- .../lists/platform/TwitterListsEditScene.kt | 6 +- .../scenes/mastodon/MastodonHashtagScene.kt | 0 .../scenes/mastodon/MastodonSignInScene.kt | 13 +- .../scenes/mastodon/MastodonWebSignInScene.kt | 0 .../scenes/search/SearchInputScene.kt | 17 +- .../twiderex/scenes/search/SearchScene.kt | 9 +- .../search/tabs/MastodonSearchHashtagItem.kt | 4 +- .../scenes/search/tabs/SearchSceneItem.kt | 0 .../scenes/search/tabs/SearchTweetsItem.kt | 4 +- .../scenes/search/tabs/SearchUserItem.kt | 4 +- .../search/tabs/TwitterSearchMediaItem.kt | 4 +- .../twiderex/scenes/settings/AboutScene.kt | 35 +- .../scenes/settings/AccountManagementScene.kt | 11 +- .../settings/AccountNotificationScene.kt | 11 +- .../scenes/settings/AppearanceScene.kt | 34 +- .../twiderex/scenes/settings/DisplayScene.kt | 34 +- .../twiderex/scenes/settings/LayoutScene.kt | 21 +- .../twiderex/scenes/settings/MiscScene.kt | 59 ++-- .../scenes/settings/NotificationScene.kt | 9 +- .../twiderex/scenes/settings/SettingsScene.kt | 39 ++- .../twiderex/scenes/settings/StorageScene.kt | 14 +- .../scenes/twitter/TwitterSigninScene.kt | 0 .../scenes/twitter/TwitterWebSignInScene.kt | 0 .../scenes/twitter/user/TwitterUserScene.kt | 0 .../twiderex/scenes/user/FollowersScene.kt | 4 +- .../twiderex/scenes/user/FollowingScene.kt | 4 +- .../twidere/twiderex/scenes/user/UserScene.kt | 9 +- .../kotlin/com/twidere/twiderex/ui/Ambient.kt | 0 .../kotlin/com/twidere/twiderex/ui/Color.kt | 0 .../kotlin/com/twidere/twiderex/ui/Shape.kt | 0 .../kotlin/com/twidere/twiderex/ui/Theme.kt | 0 .../resources/MR/files/svg/Blocked_Badge.svg | 4 + .../resources/MR/files/svg/Drafts-1.svg | 4 + .../resources/MR/files/svg/Drafts-2.svg | 4 + .../resources/MR/files/svg/Drafts-3.svg | 4 + .../resources/MR/files/svg/Drafts-4.svg | 4 + .../resources/MR/files/svg/Drafts-5.svg | 4 + .../resources/MR/files/svg/Drafts-6.svg | 4 + .../resources/MR/files/svg/Drafts-7.svg | 4 + .../resources/MR/files/svg/Drafts-8.svg | 4 + .../resources/MR/files/svg/Drafts-9.svg | 4 + .../resources/MR/files/svg/Drafts-9more.svg | 6 + .../resources/MR/files/svg/Mastodon_Badge.svg | 4 + .../resources/MR/files/svg/Twitter_Badge.svg | 4 + .../commonMain/resources/MR/files/svg/add.svg | 5 + .../MR/files/svg/adjustments-horizontal.svg | 3 + .../resources/MR/files/svg/alarm.svg | 6 + .../MR/files/svg/alert-circle-warn.svg | 5 + .../resources/MR/files/svg/alert-circle.svg | 5 + .../resources/MR/files/svg/alert-octagon.svg | 5 + .../resources/MR/files/svg/alert-triangle.svg | 4 + .../resources/MR/files/svg/align-center.svg | 5 + .../MR/files/svg/align-justified.svg | 5 + .../resources/MR/files/svg/align-left.svg | 5 + .../resources/MR/files/svg/align-right.svg | 5 + .../resources/MR/files/svg/aperture.svg | 8 + .../resources/MR/files/svg/arrow-back-up.svg | 3 + .../MR/files/svg/arrow-down-left.svg | 4 + .../MR/files/svg/arrow-down-right.svg | 4 + .../resources/MR/files/svg/arrow-down.svg | 5 + .../resources/MR/files/svg/arrow-left.svg | 3 + .../resources/MR/files/svg/arrow-right.svg | 3 + .../resources/MR/files/svg/arrow-up-left.svg | 4 + .../resources/MR/files/svg/arrow-up-right.svg | 4 + .../resources/MR/files/svg/arrow-up.svg | 3 + .../resources/MR/files/svg/at-sign.svg | 3 + .../commonMain/resources/MR/files/svg/ban.svg | 3 + .../resources/MR/files/svg/bell-off.svg | 5 + .../MR/files/svg/bell-ringing-2_1.svg | 4 + .../resources/MR/files/svg/bell-ringing.svg | 3 + .../resources/MR/files/svg/bell.svg | 3 + .../MR/files/svg/blockquote-16px.svg | 8 + .../resources/MR/files/svg/blockquote.svg | 8 + .../resources/MR/files/svg/bookmark-off.svg | 4 + .../resources/MR/files/svg/bookmark.svg | 3 + .../resources/MR/files/svg/bookmarks.svg | 4 + .../resources/MR/files/svg/browser.svg | 3 + .../resources/MR/files/svg/camera.svg | 4 + .../resources/MR/files/svg/check.svg | 3 + .../resources/MR/files/svg/checkbox.svg | 4 + .../resources/MR/files/svg/checks.svg | 4 + .../resources/MR/files/svg/circle-check.svg | 4 + .../resources/MR/files/svg/circle-x_1.svg | 4 + .../resources/MR/files/svg/circle.svg | 3 + .../resources/MR/files/svg/clock.svg | 4 + .../MR/files/svg/corner-down-left.svg | 3 + .../MR/files/svg/corner-down-right.svg | 3 + .../MR/files/svg/corner-left-down.svg | 3 + .../resources/MR/files/svg/corner-left-up.svg | 3 + .../MR/files/svg/corner-right-down.svg | 3 + .../MR/files/svg/corner-right-up.svg | 3 + .../MR/files/svg/corner-up-left-16px.svg | 3 + .../resources/MR/files/svg/corner-up-left.svg | 3 + .../MR/files/svg/corner-up-right.svg | 3 + .../resources/MR/files/svg/database.svg | 5 + .../resources/MR/files/svg/delete.svg | 4 + .../resources/MR/files/svg/device-floppy.svg | 5 + .../files/svg/dots-circle-horizontal-16px.svg | 6 + .../svg/dots-circle-horizontal-fill-16px.svg | 3 + .../MR/files/svg/dots-circle-horizontal.svg | 6 + .../resources/MR/files/svg/dots-vertical.svg | 5 + .../resources/MR/files/svg/dots.svg | 5 + .../resources/MR/files/svg/download.svg | 5 + .../resources/MR/files/svg/external-link.svg | 5 + .../resources/MR/files/svg/eye-off-1.svg | 3 + .../resources/MR/files/svg/eye-off.svg | 4 + .../commonMain/resources/MR/files/svg/eye.svg | 4 + .../resources/MR/files/svg/filter.svg | 3 + .../resources/MR/files/svg/flame.svg | 3 + .../resources/MR/files/svg/float-center.svg | 9 + .../resources/MR/files/svg/float-left.svg | 7 + .../resources/MR/files/svg/float-none.svg | 5 + .../resources/MR/files/svg/float-right.svg | 7 + .../commonMain/resources/MR/files/svg/gif.svg | 5 + .../resources/MR/files/svg/globe-16px.svg | 3 + .../resources/MR/files/svg/globe.svg | 3 + .../resources/MR/files/svg/hash.svg | 3 + .../resources/MR/files/svg/heart-16px.svg | 3 + .../MR/files/svg/heart-fill-16px.svg | 3 + .../resources/MR/files/svg/heart-fill.svg | 3 + .../resources/MR/files/svg/heart.svg | 3 + .../resources/MR/files/svg/history.svg | 4 + .../resources/MR/files/svg/home.svg | 5 + .../resources/MR/files/svg/info-circle.svg | 5 + .../resources/MR/files/svg/keyboard.svg | 10 + .../MR/files/svg/layers-intersect.svg | 4 + .../resources/MR/files/svg/layout-2.svg | 6 + .../MR/files/svg/layout-bottombar.svg | 3 + .../resources/MR/files/svg/layout-list.svg | 4 + .../resources/MR/files/svg/layout-sidebar.svg | 4 + .../resources/MR/files/svg/layout.svg | 5 + .../resources/MR/files/svg/link.svg | 4 + .../resources/MR/files/svg/list.svg | 8 + .../resources/MR/files/svg/lock-16px.svg | 4 + .../resources/MR/files/svg/lock-open-16px.svg | 5 + .../resources/MR/files/svg/lock-open.svg | 5 + .../resources/MR/files/svg/lock.svg | 3 + .../MR/files/svg/lock_and_repeat-16px.svg | 5 + .../resources/MR/files/svg/login.svg | 4 + .../resources/MR/files/svg/logout.svg | 4 + .../resources/MR/files/svg/mail-16px.svg | 3 + .../resources/MR/files/svg/mail-plus.svg | 5 + .../resources/MR/files/svg/mail.svg | 3 + .../resources/MR/files/svg/map-pin-16px.svg | 4 + .../resources/MR/files/svg/map-pin.svg | 4 + .../resources/MR/files/svg/message-2-16px.svg | 5 + .../MR/files/svg/message-circle-16px.svg | 6 + .../MR/files/svg/message-circle-add.svg | 7 + .../resources/MR/files/svg/message-circle.svg | 6 + .../resources/MR/files/svg/microphone.svg | 4 + .../resources/MR/files/svg/mood-neutral.svg | 6 + .../resources/MR/files/svg/mood-sad.svg | 6 + .../resources/MR/files/svg/mood-smile.svg | 6 + .../resources/MR/files/svg/note.svg | 3 + .../resources/MR/files/svg/notification.svg | 4 + .../resources/MR/files/svg/open-source.svg | 3 + .../resources/MR/files/svg/paperclip.svg | 3 + .../resources/MR/files/svg/pencil.svg | 4 + .../resources/MR/files/svg/photo.svg | 3 + .../resources/MR/files/svg/photos.svg | 3 + .../resources/MR/files/svg/pin-1.svg | 5 + .../commonMain/resources/MR/files/svg/pin.svg | 5 + .../resources/MR/files/svg/pinned-16px.svg | 6 + .../resources/MR/files/svg/pinned-off.svg | 6 + .../resources/MR/files/svg/pinned.svg | 5 + .../resources/MR/files/svg/planet.svg | 3 + .../resources/MR/files/svg/player-pause.svg | 4 + .../resources/MR/files/svg/player-play.svg | 3 + .../resources/MR/files/svg/plus.svg | 4 + .../resources/MR/files/svg/poll.svg | 5 + .../resources/MR/files/svg/qrcode.svg | 14 + .../resources/MR/files/svg/refresh-alert.svg | 6 + .../resources/MR/files/svg/refresh.svg | 4 + .../resources/MR/files/svg/repeat-12px.svg | 3 + .../resources/MR/files/svg/repeat-16px.svg | 4 + .../resources/MR/files/svg/repeat.svg | 4 + .../resources/MR/files/svg/search.svg | 3 + .../resources/MR/files/svg/select-1.svg | 4 + .../resources/MR/files/svg/select.svg | 4 + .../resources/MR/files/svg/selector.svg | 4 + .../resources/MR/files/svg/send-add.svg | 4 + .../resources/MR/files/svg/send.svg | 3 + .../resources/MR/files/svg/settings.svg | 4 + .../resources/MR/files/svg/share-16px.svg | 3 + .../resources/MR/files/svg/share-iOS-16px.svg | 5 + .../resources/MR/files/svg/share-iOS.svg | 3 + .../resources/MR/files/svg/share.svg | 3 + .../resources/MR/files/svg/shield-off.svg | 5 + .../resources/MR/files/svg/shield.svg | 3 + .../resources/MR/files/svg/shirt.svg | 3 + .../resources/MR/files/svg/square-check.svg | 4 + .../resources/MR/files/svg/square.svg | 3 + .../resources/MR/files/svg/template.svg | 7 + .../resources/MR/files/svg/trash.svg | 3 + .../resources/MR/files/svg/trending-up.svg | 4 + .../MR/files/svg/triangle-square-circle.svg | 5 + .../resources/MR/files/svg/upload.svg | 5 + .../resources/MR/files/svg/user-check_1.svg | 5 + .../MR/files/svg/user-exclamation_1.svg | 6 + .../resources/MR/files/svg/user-plus.svg | 5 + .../resources/MR/files/svg/user-x.svg | 5 + .../resources/MR/files/svg/user.svg | 4 + .../resources/MR/files/svg/users.svg | 6 + .../resources/MR/files/svg/video-off.svg | 5 + .../resources/MR/files/svg/video.svg | 4 + .../resources/MR/files/svg/volume-2.svg | 4 + .../resources/MR/files/svg/volume-3.svg | 4 + .../resources/MR/files/svg/volume.svg | 5 + .../commonMain/resources/MR/files/svg/x.svg | 3 + 300 files changed, 1629 insertions(+), 606 deletions(-) create mode 100644 common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/TimelineComponent.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/UserComponent.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/UserListComponent.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt (88%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt (88%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/requireAuthorization.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt (88%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/HtmlText.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusActions.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusText.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/StatusThread.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt (87%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/navigation/Route.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/navigation/Router.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/HomeScene.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/MediaScene.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt (97%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/SignInScene.kt (83%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/StatusScene.kt (98%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt (86%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt (84%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt (86%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt (84%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt (86%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt (86%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt (91%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt (90%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt (96%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt (95%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt (85%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt (82%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt (79%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt (89%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt (81%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt (93%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt (70%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt (81%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt (92%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt (94%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/ui/Ambient.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/ui/Color.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/ui/Shape.kt (100%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/ui/Theme.kt (100%) create mode 100644 common/src/commonMain/resources/MR/files/svg/Blocked_Badge.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-2.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-3.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-4.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-5.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-6.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-7.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-8.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-9.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Drafts-9more.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Mastodon_Badge.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/Twitter_Badge.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/add.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/adjustments-horizontal.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/alarm.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/alert-circle-warn.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/alert-circle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/alert-octagon.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/alert-triangle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/align-center.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/align-justified.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/align-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/align-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/aperture.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-back-up.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-down-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-down-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-down.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-up-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-up-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/arrow-up.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/at-sign.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/ban.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bell-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bell-ringing-2_1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bell-ringing.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bell.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/blockquote-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/blockquote.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bookmark-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bookmark.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/bookmarks.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/browser.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/camera.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/check.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/checkbox.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/checks.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/circle-check.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/circle-x_1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/circle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/clock.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-down-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-down-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-left-down.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-left-up.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-right-down.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-right-up.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-up-left-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-up-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/corner-up-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/database.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/delete.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/device-floppy.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-fill-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/dots-vertical.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/dots.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/download.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/external-link.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/eye-off-1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/eye-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/eye.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/filter.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/flame.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/float-center.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/float-left.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/float-none.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/float-right.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/gif.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/globe-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/globe.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/hash.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/heart-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/heart-fill-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/heart-fill.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/heart.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/history.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/home.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/info-circle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/keyboard.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layers-intersect.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layout-2.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layout-bottombar.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layout-list.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layout-sidebar.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/layout.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/link.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/list.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/lock-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/lock-open-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/lock-open.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/lock.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/lock_and_repeat-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/login.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/logout.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mail-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mail-plus.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mail.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/map-pin-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/map-pin.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/message-2-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/message-circle-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/message-circle-add.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/message-circle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/microphone.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mood-neutral.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mood-sad.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/mood-smile.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/note.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/notification.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/open-source.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/paperclip.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pencil.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/photo.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/photos.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pin-1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pin.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pinned-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pinned-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/pinned.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/planet.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/player-pause.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/player-play.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/plus.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/poll.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/qrcode.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/refresh-alert.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/refresh.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/repeat-12px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/repeat-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/repeat.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/search.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/select-1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/select.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/selector.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/send-add.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/send.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/settings.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/share-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/share-iOS-16px.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/share-iOS.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/share.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/shield-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/shield.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/shirt.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/square-check.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/square.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/template.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/trash.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/trending-up.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/triangle-square-circle.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/upload.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/user-check_1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/user-exclamation_1.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/user-plus.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/user-x.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/user.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/users.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/video-off.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/video.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/volume-2.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/volume-3.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/volume.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/x.svg diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b076107c9..c8eb97fb6 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -39,6 +39,7 @@ object Versions { const val startup = "1.1.0" const val coil = "1.3.0" const val accompanist = "0.15.0" + const val accompanist_jb = "0.18.1" const val androidx_exifinterface = "1.3.2" const val exoplayer = "2.14.2" const val browser = "1.3.0" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index dee1bd929..a0e975d7c 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -48,6 +48,8 @@ kotlin { ksp(projects.routeProcessor) api("dev.icerock.moko:resources:${Versions.moko}") implementation("app.cash.turbine:turbine:0.6.1") + implementation("ca.gosyer:accompanist-pager:${Versions.accompanist_jb}") + implementation("ca.gosyer:accompanist-pager-indicators:${Versions.accompanist_jb}") } } val commonTest by getting { diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt new file mode 100644 index 000000000..f6c16af50 --- /dev/null +++ b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt @@ -0,0 +1,310 @@ +package androidx.paging.compose + +import android.annotation.SuppressLint +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.paging.CombinedLoadStates +import androidx.paging.DifferCallback +import androidx.paging.ItemSnapshotList +import androidx.paging.LoadState +import androidx.paging.LoadStates +import androidx.paging.NullPaddedList +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.PagingDataDiffer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.collectLatest + +/** + * The class responsible for accessing the data from a [Flow] of [PagingData]. + * In order to obtain an instance of [LazyPagingItems] use the [collectAsLazyPagingItems] extension + * method of [Flow] with [PagingData]. + * This instance can be used by the [items] and [itemsIndexed] methods inside [LazyListScope] to + * display data received from the [Flow] of [PagingData]. + * + * @param T the type of value used by [PagingData]. + */ +public class LazyPagingItems internal constructor( + /** + * the [Flow] object which contains a stream of [PagingData] elements. + */ + private val flow: Flow> +) { + private val mainDispatcher = Dispatchers.Main + + /** + * Contains the latest items list snapshot collected from the [flow]. + */ + private var itemSnapshotList by mutableStateOf( + ItemSnapshotList(0, 0, emptyList()) + ) + + /** + * The number of items which can be accessed. + */ + val itemCount: Int get() = itemSnapshotList.size + + @SuppressLint("RestrictedApi") + private val differCallback: DifferCallback = object : DifferCallback { + override fun onChanged(position: Int, count: Int) { + if (count > 0) { + updateItemSnapshotList() + } + } + + override fun onInserted(position: Int, count: Int) { + if (count > 0) { + updateItemSnapshotList() + } + } + + override fun onRemoved(position: Int, count: Int) { + if (count > 0) { + updateItemSnapshotList() + } + } + } + + private val pagingDataDiffer = object : PagingDataDiffer( + differCallback = differCallback, + mainDispatcher = mainDispatcher + ) { + override suspend fun presentNewList( + previousList: NullPaddedList, + newList: NullPaddedList, + newCombinedLoadStates: CombinedLoadStates, + lastAccessedIndex: Int, + onListPresentable: () -> Unit + ): Int? { + onListPresentable() + updateItemSnapshotList() + return null + } + } + + private fun updateItemSnapshotList() { + itemSnapshotList = pagingDataDiffer.snapshot() + } + + /** + * Returns the presented item at the specified position, notifying Paging of the item access to + * trigger any loads necessary to fulfill prefetchDistance. + * + * @see peek + */ + operator fun get(index: Int): T? { + pagingDataDiffer[index] // this registers the value load + return itemSnapshotList[index] + } + + /** + * Returns the state containing the item specified at [index] and notifies Paging of the item + * accessed in order to trigger any loads necessary to fulfill [PagingConfig.prefetchDistance]. + * + * @param index the index of the item which should be returned. + * @return the state containing the item specified at [index] or null if the item is a + * placeholder or [index] is not within the correct bounds. + */ + @Composable + @Deprecated( + "Use get() instead. It will return you the value not wrapped into a State", + ReplaceWith("this[index]") + ) + fun getAsState(index: Int): State { + return rememberUpdatedState(get(index)) + } + + /** + * Returns the presented item at the specified position, without notifying Paging of the item + * access that would normally trigger page loads. + * + * @param index Index of the presented item to return, including placeholders. + * @return The presented item at position [index], `null` if it is a placeholder + */ + fun peek(index: Int): T? { + return itemSnapshotList[index] + } + + /** + * Returns a new [ItemSnapshotList] representing the currently presented items, including any + * placeholders if they are enabled. + */ + fun snapshot(): ItemSnapshotList { + return itemSnapshotList + } + + /** + * Retry any failed load requests that would result in a [LoadState.Error] update to this + * [LazyPagingItems]. + * + * Unlike [refresh], this does not invalidate [PagingSource], it only retries failed loads + * within the same generation of [PagingData]. + * + * [LoadState.Error] can be generated from two types of load requests: + * * [PagingSource.load] returning [PagingSource.LoadResult.Error] + * * [RemoteMediator.load] returning [RemoteMediator.MediatorResult.Error] + */ + fun retry() { + pagingDataDiffer.retry() + } + + /** + * Refresh the data presented by this [LazyPagingItems]. + * + * [refresh] triggers the creation of a new [PagingData] with a new instance of [PagingSource] + * to represent an updated snapshot of the backing dataset. If a [RemoteMediator] is set, + * calling [refresh] will also trigger a call to [RemoteMediator.load] with [LoadType] [REFRESH] + * to allow [RemoteMediator] to check for updates to the dataset backing [PagingSource]. + * + * Note: This API is intended for UI-driven refresh signals, such as swipe-to-refresh. + * Invalidation due repository-layer signals, such as DB-updates, should instead use + * [PagingSource.invalidate]. + * + * @see PagingSource.invalidate + */ + fun refresh() { + pagingDataDiffer.refresh() + } + + /** + * A [CombinedLoadStates] object which represents the current loading state. + */ + public var loadState: CombinedLoadStates by mutableStateOf( + CombinedLoadStates( + refresh = InitialLoadStates.refresh, + prepend = InitialLoadStates.prepend, + append = InitialLoadStates.append, + source = InitialLoadStates + ) + ) + private set + + internal suspend fun collectLoadState() { + pagingDataDiffer.loadStateFlow.collect { + loadState = it + } + } + + internal suspend fun collectPagingData() { + flow.collectLatest { + pagingDataDiffer.collectFrom(it) + } + } +} + +private val IncompleteLoadState = LoadState.NotLoading(false) +private val InitialLoadStates = LoadStates( + IncompleteLoadState, + IncompleteLoadState, + IncompleteLoadState +) + +/** + * Collects values from this [Flow] of [PagingData] and represents them inside a [LazyPagingItems] + * instance. The [LazyPagingItems] instance can be used by the [items] and [itemsIndexed] methods + * from [LazyListScope] in order to display the data obtained from a [Flow] of [PagingData]. + * + * @sample androidx.paging.compose.samples.PagingBackendSample + */ +@Composable +public fun Flow>.collectAsLazyPagingItems(): LazyPagingItems { + val lazyPagingItems = remember(this) { LazyPagingItems(this) } + + LaunchedEffect(lazyPagingItems) { + lazyPagingItems.collectPagingData() + } + LaunchedEffect(lazyPagingItems) { + lazyPagingItems.collectLoadState() + } + + return lazyPagingItems +} + +/** + * Adds the [LazyPagingItems] and their content to the scope. The range from 0 (inclusive) to + * [LazyPagingItems.itemCount] (exclusive) always represents the full range of presentable items, + * because every event from [PagingDataDiffer] will trigger a recomposition. + * + * @sample androidx.paging.compose.samples.ItemsDemo + * + * @param items the items received from a [Flow] of [PagingData]. + * @param key a factory of stable and unique keys representing the item. Using the same key + * for multiple items in the list is not allowed. Type of the key should be saveable + * via Bundle on Android. If null is passed the position in the list will represent the key. + * When you specify the key the scroll position will be maintained based on the key, which + * means if you add/remove items before the current visible item the item with the given key + * will be kept as the first visible one. + * @param itemContent the content displayed by a single item. In case the item is `null`, the + * [itemContent] method should handle the logic of displaying a placeholder instead of the main + * content displayed by an item which is not `null`. + */ +public fun LazyListScope.items( + items: LazyPagingItems, + key: ((item: T) -> Any)? = null, + itemContent: @Composable LazyItemScope.(value: T?) -> Unit +) { + items( + count = items.itemCount, + key = if (key == null) null else { index -> + val item = items.peek(index) + if (item == null) { + PagingPlaceholderKey(index) + } else { + key(item) + } + } + ) { index -> + itemContent(items[index]) + } +} + +/** + * Adds the [LazyPagingItems] and their content to the scope where the content of an item is + * aware of its local index. The range from 0 (inclusive) to [LazyPagingItems.itemCount] (exclusive) + * always represents the full range of presentable items, because every event from + * [PagingDataDiffer] will trigger a recomposition. + * + * @sample androidx.paging.compose.samples.ItemsIndexedDemo + * + * @param items the items received from a [Flow] of [PagingData]. + * @param key a factory of stable and unique keys representing the item. Using the same key + * for multiple items in the list is not allowed. Type of the key should be saveable + * via Bundle on Android. If null is passed the position in the list will represent the key. + * When you specify the key the scroll position will be maintained based on the key, which + * means if you add/remove items before the current visible item the item with the given key + * will be kept as the first visible one. + * @param itemContent the content displayed by a single item. In case the item is `null`, the + * [itemContent] method should handle the logic of displaying a placeholder instead of the main + * content displayed by an item which is not `null`. + */ +public fun LazyListScope.itemsIndexed( + items: LazyPagingItems, + key: ((index: Int, item: T) -> Any)? = null, + itemContent: @Composable LazyItemScope.(index: Int, value: T?) -> Unit +) { + items( + count = items.itemCount, + key = if (key == null) null else { index -> + val item = items.peek(index) + if (item == null) { + PagingPlaceholderKey(index) + } else { + key(index, item) + } + } + ) { index -> + itemContent(index, items[index]) + } +} + +private data class PagingPlaceholderKey(private val index: Int) \ No newline at end of file diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt index 9285b6f60..45d9f98ba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt @@ -33,7 +33,7 @@ fun LoginLogo( Image( modifier = modifier, contentScale = ContentScale.FillWidth, - painter = painterResource(res = MR.files.ic_login_logo), + painter = painterResource(res = com.twidere.twiderex.MR.files.ic_login_logo), contentDescription = stringResource(res = MR.strings.accessibility_common_logo_twidere) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/TimelineComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt index 5e7798609..89049b27e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -72,8 +72,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -83,7 +83,6 @@ import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.pagerTabIndicatorOffset import com.google.accompanist.pager.rememberPagerState -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.HorizontalDivider import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout @@ -128,22 +127,22 @@ fun UserComponent( val isMe by viewModel.isMe.observeAsState(initial = false) val tabs = listOf( UserTabComponent( - painterResource(id = R.drawable.ic_float_left), - stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_status) + painterResource(res = com.twidere.twiderex.MR.files.float_left), + stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_user_tab_status) ) { UserStatusTimeline(userKey = userKey, viewModel = viewModel) }, UserTabComponent( - painterResource(id = R.drawable.ic_photo), - stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_media) + painterResource(res = com.twidere.twiderex.MR.files.photo), + stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_user_tab_media) ) { UserMediaTimeline(userKey = userKey) }, ).let { if (isMe || userKey.host == MicroBlogKey.TwitterHost) { it + UserTabComponent( - painterResource(id = R.drawable.ic_heart), - stringResource(id = com.twidere.common.R.string.accessibility_scene_user_tab_favourite) + painterResource(res = com.twidere.twiderex.MR.files.heart), + stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_user_tab_favourite) ) { UserFavouriteTimeline(userKey = userKey) } @@ -267,12 +266,12 @@ private fun UserStatusTimelineFilter( modifier = Modifier.weight(1f), text = if (user.metrics.status > 1) { stringResource( - id = com.twidere.common.R.string.common_countable_tweet_single, + res = com.twidere.twiderex.MR.strings.common_countable_tweet_single, user.metrics.status ) } else { stringResource( - id = com.twidere.common.R.string.common_countable_tweet_multiple, + res = com.twidere.twiderex.MR.strings.common_countable_tweet_multiple, user.metrics.status ) } @@ -302,7 +301,7 @@ private fun UserStatusTimelineFilter( ) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_filter_all)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_profile_filter_all)) } } DropdownMenuItem( @@ -322,7 +321,7 @@ private fun UserStatusTimelineFilter( ) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_filter_exclude_replies)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_profile_filter_exclude_replies)) } } } @@ -332,7 +331,7 @@ private fun UserStatusTimelineFilter( } ) { Icon( - painter = painterResource(id = R.drawable.ic_filter), + painter = painterResource(res = com.twidere.twiderex.MR.files.filter), contentDescription = null, ) } @@ -451,9 +450,9 @@ fun UserInfo( ) .padding(UserInfoDefaults.WebsitePaddingValue) .fillMaxWidth(), - painter = painterResource(id = R.drawable.ic_globe), + painter = painterResource(res = com.twidere.twiderex.MR.files.globe), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_user_website + res = com.twidere.twiderex.MR.strings.accessibility_scene_user_website ), text = it, textColor = MaterialTheme.colors.primary, @@ -462,9 +461,9 @@ fun UserInfo( } user?.location?.takeIf { it.isNotEmpty() }?.let { ProfileItem( - painter = painterResource(id = R.drawable.ic_map_pin), + painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_user_location + res = com.twidere.twiderex.MR.strings.accessibility_scene_user_location ), text = it ) @@ -508,7 +507,7 @@ private fun UserInfoName(user: UiUser) { LocalContentAlpha provides ContentAlpha.medium, ) { Icon( - painter = painterResource(id = R.drawable.ic_lock), + painter = painterResource(res = com.twidere.twiderex.MR.files.lock), contentDescription = null ) } @@ -649,16 +648,16 @@ private fun UserRelationship(viewModel: UserViewModel) { modifier = Modifier .padding(ButtonDefaults.ContentPadding), text = if (relationshipResult.followedBy) { - stringResource(id = com.twidere.common.R.string.common_controls_friendship_actions_unfollow) + stringResource(res = com.twidere.twiderex.MR.strings.common_controls_friendship_actions_unfollow) } else { - stringResource(id = com.twidere.common.R.string.common_controls_friendship_actions_follow) + stringResource(res = com.twidere.twiderex.MR.strings.common_controls_friendship_actions_follow) }, ) } Spacer(modifier = Modifier.height(UserRelationshipDefaults.FollowingSpacing)) if (relationshipResult.following) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_friendship_follows_you), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_friendship_follows_you), style = MaterialTheme.typography.caption, ) } @@ -712,7 +711,7 @@ fun UserMetrics( navController.navigate(RootRoute.Following(user.userKey)) }, primaryText = user.metrics.follow.toString(), - secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_following), + secondaryText = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_following), ) HorizontalDivider( modifier = Modifier.height(LocalTextStyle.current.fontSize.value.dp * 2) @@ -724,7 +723,7 @@ fun UserMetrics( navController.navigate(RootRoute.Followers(user.userKey)) }, primaryText = user.metrics.fans.toString(), - secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_followers), + secondaryText = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_followers), ) if (user.platformType == PlatformType.Twitter) { HorizontalDivider( @@ -734,7 +733,7 @@ fun UserMetrics( modifier = Modifier .weight(1f), primaryText = user.metrics.listed.toString(), - secondaryText = stringResource(id = com.twidere.common.R.string.common_controls_profile_dashboard_listed), + secondaryText = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_listed), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/UserListComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt similarity index 88% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt index 600361b67..70bdeb985 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt @@ -26,8 +26,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.ui.LocalNavController @Composable @@ -42,7 +41,7 @@ fun AppBarNavigationButton( ) { Icon( imageVector = icon, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_back) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_back) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt index 654883da6..020f77977 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.component.painterResource import androidx.core.graphics.drawable.toBitmap import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat @@ -67,7 +67,7 @@ fun BlurImage( } } Image( - painter = painter ?: painterResource(id = resource), + painter = painter ?: painterResource(res = resource), contentDescription = contentDescription, modifier = modifier, alignment = alignment, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt index e40e4f725..a1a762f35 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt @@ -31,8 +31,7 @@ import androidx.compose.material.icons.filled.ErrorOutline import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.notification.NotificationEvent @OptIn(ExperimentalMaterialApi::class) @@ -60,7 +59,7 @@ fun ErrorPlaceholder( ) { Text( text = message - ?: stringResource(id = com.twidere.common.R.string.common_alerts_failed_to_load_title), + ?: stringResource(res = com.twidere.twiderex.MR.strings.common_alerts_failed_to_load_title), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ReorderableColumn.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt index 91050fdb9..7263f008b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt @@ -43,10 +43,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.LoginLogo import com.twidere.twiderex.ui.TwidereScene @@ -93,7 +92,7 @@ fun SignInScaffold( ) Spacer(modifier = Modifier.width(SignInScaffoldDefaults.IconSpacing)) Text( - text = stringResource(id = R.string.app_name), + text = stringResource(res = com.twidere.twiderex.MR.strings.app_name), style = MaterialTheme.typography.h4, ) } @@ -101,7 +100,7 @@ fun SignInScaffold( modifier = Modifier .weight(1F) .align(Alignment.Start), - text = stringResource(id = com.twidere.common.R.string.scene_sign_in_hello_sign_in_to_get_started), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_hello_sign_in_to_get_started), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h3, color = MaterialTheme.colors.primary, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index a75881778..509f2db75 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -43,7 +43,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver @@ -57,7 +57,6 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory -import com.twidere.twiderex.R import com.twidere.twiderex.component.status.UserAvatarDefaults import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig @@ -207,7 +206,7 @@ fun VideoPlayer( .align(Alignment.Center) .size(UserAvatarDefaults.AvatarSize) .background(MaterialTheme.colors.primary, CircleShape), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_video_play) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_video_play) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt index 44773097d..31d88f6c2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt @@ -39,7 +39,7 @@ fun LazyListScope.loadState( ListItem( modifier = Modifier.clickable(onClick = { onReloadRequested.invoke() }), text = { -// Text(text = stringResource(id = com.twidere.common.R.string.list_load_state_error)) +// Text(text = stringResource(res = com.twidere.twiderex.MR.strings.list_load_state_error)) } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt index abe6570ce..755cb13c2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt @@ -42,7 +42,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.placeholder.UiUserPlaceholder import com.twidere.twiderex.component.status.HtmlText @@ -161,7 +160,7 @@ private fun UiDMEvent.resolveLink( entity != null -> { if (entity.displayUrl.contains("pic.twitter.com")) { ResolvedLink( - expanded = context.getString(com.twidere.common.R.string.scene_messages_expanded_photo), + expanded = context.getString(com.twidere.twiderex.MR.strings.scene_messages_expanded_photo), clickable = false ) } else { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index dca25bda2..b626bdf08 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -62,13 +62,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.placeholder.UiUserPlaceholder import com.twidere.twiderex.component.status.HtmlText @@ -154,8 +153,8 @@ private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiD .padding(DMOutComeEventDefaults.Error.ContentPadding) ) { Icon( - painter = painterResource(id = R.drawable.ic_alert), - contentDescription = stringResource(id = com.twidere.common.R.string.scene_messages_icon_failed), + painter = painterResource(res = com.twidere.twiderex.MR.files.alert), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_icon_failed), tint = Color.Unspecified, modifier = Modifier.size(DMOutComeEventDefaults.Error.size) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt index 93ad0aaca..ae0c36c57 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt @@ -44,14 +44,13 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.status.StatusDivider import com.twidere.twiderex.model.enums.ListType @@ -88,7 +87,7 @@ fun LazyUiListsList( if (listType == ListType.All) { item { LazyUiListTitleItem( - title = stringResource(id = com.twidere.common.R.string.scene_lists_tabs_created).uppercase( + title = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_tabs_created).uppercase( Locale.getDefault() ) ) @@ -115,7 +114,7 @@ fun LazyUiListsList( if (listType == ListType.All) { item { LazyUiListTitleItem( - title = stringResource(id = com.twidere.common.R.string.scene_lists_tabs_subscribed).uppercase( + title = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_tabs_subscribed).uppercase( Locale.getDefault() ), divider = true @@ -185,8 +184,8 @@ private fun LazyUiListItem(uiList: UiList, onItemClicked: (UiList) -> Unit = {}) trailing = { if (uiList.isPrivate) { Icon( - painter = painterResource(id = R.drawable.ic_lock), - contentDescription = stringResource(id = com.twidere.common.R.string.scene_lists_icons_private), + painter = painterResource(res = com.twidere.twiderex.MR.files.lock), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_icons_private), modifier = Modifier .alpha(ContentAlpha.disabled) .size(LazyUiListsItemDefaults.LockIconSize) @@ -239,11 +238,6 @@ private fun DividerListItem( } } -@Preview -@Composable -private fun PreviewLazyListItem() { - LazyUiListItem(uiList = UiList.sample()) -} @Composable private fun EmptyList() { @@ -253,8 +247,8 @@ private fun EmptyList() { verticalArrangement = Arrangement.Center ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_list), - contentDescription = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title) + painter = painterResource(res = com.twidere.twiderex.MR.files.empty_list), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.common_alerts_no_tweets_found_title) ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt index bd86cac44..21af62a18 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt @@ -64,12 +64,11 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.itemsIndexed -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.placeholder.UiStatusPlaceholder @@ -253,7 +252,7 @@ private fun LoadMoreButton( Text( modifier = Modifier.padding(12.dp), text = stringResource( - id = com.twidere.common.R.string.common_controls_timeline_load_more + res = com.twidere.twiderex.MR.strings.common_controls_timeline_load_more ), color = MaterialTheme.colors.primary, ) @@ -268,13 +267,13 @@ private fun EmptyStatusList() { verticalArrangement = Arrangement.Center ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_status), - contentDescription = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title) + painter = painterResource(res = com.twidere.twiderex.MR.files.empty_status), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.common_alerts_no_tweets_found_title) ) Spacer(modifier = Modifier.height(EmptyStatusListDefaults.VerticalPadding)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = com.twidere.common.R.string.common_alerts_no_tweets_found_title), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_alerts_no_tweets_found_title), style = MaterialTheme.typography.h6 ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt index 2e92a9f95..2f23156e7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt @@ -38,11 +38,10 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.placeholder.UiUserPlaceholder import com.twidere.twiderex.component.status.UserAvatar @@ -96,7 +95,7 @@ fun LazyUiUserList( Row { Text( text = stringResource( - id = com.twidere.common.R.string.common_controls_profile_dashboard_followers, + res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_followers, ) ) Spacer(modifier = Modifier.width(UiUserListDefaults.HorizontalPadding)) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt similarity index 88% rename from android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt index 7451a7331..9e097ea16 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt @@ -34,9 +34,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R @Composable fun MastodonListsModifyComponent( @@ -66,7 +65,7 @@ fun MastodonListsModifyComponent( onValueChange = onNameChanged, placeholder = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_name), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_name), style = MaterialTheme.typography.subtitle1, ) }, @@ -79,7 +78,7 @@ fun MastodonListsModifyComponent( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_cancel)) } }, confirmButton = { @@ -88,7 +87,7 @@ fun MastodonListsModifyComponent( onConfirm(name) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } }, shape = RoundedCornerShape(0.dp) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt index ad8f3aa9b..6b0f00806 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt @@ -49,13 +49,12 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.ColoredSwitch @OptIn(ExperimentalComposeUiApi::class) @@ -84,7 +83,7 @@ fun TwitterListsModifyComponent( } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_name), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_name), style = MaterialTheme.typography.caption, modifier = Modifier.padding(TwitterListsModifyComponentDefaults.TextFieldTitle.ContentPadding) ) @@ -119,7 +118,7 @@ fun TwitterListsModifyComponent( Spacer(modifier = Modifier.height(TwitterListsModifyComponentDefaults.VerticalPadding)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_description), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_description), style = MaterialTheme.typography.caption, modifier = Modifier.padding(TwitterListsModifyComponentDefaults.TextFieldTitle.ContentPadding) ) @@ -152,7 +151,7 @@ fun TwitterListsModifyComponent( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_private), style = MaterialTheme.typography.body1) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_private), style = MaterialTheme.typography.body1) ColoredSwitch( checked = isPrivate, onCheckedChange = onPrivateChanged, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/requireAuthorization.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt similarity index 88% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt index 9c4bdbca6..e2ecf66ac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt @@ -43,10 +43,9 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.FormattedTime import com.twidere.twiderex.extensions.humanizedCount import com.twidere.twiderex.model.enums.PlatformType @@ -91,9 +90,9 @@ fun DetailedStatusComponent( ) { Icon( modifier = Modifier.size(MaterialTheme.typography.body1.fontSize.value.dp), - painter = painterResource(id = R.drawable.ic_map_pin), + painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_status_location + res = com.twidere.twiderex.MR.strings.accessibility_common_status_location ) ) Text(text = status.geo.name) @@ -124,18 +123,18 @@ fun DetailedStatusComponent( ) { StatusStatistics( count = status.metrics.reply, - icon = painterResource(id = R.drawable.ic_corner_up_left), + icon = painterResource(res = com.twidere.twiderex.MR.files.corner_up_left), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_status_reply_mutiple, + res = com.twidere.twiderex.MR.strings.scene_status_reply_mutiple, status.metrics.reply, ), ) Spacer(modifier = Modifier.width(DetailedStatusDefaults.StatusStatisticsSpacing)) StatusStatistics( count = status.metrics.retweet, - icon = painterResource(id = R.drawable.ic_repeat), + icon = painterResource(res = com.twidere.twiderex.MR.files.repeat), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_status_retweet_mutiple, + res = com.twidere.twiderex.MR.strings.scene_status_retweet_mutiple, status.metrics.retweet, ), ) @@ -143,16 +142,16 @@ fun DetailedStatusComponent( Spacer(modifier = Modifier.width(DetailedStatusDefaults.StatusStatisticsSpacing)) StatusStatistics( count = status.twitterExtra?.quoteCount ?: 0, - icon = painterResource(id = R.drawable.ic_blockquote), + icon = painterResource(res = com.twidere.twiderex.MR.files.blockquote), contentDescription = null, ) } Spacer(modifier = Modifier.width(DetailedStatusDefaults.StatusStatisticsSpacing)) StatusStatistics( count = status.metrics.like, - icon = painterResource(id = R.drawable.ic_heart), + icon = painterResource(res = com.twidere.twiderex.MR.files.heart), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_status_like_multiple, + res = com.twidere.twiderex.MR.strings.scene_status_like_multiple, status.metrics.like, ), ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/HtmlText.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt index 3211f26e4..2daef1503 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt @@ -39,11 +39,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.Layout -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.NetworkImage +import com.twidere.twiderex.component.painterResource @Composable fun LinkPreview( @@ -131,7 +130,7 @@ private fun LinkOnlyPreview( } Row { Icon( - painter = painterResource(id = R.drawable.ic_planet), + painter = painterResource(res = com.twidere.twiderex.MR.files.planet), contentDescription = null ) Spacer(modifier = Modifier.width(LinkPreviewDefaults.TextPaddingStart)) @@ -155,7 +154,7 @@ private fun LinkWithTitlePreview( CompositionLocalProvider( LocalContentColor provides MaterialTheme.colors.primary ) { - Icon(painter = painterResource(id = R.drawable.ic_planet), contentDescription = null) + Icon(painter = painterResource(res = com.twidere.twiderex.MR.files.planet), contentDescription = null) } Spacer(modifier = Modifier.width(LinkPreviewDefaults.TextPaddingStart)) Column { diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt index e8f456e8b..44cbea33c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt @@ -64,9 +64,8 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.extensions.humanizedTimestamp import com.twidere.twiderex.model.enums.PlatformType @@ -129,24 +128,24 @@ fun MastodonPoll(status: UiStatus) { val countText = status.poll?.votersCount?.let { if (it > 1) { stringResource( - id = com.twidere.common.R.string.common_controls_status_poll_total_people, + res = com.twidere.twiderex.MR.strings.common_controls_status_poll_total_people, it, ) } else { stringResource( - id = com.twidere.common.R.string.common_controls_status_poll_total_person, + res = com.twidere.twiderex.MR.strings.common_controls_status_poll_total_person, it, ) } } ?: status.poll?.votesCount?.let { if (it > 1) { stringResource( - id = com.twidere.common.R.string.common_controls_status_poll_total_votes, + res = com.twidere.twiderex.MR.strings.common_controls_status_poll_total_votes, it, ) } else { stringResource( - id = com.twidere.common.R.string.common_controls_status_poll_total_vote, + res = com.twidere.twiderex.MR.strings.common_controls_status_poll_total_vote, it, ) } @@ -163,7 +162,7 @@ fun MastodonPoll(status: UiStatus) { } Spacer(modifier = Modifier.width(MastodonPollDefaults.VoteTimeSpacing)) if (status.poll?.expired == true) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_poll_expired)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_poll_expired)) } else { Text(text = status.poll?.expiresAt?.humanizedTimestamp() ?: "") } @@ -178,7 +177,7 @@ fun MastodonPoll(status: UiStatus) { }, enabled = voteState.isNotEmpty(), ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_vote)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_vote)) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 54fd51a00..92f169f30 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -53,13 +53,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.ExperimentalUnitApi import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.extensions.humanizedCount @@ -78,8 +77,8 @@ fun ReplyButton( withNumber: Boolean = true, ) { val navigator = LocalNavigator.current - val icon = painterResource(id = R.drawable.ic_corner_up_left) - val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_reply) + val icon = painterResource(res = com.twidere.twiderex.MR.files.corner_up_left) + val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_actions_reply) val action = { navigator.compose(ComposeType.Reply, statusKey = status.statusKey) } @@ -124,8 +123,8 @@ fun LikeButton( } else { LocalContentColor.current } - val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_like) - val icon = painterResource(id = R.drawable.ic_heart) + val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_actions_like) + val icon = painterResource(res = com.twidere.twiderex.MR.files.heart) val action = { if (account != null) { actionsViewModel.like(status, account) @@ -172,9 +171,9 @@ fun RetweetButton( } else { LocalContentColor.current } - val icon = painterResource(id = R.drawable.ic_repeat) + val icon = painterResource(res = com.twidere.twiderex.MR.files.repeat) val contentDescription = - stringResource(id = com.twidere.common.R.string.accessibility_common_status_actions_retweet) + stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_actions_retweet) var expanded by remember { mutableStateOf(false) } val retweetAction = { if (status.platformType == PlatformType.Twitter) { @@ -200,7 +199,7 @@ fun RetweetButton( expanded = false } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_retweet)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_retweet)) } val navigator = LocalNavigator.current DropdownMenuItem( @@ -209,7 +208,7 @@ fun RetweetButton( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_quote), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_quote), ) } } @@ -260,7 +259,7 @@ fun ShareButton( linkResolver = { data.resolveLink(it) }, ) val clipboardManager = LocalClipboardManager.current - val contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_more) + val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_more) Box( modifier = modifier, ) { @@ -316,7 +315,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_copy_text), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_copy_text), ) } DropdownMenuItem( @@ -330,7 +329,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_copy_link), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_copy_link), ) } DropdownMenuItem( @@ -340,7 +339,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_actions_share_link), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_share_link), ) } if (data.user.userKey == accountKey) { @@ -351,7 +350,7 @@ fun ShareButton( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 9fbde5051..784a5b0cd 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -51,9 +51,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.GridLayout import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer @@ -195,7 +194,7 @@ fun StatusMediaComponent( .size(StatusMediaDefaults.Sensitive.BackgroundSize) ) { Icon( - painter = painterResource(id = R.drawable.ic_alert_triangle), + painter = painterResource(res = com.twidere.twiderex.MR.files.alert_triangle), contentDescription = null, tint = MaterialTheme.colors.onSurface, modifier = Modifier @@ -226,7 +225,7 @@ fun StatusMediaComponent( .padding(StatusMediaDefaults.Icon.ContentPadding), ) { Icon( - painter = painterResource(id = R.drawable.ic_eye_off), + painter = painterResource(res = com.twidere.twiderex.MR.files.eye_off), contentDescription = null, tint = MaterialTheme.colors.onSurface, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index fd4aca24f..1f3d636e3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -43,9 +43,8 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus @@ -77,7 +76,7 @@ fun ColumnScope.StatusText( ) { Icon( modifier = Modifier.padding(StatusTextDefaults.Mastodon.SpoilerButtonPadding), - painter = painterResource(id = R.drawable.ic_expand_more), + painter = painterResource(res = com.twidere.twiderex.MR.files.expand_more), contentDescription = null, tint = MaterialTheme.colors.primary, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt index 0244d6637..7da9f777c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/StatusThread.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt @@ -28,9 +28,8 @@ import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.model.ui.UiStatus @Composable @@ -48,7 +47,7 @@ fun StatusThreadWithAvatar(modifier: Modifier = Modifier, data: UiStatus, onClic .height(StatusThreadDefaults.AvatarSize) ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_thread_show), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_thread_show), style = MaterialTheme.typography.body2 ) } @@ -62,7 +61,7 @@ fun StatusThreadTextOnly(modifier: Modifier = Modifier, onClick: () -> Unit) { onClick = onClick, ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_status_thread_show), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_thread_show), style = MaterialTheme.typography.body2 ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 70c9d7a69..21a4eb413 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -57,15 +57,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintSet import androidx.constraintlayout.compose.Dimension -import com.twidere.twiderex.R import com.twidere.twiderex.component.HumanizedTime import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.extensions.icon @@ -251,7 +250,7 @@ private fun MastodonStatusHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_user_plus), + painter = painterResource(res = com.twidere.twiderex.MR.files.user_plus), contentDescription = null, tint = Color(0xFF4C9EEB), ) @@ -259,7 +258,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = com.twidere.common.R.string.common_notification_follow, + res = com.twidere.twiderex.MR.strings.common_notification_follow, data.user.displayName ) ) @@ -271,7 +270,7 @@ private fun MastodonStatusHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_user_exclamation), + painter = painterResource(res = com.twidere.twiderex.MR.files.user_exclamation), contentDescription = null, tint = Color(0xFFFF9500), ) @@ -279,7 +278,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = com.twidere.common.R.string.common_notification_follow_request, + res = com.twidere.twiderex.MR.strings.common_notification_follow_request, data.user.displayName ) ) @@ -296,7 +295,7 @@ private fun MastodonStatusHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_heart), + painter = painterResource(res = com.twidere.twiderex.MR.files.heart), contentDescription = null, tint = Color(0xFFFF2D55), ) @@ -304,7 +303,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = com.twidere.common.R.string.common_notification_favourite, + res = com.twidere.twiderex.MR.strings.common_notification_favourite, data.user.displayName ) ) @@ -316,7 +315,7 @@ private fun MastodonStatusHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_poll), + painter = painterResource(res = com.twidere.twiderex.MR.files.poll), contentDescription = null, tint = Color(0xFF4C9EEB), ) @@ -325,11 +324,11 @@ private fun MastodonStatusHeader( val text = if (LocalActiveAccount.current?.let { it.accountKey == data.user.userKey } == true) { stringResource( - id = com.twidere.common.R.string.common_notification_own_poll, + res = com.twidere.twiderex.MR.strings.common_notification_own_poll, ) } else { stringResource( - id = com.twidere.common.R.string.common_notification_poll, + res = com.twidere.twiderex.MR.strings.common_notification_poll, ) } @@ -344,7 +343,7 @@ private fun MastodonStatusHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_bell_ringing), + painter = painterResource(res = com.twidere.twiderex.MR.files.bell_ringing), contentDescription = null, tint = Color(0xFFFF9500), ) @@ -352,7 +351,7 @@ private fun MastodonStatusHeader( text = { Text( text = stringResource( - id = com.twidere.common.R.string.common_notification_status, + res = com.twidere.twiderex.MR.strings.common_notification_status, data.user.displayName ) ) @@ -618,8 +617,8 @@ fun ColumnScope.StatusBody( ) { Icon( modifier = Modifier.size(16.dp), - painter = painterResource(id = R.drawable.ic_map_pin), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_location) + painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_location) ) Box(modifier = Modifier.width(StatusBodyDefaults.PlaceSpacing)) Text(text = status.geo.name) @@ -721,13 +720,13 @@ fun MediaPreviewButton( .padding(4.dp) ) { Icon( - painter = painterResource(id = R.drawable.ic_photo), + painter = painterResource(res = com.twidere.twiderex.MR.files.photo), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_status_media + res = com.twidere.twiderex.MR.strings.accessibility_common_status_media ) ) Spacer(modifier = Modifier.width(MediaPreviewButtonDefaults.IconSpacing)) - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_status_media)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_media)) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt similarity index 87% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt index c6ff6cd21..26dc89381 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt @@ -36,10 +36,9 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.ui.UiStatus @Composable @@ -49,8 +48,8 @@ fun RetweetHeader( TweetHeader( icon = { Icon( - painter = painterResource(id = R.drawable.ic_repeat), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_status_retweeted), + painter = painterResource(res = com.twidere.twiderex.MR.files.repeat), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_retweeted), tint = Color(0xFF4C9EEB) ) }, @@ -58,7 +57,7 @@ fun RetweetHeader( Text( style = MaterialTheme.typography.caption, text = stringResource( - id = com.twidere.common.R.string.common_controls_status_user_retweeted, + res = com.twidere.twiderex.MR.strings.common_controls_status_user_retweeted, data.user.displayName ), ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt index 898144e5e..430d9caaf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt @@ -31,10 +31,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource +import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser @@ -63,10 +62,10 @@ fun UserAvatar( ) if (withPlatformIcon) { val icon = when (user.platformType) { - PlatformType.Twitter -> painterResource(id = R.drawable.ic_twitter_badge) + PlatformType.Twitter -> painterResource(res = com.twidere.twiderex.MR.files.twitter_badge) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> painterResource(id = R.drawable.ic_mastodon_badge) + PlatformType.Mastodon -> painterResource(res = com.twidere.twiderex.MR.files.mastodon_badge) } Image( painter = icon, diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt index 055c3ba52..caf86cd18 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt @@ -41,10 +41,9 @@ import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeJoin import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.ui.UiTrend import com.twidere.twiderex.model.ui.UiTrendHistory @@ -61,7 +60,7 @@ fun MastodonTrendItem(trend: UiTrend, onClick: (UiTrend) -> Unit) { ListItem( modifier = Modifier.weight(1f), secondaryText = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_trends_accounts, trend.dailyAccounts), style = MaterialTheme.typography.body2) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_trends_accounts, trend.dailyAccounts), style = MaterialTheme.typography.body2) }, text = { Text(text = trend.displayName, style = MaterialTheme.typography.subtitle1) diff --git a/android/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 85853bc87..03809df68 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -74,10 +74,10 @@ data class UiMedia( url = null, belongToKey = MicroBlogKey.Empty, mediaUrl = null, - previewUrl = "", // painterResource(id = R.drawable.featured_graphics), + previewUrl = "", // painterResource(res = R.drawable.featured_graphics), type = MediaType.photo, - width = 0, // painterResource(id = R.drawable.featured_graphics).intrinsicSize.width.toLong(), - height = 0, // painterResource(id = R.drawable.featured_graphics).intrinsicSize.height.toLong(), + width = 0, // painterResource(res = R.drawable.featured_graphics).intrinsicSize.width.toLong(), + height = 0, // painterResource(res = R.drawable.featured_graphics).intrinsicSize.height.toLong(), pageUrl = null, altText = "", order = 0, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt index e9780a4b7..5246c2fbb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt @@ -96,7 +96,7 @@ data class UiStatus( @Composable fun sample() = UiStatus( statusId = "", - htmlText = "", // stringResource(id = com.twidere.common.R.string.scene_settings_display_preview_thank_for_using_twidere_x), + htmlText = "", // stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_preview_thank_for_using_twidere_x), timestamp = System.currentTimeMillis(), metrics = StatusMetrics( retweet = 1200, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt index 5b1003b83..874871fc8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt @@ -66,7 +66,7 @@ data class UiUser( id = "", name = "Twidere", screenName = "TwidereProject", - profileImage = "", // painterResource(id = R.drawable.ic_profile_image_twidere), + profileImage = "", // painterResource(res = com.twidere.twiderex.MR.files.profile_image_twidere), profileBackgroundImage = null, metrics = UserMetrics( fans = 0, diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/navigation/Route.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/navigation/Router.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index 437e57daa..532edf84d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -40,7 +40,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -62,7 +62,7 @@ fun DraftListScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_drafts_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_drafts_title)) } ) } @@ -99,7 +99,7 @@ fun DraftListSceneContent( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_more + res = com.twidere.twiderex.MR.strings.accessibility_common_more ) ) } @@ -112,7 +112,7 @@ fun DraftListSceneContent( navController.navigate(RootRoute.Draft.Compose(it.draftId)) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_drafts_actions_edit_draft)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_drafts_actions_edit_draft)) } DropdownMenuItem( onClick = { @@ -120,7 +120,7 @@ fun DraftListSceneContent( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index b130b5c79..a4fc0e9ca 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -70,10 +70,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.UserMetrics import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarDefaults @@ -223,7 +222,7 @@ private fun EmptyColumnHomeContent(scaffoldState: ScaffoldState) { verticalArrangement = Arrangement.Center, ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_column), + painter = painterResource(res = com.twidere.twiderex.MR.files.empty_column), contentDescription = null, ) Spacer(modifier = Modifier.height(EmptyColumnHomeContentDefaults.VerticalPadding)) @@ -460,7 +459,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { } ), text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_drawer_sign_in)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_drawer_sign_in)) } ) } @@ -473,7 +472,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { } ), text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_manage_accounts_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_manage_accounts_title)) } ) } @@ -520,14 +519,14 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { ), icon = { Icon( - painter = painterResource(id = R.drawable.ic_adjustments_horizontal), + painter = painterResource(res = com.twidere.twiderex.MR.files.adjustments_horizontal), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_settings_title + res = com.twidere.twiderex.MR.strings.scene_settings_title ) ) }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_title)) } ) } @@ -603,7 +602,7 @@ private fun DrawerUserHeader( modifier = Modifier.rotate(rotate), imageVector = Icons.Default.ArrowDropDown, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_home_drawer_account_dropdown + res = com.twidere.twiderex.MR.strings.accessibility_scene_home_drawer_account_dropdown ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 02fd7106c..bb462d07a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -65,8 +65,8 @@ import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsHeight @@ -79,7 +79,6 @@ import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.rememberPagerState import com.google.android.exoplayer2.ui.PlayerControlView import com.mxalbert.zoomable.Zoomable -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.NetworkImage @@ -277,9 +276,9 @@ fun StatusMediaScene(status: UiStatus, selectedIndex: Int, viewModel: MediaViewM } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_close + res = com.twidere.twiderex.MR.strings.accessibility_common_close ) ) } @@ -343,7 +342,7 @@ private fun StatusMediaInfo( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_save), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_save), ) } DropdownMenuItem( @@ -359,7 +358,7 @@ private fun StatusMediaInfo( } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_share_media), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_share_media), ) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt similarity index 97% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index b06aa705e..e04dc0532 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -50,8 +50,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsPadding @@ -59,7 +59,6 @@ import com.google.accompanist.insets.statusBarsPadding import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.rememberPagerState import com.google.android.exoplayer2.ui.PlayerControlView -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.hideControls @@ -240,9 +239,9 @@ fun PureMediaControlPanel( } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_close + res = com.twidere.twiderex.MR.strings.accessibility_common_close ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt similarity index 83% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index 7dcabbcc1..f8fb769f0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -44,11 +44,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.DefaultConfig -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold import com.twidere.twiderex.navigation.RootRoute @@ -94,15 +93,15 @@ private fun MastodonSignIn() { ListItem( icon = { Icon( - painter = painterResource(id = R.drawable.ic_mastodon_logo_blue), + painter = painterResource(res = com.twidere.twiderex.MR.files.mastodon_logo_blue), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_logo_mastodon + res = com.twidere.twiderex.MR.strings.accessibility_common_logo_mastodon ) ) }, text = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_sign_in_with_mastodon) ) }, trailing = { @@ -113,7 +112,7 @@ private fun MastodonSignIn() { Icon( imageVector = Icons.Default.KeyboardArrowRight, contentDescription = stringResource( - id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon + res = com.twidere.twiderex.MR.strings.scene_sign_in_sign_in_with_mastodon ) ) } @@ -156,22 +155,22 @@ private fun TwitterSignIn() { ListItem( icon = { Icon( - painter = painterResource(id = R.drawable.ic_twitter_logo_white), + painter = painterResource(res = com.twidere.twiderex.MR.files.twitter_logo_white), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_logo_twitter + res = com.twidere.twiderex.MR.strings.accessibility_common_logo_twitter ) ) }, text = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_twitter) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_sign_in_with_twitter) ) }, trailing = { IconButton(onClick = { showKeyConfiguration = true }) { Icon( imageVector = Icons.Default.MoreHoriz, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_more) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_more) ) } } @@ -192,11 +191,11 @@ private fun TwitterCustomKeySignIn( onDismissRequest.invoke() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_twitter_options_sign_in_with_custom_twitter_key)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_twitter_options_sign_in_with_custom_twitter_key)) }, text = { Column { - Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_twitter_options_twitter_api_v2_access_is_required)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_twitter_options_twitter_api_v2_access_is_required)) OutlinedTextField( modifier = Modifier.fillMaxWidth(), value = apiKey, @@ -221,7 +220,7 @@ private fun TwitterCustomKeySignIn( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_cancel)) } }, confirmButton = { @@ -244,7 +243,7 @@ private fun TwitterCustomKeySignIn( }, enabled = apiKey.isNotEmpty() && apiSecret.isNotEmpty() ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_drawer_sign_in)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_drawer_sign_in)) } }, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt similarity index 98% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt index bc9a76ec0..e0fe7c1a7 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/StatusScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -82,7 +82,7 @@ fun StatusScene( topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_search_tabs_tweets)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_tabs_tweets)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index c65993719..78f34cc4f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -96,15 +96,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.dp import com.google.accompanist.insets.LocalWindowInsets -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.CheckboxItem import com.twidere.twiderex.component.foundation.InAppNotificationBottomSheetScaffold @@ -240,9 +239,9 @@ private fun ComposeBody( title = { Text( text = when (composeType) { - ComposeType.Reply -> stringResource(id = com.twidere.common.R.string.scene_compose_title_reply) - ComposeType.Quote -> stringResource(id = com.twidere.common.R.string.scene_compose_title_quote) - else -> stringResource(id = com.twidere.common.R.string.scene_compose_title_compose) + ComposeType.Reply -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_title_reply) + ComposeType.Quote -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_title_quote) + else -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_title_compose) } ) }, @@ -257,9 +256,9 @@ private fun ComposeBody( } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_close + res = com.twidere.twiderex.MR.strings.accessibility_common_close ) ) } @@ -274,9 +273,9 @@ private fun ComposeBody( } ) { Icon( - painter = painterResource(id = if (enableThreadMode) R.drawable.ic_send_thread else R.drawable.ic_send), + painter = painterResource(res = if (enableThreadMode) com.twidere.twiderex.MR.files.send_thread else com.twidere.twiderex.MR.files.send), contentDescription = stringResource( - id = if (enableThreadMode) com.twidere.common.R.string.accessibility_scene_compose_thread else com.twidere.common.R.string.accessibility_scene_compose_send + id = if (enableThreadMode) com.twidere.twiderex.MR.strings.accessibility_scene_compose_thread else com.twidere.twiderex.MR.strings.accessibility_scene_compose_send ), tint = if (canSend) MaterialTheme.colors.primary else LocalContentColor.current.copy( alpha = LocalContentAlpha.current @@ -585,7 +584,7 @@ private fun MastodonExtraActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_eye_off), + painter = painterResource(res = com.twidere.twiderex.MR.files.eye_off), contentDescription = null, tint = if (isImageSensitive) { MaterialTheme.colors.primary @@ -604,7 +603,7 @@ private fun MastodonExtraActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_alert_octagon), + painter = painterResource(res = com.twidere.twiderex.MR.files.alert_octagon), contentDescription = null, tint = if (isContentWarning) { MaterialTheme.colors.primary @@ -622,9 +621,9 @@ private fun LocationDisplay(it: com.twidere.twiderex.model.kmp.Location) { ) { Row { Icon( - painter = painterResource(id = R.drawable.ic_map_pin), + painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_status_location + res = com.twidere.twiderex.MR.strings.accessibility_common_status_location ) ) Text(text = "${it.latitude}, ${it.longitude}") @@ -766,15 +765,15 @@ private fun ReplySheetContent( } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_close + res = com.twidere.twiderex.MR.strings.accessibility_common_close ) ) } }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_replying_to)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_replying_to)) } ) status?.let { @@ -803,7 +802,7 @@ private fun ReplySheetContent( } if (replyToUser.any()) { ListItem { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_others_in_this_conversation)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_others_in_this_conversation)) } Divider(modifier = Modifier.padding(horizontal = 16.dp)) replyToUser.forEach { user -> @@ -858,18 +857,18 @@ private fun ConfirmDraftDialog( onDismissRequest = onDismiss, text = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_compose_save_draft_message), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_save_draft_message), style = MaterialTheme.typography.body2 ) }, dismissButton = { TextButton(onClick = onCancel) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_cancel)) } }, confirmButton = { TextButton(onClick = onConfirm) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_save_draft_action)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_save_draft_action)) } }, ) @@ -916,7 +915,7 @@ private fun ComposeInput( autoFocus = autoFocus, placeholder = { CompositionLocalProvider(LocalContentAlpha.provides(ContentAlpha.medium)) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_placeholder)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_placeholder)) } }, keyboardOptions = KeyboardOptions( @@ -954,7 +953,7 @@ private fun ColumnScope.MastodonContentWarningInput(viewModel: ComposeViewModel) LocalContentAlpha provides ContentAlpha.disabled, ) { Icon( - painter = painterResource(id = R.drawable.ic_alert_octagon), + painter = painterResource(res = com.twidere.twiderex.MR.files.alert_octagon), contentDescription = null, ) } @@ -966,7 +965,7 @@ private fun ColumnScope.MastodonContentWarningInput(viewModel: ComposeViewModel) CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.disabled, ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_cw_placeholder)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_cw_placeholder)) } }, keyboardOptions = KeyboardOptions( @@ -1103,7 +1102,7 @@ private fun ComposeVote(voteState: VoteState) { modifier = Modifier.fillMaxWidth(), checked = multiple, onCheckedChange = { voteState.setMultiple(!multiple) }, - text = stringResource(id = com.twidere.common.R.string.scene_compose_vote_multiple) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_multiple) ) } } @@ -1164,9 +1163,9 @@ private fun ComposeActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_camera), + painter = painterResource(res = com.twidere.twiderex.MR.files.camera), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_compose_image + res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_image ) ) } @@ -1178,7 +1177,7 @@ private fun ComposeActions( } ) { Icon( - painter = painterResource(id = if (showEmoji) R.drawable.ic_keyboard else R.drawable.ic_mood_smile), + painter = painterResource(res = if (showEmoji) com.twidere.twiderex.MR.files.keyboard else com.twidere.twiderex.MR.files.mood_smile), contentDescription = null, tint = if (showEmoji) MaterialTheme.colors.primary @@ -1194,7 +1193,7 @@ private fun ComposeActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_poll), + painter = painterResource(res = com.twidere.twiderex.MR.files.poll), contentDescription = null, tint = if (isInVoteState) { MaterialTheme.colors.primary @@ -1206,7 +1205,7 @@ private fun ComposeActions( } // TODO: // IconButton(onClick = {}) { -// Icon(painter = painterResource(id = R.drawable.ic_gif)) +// Icon(painter = painterResource(res = com.twidere.twiderex.MR.files.gif)) // } if (account.type == PlatformType.Mastodon || account.type == PlatformType.Twitter) { IconButton( @@ -1222,9 +1221,9 @@ private fun ComposeActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_at_sign), + painter = painterResource(res = com.twidere.twiderex.MR.files.at_sign), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_compose_add_mention, + res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_add_mention, ) ) } @@ -1243,7 +1242,7 @@ private fun ComposeActions( } ) { Icon( - painter = painterResource(id = R.drawable.ic_hash), + painter = painterResource(res = com.twidere.twiderex.MR.files.hash), contentDescription = null ) } @@ -1263,12 +1262,12 @@ private fun ComposeActions( }, ) { Icon( - painter = painterResource(id = R.drawable.ic_map_pin), + painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), contentDescription = stringResource( id = if (locationEnabled) { - com.twidere.common.R.string.accessibility_scene_compose_location_disable + com.twidere.twiderex.MR.strings.accessibility_scene_compose_location_disable } else { - com.twidere.common.R.string.accessibility_scene_compose_location_enable + com.twidere.twiderex.MR.strings.accessibility_scene_compose_location_enable } ) ) @@ -1280,8 +1279,8 @@ private fun ComposeActions( }, ) { Icon( - painter = painterResource(id = R.drawable.ic_thread_mode), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_compose_thread), + painter = painterResource(res = com.twidere.twiderex.MR.files.thread_mode), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_thread), tint = if (enableThreadMode) MaterialTheme.colors.primary else LocalContentColor.current.copy( alpha = LocalContentAlpha.current ) @@ -1298,12 +1297,12 @@ private fun ComposeActions( Icon( painter = painterResource( id = if (draftCount.value > 9) - R.drawable.ic_drafts_more + com.twidere.twiderex.MR.files.drafts_more else - R.drawable.ic_draft_number + com.twidere.twiderex.MR.files.draft_number ), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_compose_draft + res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_draft ) ) if (draftCount.value < 9) { @@ -1361,7 +1360,7 @@ private fun ComposeImage(item: String, viewModel: ComposeViewModel) { } ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_remove), color = Color.Red, ) } @@ -1376,12 +1375,12 @@ private object ComposeImageDefaults { @Composable fun VoteExpired.stringName(): String { return when (this) { - VoteExpired.Min_5 -> stringResource(id = R.string.scene_compose_vote_expiration_5_Min) - VoteExpired.Min_30 -> stringResource(id = R.string.scene_compose_vote_expiration_30_Min) - VoteExpired.Hour_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Hour) - VoteExpired.Hour_6 -> stringResource(id = R.string.scene_compose_vote_expiration_6_Hour) - VoteExpired.Day_1 -> stringResource(id = R.string.scene_compose_vote_expiration_1_Day) - VoteExpired.Day_3 -> stringResource(id = R.string.scene_compose_vote_expiration_3_Day) - VoteExpired.Day_7 -> stringResource(id = R.string.scene_compose_vote_expiration_7_Day) + VoteExpired.Min_5 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_5_Min) + VoteExpired.Min_30 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_30_Min) + VoteExpired.Hour_1 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_1_Hour) + VoteExpired.Hour_6 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_6_Hour) + VoteExpired.Day_1 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_1_Day) + VoteExpired.Day_3 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_3_Day) + VoteExpired.Day_7 -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_vote_expiration_7_Day) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index 2fa9dce31..dd37ae5ad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -37,7 +37,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items @@ -72,7 +72,7 @@ fun ComposeSearchHashtagScene() { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_hashtag_search_search_placeholder)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_hashtag_search_search_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -99,7 +99,7 @@ fun ComposeSearchHashtagScene() { Icon( imageVector = Icons.Default.Done, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_done + res = com.twidere.twiderex.MR.strings.accessibility_common_done ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index 07fe7b00b..c4f6c9f60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -34,7 +34,7 @@ import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.AppBar @@ -71,7 +71,7 @@ fun ComposeSearchUserScene() { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_compose_user_search_search_placeholder)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_user_search_search_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -98,7 +98,7 @@ fun ComposeSearchUserScene() { Icon( imageVector = Icons.Default.Done, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_done + res = com.twidere.twiderex.MR.strings.accessibility_common_done ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index c87facabf..e2d2345c8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -26,11 +26,10 @@ import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -54,7 +53,7 @@ fun DMConversationListScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_title)) }, ) }, @@ -76,9 +75,9 @@ fun DMConversationListSceneFab() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(res = com.twidere.twiderex.MR.files.add), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_lists_icons_create + res = com.twidere.twiderex.MR.strings.scene_lists_icons_create ), ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index 825826195..326ac8757 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -63,13 +63,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.compose.collectAsLazyPagingItems import com.google.accompanist.insets.ExperimentalAnimatedInsets -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -111,7 +110,7 @@ fun DMConversationScene(conversationKey: MicroBlogKey) { }, ) { if (!account.supportDirectMessage) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_error_not_supported)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_error_not_supported)) } else { // user might enter this page by notifications after switch platform NormalContent(viewModel) @@ -129,7 +128,7 @@ fun NormalContent(viewModel: DMEventViewModel) { viewModel.inputImage.value = it.toString() }, ) - val copyText = stringResource(id = com.twidere.common.R.string.scene_messages_action_copy_text) + val copyText = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_action_copy_text) val source = viewModel.source.collectAsLazyPagingItems() val input by viewModel.input.observeAsState(initial = "") val inputImage by viewModel.inputImage.observeAsState(null) @@ -213,7 +212,7 @@ fun MessageActionComponent( onDismissRequest() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_action_copy_text)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_action_copy_text)) } ListItem( modifier = Modifier.clickable { @@ -221,7 +220,7 @@ fun MessageActionComponent( onDismissRequest() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_action_delete)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_action_delete)) } } } @@ -260,8 +259,8 @@ fun InputPhotoPreview(inputImage: String?, onRemove: () -> Unit) { .clickable { onRemove() } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), - contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_remove), tint = MaterialTheme.colors.surface, modifier = Modifier.padding(InputPhotoPreviewDefaults.IconPadding) ) @@ -300,9 +299,9 @@ fun InputComponent( } ) { Icon( - painter = painterResource(id = R.drawable.ic_camera), + painter = painterResource(res = com.twidere.twiderex.MR.files.camera), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_compose_image + res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_image ) ) } @@ -320,9 +319,9 @@ fun InputComponent( onClick = onSend ) { Icon( - painter = painterResource(id = R.drawable.ic_send), + painter = painterResource(res = com.twidere.twiderex.MR.files.send), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_compose_send + res = com.twidere.twiderex.MR.strings.accessibility_scene_compose_send ), tint = if (enableSend) MaterialTheme.colors.primary else LocalContentColor.current.copy(alpha = LocalContentAlpha.current) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index 17fd1a2c1..a9282a95e 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -37,12 +37,11 @@ import androidx.compose.runtime.Composable 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.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -75,7 +74,7 @@ fun DMNewConversationScene() { ) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_new_conversation_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_new_conversation_title)) }, elevation = 0.dp ) @@ -119,9 +118,9 @@ fun SearchInput( modifier = modifier.padding(SearchInputDefaults.ContentPadding) ) { Icon( - painter = painterResource(id = R.drawable.ic_search), + painter = painterResource(res = com.twidere.twiderex.MR.files.search), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_search_title + res = com.twidere.twiderex.MR.strings.scene_search_title ) ) Spacer(modifier = Modifier.width(SearchInputDefaults.ContentSpacing)) @@ -129,7 +128,7 @@ fun SearchInput( value = input, onValueChange = onValueChanged, modifier = Modifier.weight(1f), placeholder = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_messages_new_conversation_search)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_new_conversation_search)) }, maxLines = 1 ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt similarity index 86% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt index c5234e08b..35e5ca6d4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt @@ -23,10 +23,9 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService -import com.twidere.twiderex.R import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -41,14 +40,14 @@ import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel class AllNotificationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_notification_tabs_all) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_notification_tabs_all) } override val route: String get() = TODO("Not yet implemented") @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_message_circle) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.message_circle) @Composable override fun Content() { @@ -69,7 +68,7 @@ fun AllNotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_notification_tabs_all)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_notification_tabs_all)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt index fb915d125..501dd3a8a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt @@ -22,9 +22,8 @@ package com.twidere.twiderex.scenes.home import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.dm.DMConversationListSceneContent @@ -33,7 +32,7 @@ import com.twidere.twiderex.scenes.dm.DMConversationListSceneFab class DMConversationListItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_messages_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_messages_title) } override val route: String @@ -41,7 +40,7 @@ class DMConversationListItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_mail) + return painterResource(res = com.twidere.twiderex.MR.files.mail) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt similarity index 84% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt index 39a8d1812..a3ed73ff6 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt @@ -22,9 +22,8 @@ package com.twidere.twiderex.scenes.home import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.DraftListSceneContent @@ -32,7 +31,7 @@ import com.twidere.twiderex.scenes.DraftListSceneContent class DraftNavigationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_drafts_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_drafts_title) } override val route: String @@ -40,7 +39,7 @@ class DraftNavigationItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_note) + return painterResource(res = com.twidere.twiderex.MR.files.note) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 30643c113..a09217564 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -25,11 +25,10 @@ import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -46,12 +45,12 @@ import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel class HomeTimelineItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(com.twidere.common.R.string.scene_timeline_title) + override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_timeline_title) override val route: String get() = RootRoute.HomeTimeline @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_home) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.home) @Composable override fun Content() { @@ -76,7 +75,7 @@ fun HomeTimelineScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_timeline_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_timeline_title)) }, navigationIcon = { AppBarNavigationButton() @@ -101,9 +100,9 @@ private fun HomeTimelineFab() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_feather), + painter = painterResource(res = com.twidere.twiderex.MR.files.feather), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_home_compose + res = com.twidere.twiderex.MR.strings.accessibility_scene_home_compose ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt similarity index 86% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt index e9f3ca4a9..0175a7eb4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt @@ -23,9 +23,8 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.FabPosition import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.lists.ListsSceneContent @@ -34,7 +33,7 @@ import com.twidere.twiderex.scenes.lists.ListsSceneFab class ListsNavigationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_lists_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_title) } override val route: String @@ -42,7 +41,7 @@ class ListsNavigationItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_lists) + return painterResource(res = com.twidere.twiderex.MR.files.lists) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt similarity index 84% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index d5398e2ad..6293091da 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -23,9 +23,8 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -38,12 +37,12 @@ import com.twidere.twiderex.ui.TwidereScene class MeItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(com.twidere.common.R.string.scene_profile_title) + override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_profile_title) override val route: String get() = RootRoute.Me @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_user) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.user) override val withAppBar: Boolean get() = false @@ -61,7 +60,7 @@ fun MeScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_profile_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_profile_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index 8baafece8..ef31f34ec 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -23,9 +23,8 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -39,12 +38,12 @@ import com.twidere.twiderex.viewmodel.timeline.MentionsTimelineViewModel class MentionItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(com.twidere.common.R.string.scene_mentions_title) + override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_mentions_title) override val route: String get() = RootRoute.Mentions @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_message_circle) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.message_circle) @Composable override fun Content() { @@ -63,7 +62,7 @@ fun MentionScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_mentions_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_mentions_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt similarity index 86% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index 4e05a3f18..9ed88d15f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -23,10 +23,9 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService -import com.twidere.twiderex.R import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -41,12 +40,12 @@ import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel class NotificationItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(com.twidere.common.R.string.scene_notification_title) + override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_notification_title) override val route: String get() = RootRoute.Notification @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_message_circle) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.message_circle) @Composable override fun Content() { @@ -63,7 +62,7 @@ fun NotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_mentions_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_mentions_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt similarity index 86% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index 1f44c6561..a0f113039 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -44,11 +44,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -68,12 +67,12 @@ import com.twidere.twiderex.viewmodel.trend.TrendViewModel class SearchItem : HomeNavigationItem() { @Composable - override fun name(): String = stringResource(com.twidere.common.R.string.scene_search_title) + override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_search_title) override val route: String get() = RootRoute.Search.Home @Composable - override fun icon(): Painter = painterResource(id = R.drawable.ic_search) + override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.search) override val withAppBar: Boolean get() = false @@ -91,7 +90,7 @@ fun SearchScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_search_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_title)) }, navigationIcon = { AppBarNavigationButton() @@ -136,7 +135,7 @@ fun SearchSceneContent() { modifier = Modifier .weight(1F) .align(Alignment.CenterVertically), - text = stringResource(id = com.twidere.common.R.string.scene_search_search_bar_placeholder), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_search_bar_placeholder), ) } IconButton( @@ -145,9 +144,9 @@ fun SearchSceneContent() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_search), + painter = painterResource(res = com.twidere.twiderex.MR.files.search), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_search_title + res = com.twidere.twiderex.MR.strings.scene_search_title ) ) } @@ -161,7 +160,7 @@ fun SearchSceneContent() { item { if (source.isNotEmpty()) ListItem { Text( - text = stringResource(id = com.twidere.common.R.string.scene_search_saved_search), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_saved_search), style = MaterialTheme.typography.button ) } @@ -181,9 +180,9 @@ fun SearchSceneContent() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_trash_can), + painter = painterResource(res = com.twidere.twiderex.MR.files.trash_can), contentDescription = stringResource( - id = com.twidere.common.R.string.common_controls_actions_remove + res = com.twidere.twiderex.MR.strings.common_controls_actions_remove ) ) } @@ -203,7 +202,7 @@ fun SearchSceneContent() { } ) { Text( - text = if (expandSearch) stringResource(id = com.twidere.common.R.string.scene_search_show_less) else stringResource(id = com.twidere.common.R.string.scene_search_show_more), + text = if (expandSearch) stringResource(res = com.twidere.twiderex.MR.strings.scene_search_show_less) else stringResource(res = com.twidere.twiderex.MR.strings.scene_search_show_more), style = MaterialTheme.typography.subtitle1, color = MaterialTheme.colors.primary ) @@ -217,14 +216,14 @@ fun SearchSceneContent() { when (account.type) { PlatformType.Twitter -> Text( - text = stringResource(id = com.twidere.common.R.string.scene_trends_world_wide), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_trends_world_wide), style = MaterialTheme.typography.button ) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> Text( - text = stringResource(id = com.twidere.common.R.string.scene_trends_world_wide), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_trends_world_wide), style = MaterialTheme.typography.button ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index f47a21dd7..6b1e2efa5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -23,10 +23,9 @@ package com.twidere.twiderex.scenes.home.mastodon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.R import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -42,7 +41,7 @@ import com.twidere.twiderex.viewmodel.timeline.mastodon.FederatedTimelineViewMod class FederatedTimelineItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_federated_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_federated_title) } override val route: String @@ -50,7 +49,7 @@ class FederatedTimelineItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_globe) + return painterResource(res = com.twidere.twiderex.MR.files.globe) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index 52e0e8840..ba22feb19 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -23,10 +23,9 @@ package com.twidere.twiderex.scenes.home.mastodon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.services.mastodon.MastodonService -import com.twidere.twiderex.R import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -42,7 +41,7 @@ import com.twidere.twiderex.viewmodel.timeline.mastodon.LocalTimelineViewModel class LocalTimelineItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_local_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_local_title) } override val route: String @@ -50,7 +49,7 @@ class LocalTimelineItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_users) + return painterResource(res = com.twidere.twiderex.MR.files.users) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 2141ad5dc..114968ce8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -27,10 +27,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -49,7 +48,7 @@ import kotlinx.coroutines.launch class MastodonNotificationItem : HomeNavigationItem() { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_notification_title) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_notification_title) } override val route: String @@ -57,7 +56,7 @@ class MastodonNotificationItem : HomeNavigationItem() { @Composable override fun icon(): Painter { - return painterResource(id = R.drawable.ic_bell) + return painterResource(res = com.twidere.twiderex.MR.files.bell) } override var lazyListController: LazyListController = LazyListController() @@ -79,7 +78,7 @@ fun MastodonNotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_notification_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_notification_title)) }, navigationIcon = { AppBarNavigationButton() diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index 4fb8bc161..51c2ddfd8 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -40,14 +40,13 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarDefaults import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -106,12 +105,12 @@ fun ListsAddMembersScene( ) { Icon( imageVector = Icons.Default.Close, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_back) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_back) ) } }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_users_add_title)) }, elevation = 0.dp, ) @@ -120,14 +119,14 @@ fun ListsAddMembersScene( verticalAlignment = Alignment.CenterVertically ) { Icon( - painter = painterResource(id = R.drawable.ic_search), - contentDescription = stringResource(id = com.twidere.common.R.string.scene_search_title), + painter = painterResource(res = com.twidere.twiderex.MR.files.search), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_title), modifier = Modifier.padding(ListsAddMembersSceneDefaults.SearchInput.Icon.Padding) ) TextInput( value = keyword, placeholder = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_search)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_users_add_search)) }, onValueChange = { searchViewModel.text.value = it @@ -180,13 +179,13 @@ private fun SearchResultsContent(source: LazyPagingItems, onAction: (use val pending = statusChecker(it) if (pending) { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_users_menu_actions_remove), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_users_menu_actions_remove), style = MaterialTheme.typography.button, color = Color(0xFFFF3B30), ) } else { Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_users_menu_actions_add), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_users_menu_actions_add), style = MaterialTheme.typography.button, color = MaterialTheme.colors.primary, ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index d0386ef51..f36965705 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -41,12 +41,11 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -84,7 +83,7 @@ fun ListsMembersScene( AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_members)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_tabs_members)) } ) }, @@ -103,14 +102,14 @@ fun ListsMembersScene( modifier = Modifier.padding(ListsMembersSceneDefaults.Fab.ContentPadding) ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(res = com.twidere.twiderex.MR.files.add), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_lists_details_add_members + res = com.twidere.twiderex.MR.strings.scene_lists_details_add_members ), modifier = Modifier.padding(ListsMembersSceneDefaults.Fab.IconPadding) ) Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_users_add_title) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_users_add_title) .uppercase(Locale.getDefault()), style = MaterialTheme.typography.button ) @@ -136,7 +135,7 @@ fun ListsMembersScene( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = com.twidere.common.R.string.scene_lists_users_menu_actions_remove + res = com.twidere.twiderex.MR.strings.scene_lists_users_menu_actions_remove ) ) } @@ -151,7 +150,7 @@ fun ListsMembersScene( ) { Text( text = stringResource( - com.twidere.common.R.string.scene_lists_users_menu_actions_remove + com.twidere.twiderex.MR.strings.scene_lists_users_menu_actions_remove ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index 76bd4c5fc..3b652d112 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -31,12 +31,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -61,7 +60,7 @@ fun ListsScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_title)) }, ) }, @@ -94,14 +93,14 @@ fun ListsSceneFab() { modifier = Modifier.padding(ListsSceneDefaults.Fab.ContentPadding) ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(res = com.twidere.twiderex.MR.files.add), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_lists_icons_create + res = com.twidere.twiderex.MR.strings.scene_lists_icons_create ), modifier = Modifier.padding(ListsSceneDefaults.Fab.IconPadding) ) Text( - text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_create_title) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_create_title) .uppercase(Locale.getDefault()), style = MaterialTheme.typography.button ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt similarity index 91% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt index 86739ec3d..1fc6c0b71 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.scenes.lists import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -48,7 +48,7 @@ fun ListsSubscribersScene( AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_subscriber)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_tabs_subscriber)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index 5670f2040..35fa29cfe 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -45,14 +45,13 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -100,14 +99,14 @@ fun ListTimeLineScene( Row(verticalAlignment = Alignment.CenterVertically) { Text( text = source?.title - ?: stringResource(id = com.twidere.common.R.string.scene_lists_details_title), + ?: stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_title), maxLines = 1, overflow = TextOverflow.Ellipsis ) if (source?.isPrivate == true) Icon( - painter = painterResource(id = R.drawable.ic_lock), - contentDescription = stringResource(id = com.twidere.common.R.string.scene_lists_icons_private), + painter = painterResource(res = com.twidere.twiderex.MR.files.lock), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_icons_private), modifier = Modifier .alpha(ContentAlpha.disabled) .padding(start = ListTimelineSceneDefaults.LockIconPadding) @@ -123,7 +122,7 @@ fun ListTimeLineScene( Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = com.twidere.common.R.string.scene_lists_details_menu_actions_edit_list + res = com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_edit_list ) ) } @@ -147,9 +146,9 @@ fun ListTimeLineScene( Text( text = stringResource( id = if (following) - com.twidere.common.R.string.scene_lists_details_menu_actions_unfollow + com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_unfollow else - com.twidere.common.R.string.scene_lists_details_menu_actions_follow + com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_follow ) ) } @@ -166,7 +165,7 @@ fun ListTimeLineScene( ) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_members)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_tabs_members)) } if (uiList.allowToSubscribe) { @@ -180,7 +179,7 @@ fun ListTimeLineScene( ) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_tabs_subscriber)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_tabs_subscriber)) } } @@ -200,9 +199,9 @@ fun ListTimeLineScene( ) { Text( text = if (account.type == PlatformType.Mastodon) - stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_rename_list) + stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_rename_list) else - stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_edit_list) + stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_edit_list) ) } } @@ -214,7 +213,7 @@ fun ListTimeLineScene( showDeleteConfirmDialog = true } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_details_menu_actions_delete_list)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_delete_list)) } } } @@ -286,7 +285,7 @@ private fun ListDeleteConfirmDialog( title = { Text( text = stringResource( - id = com.twidere.common.R.string.scene_lists_details_delete_list_title, + res = com.twidere.twiderex.MR.strings.scene_lists_details_delete_list_title, title ), style = MaterialTheme.typography.subtitle1 @@ -303,7 +302,7 @@ private fun ListDeleteConfirmDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_cancel)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_cancel)) } }, confirmButton = { @@ -313,7 +312,7 @@ private fun ListDeleteConfirmDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_yes)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_yes)) } }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index 9c0894ed1..3047beb9d 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -26,9 +26,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent import com.twidere.twiderex.di.ext.getViewModel @@ -69,7 +68,7 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { if (showMastodonComponent) { MastodonListsModifyComponent( onDismissRequest = { dismiss() }, - title = stringResource(id = com.twidere.common.R.string.scene_lists_modify_dialog_create), + title = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_dialog_create), name = name, onNameChanged = { name = it } ) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt index 6ba3e8737..059beacbf 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent @@ -67,7 +67,7 @@ fun MastodonListsEditDialog(listKey: MicroBlogKey, onDismissRequest: () -> Unit) } MastodonListsModifyComponent( onDismissRequest = { dismiss() }, - title = stringResource(id = com.twidere.common.R.string.scene_lists_modify_dialog_edit), + title = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_dialog_edit), name = name, onNameChanged = { name = it } ) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index c0bf16a32..bc4465d60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -36,9 +36,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -76,7 +75,7 @@ fun TwitterListsCreateScene() { AppBar( navigationIcon = { AppBarNavigationButton(Icons.Default.Close) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_create_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_create_title)) }, actions = { IconButton( @@ -100,7 +99,7 @@ fun TwitterListsCreateScene() { ) { Icon( imageVector = Icons.Default.Done, - contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_confirm), tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy( alpha = LocalContentAlpha.current ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt index 14e97e28c..df61cdb5b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt @@ -32,7 +32,7 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -67,7 +67,7 @@ fun TwitterListsEditScene( AppBar( navigationIcon = { AppBarNavigationButton(Icons.Default.Close) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_lists_modify_edit_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_lists_modify_edit_title)) }, actions = { IconButton( @@ -85,7 +85,7 @@ fun TwitterListsEditScene( ) { Icon( imageVector = Icons.Default.Done, - contentDescription = stringResource(id = com.twidere.common.R.string.common_controls_actions_confirm), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_confirm), tint = if (name.isNotEmpty()) MaterialTheme.colors.primary else LocalContentColor.current.copy( alpha = LocalContentAlpha.current ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt index edb08e7c9..ed539315b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt @@ -45,13 +45,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold import com.twidere.twiderex.di.ext.getViewModel @@ -103,14 +102,14 @@ fun MastodonSignInScene() { ListItem( icon = { Icon( - painter = painterResource(id = R.drawable.ic_mastodon_logo_white), + painter = painterResource(res = com.twidere.twiderex.MR.files.mastodon_logo_white), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_logo_mastodon + res = com.twidere.twiderex.MR.strings.accessibility_common_logo_mastodon ) ) }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_sign_in_sign_in_with_mastodon)) }, trailing = { IconButton( @@ -120,7 +119,7 @@ fun MastodonSignInScene() { Icon( imageVector = Icons.Default.KeyboardArrowRight, contentDescription = stringResource( - id = com.twidere.common.R.string.scene_sign_in_sign_in_with_mastodon + res = com.twidere.twiderex.MR.strings.scene_sign_in_sign_in_with_mastodon ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonWebSignInScene.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt similarity index 90% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index 8b9a7e92d..d8ba715e0 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -43,12 +43,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.GraphicsLayerScope -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -103,7 +102,7 @@ fun SearchInputScene(initial: String? = null) { }, maxLines = 1, placeholder = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_search_search_bar_placeholder)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_search_search_bar_placeholder)) }, autoFocus = true, alignment = Alignment.CenterStart, @@ -131,9 +130,9 @@ fun SearchInputScene(initial: String? = null) { } ) { Icon( - painter = painterResource(id = R.drawable.ic_search), + painter = painterResource(res = com.twidere.twiderex.MR.files.search), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_search_title + res = com.twidere.twiderex.MR.strings.scene_search_title ) ) } @@ -154,7 +153,7 @@ fun SearchInputScene(initial: String? = null) { Icon( imageVector = Icons.Default.History, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_search_history + res = com.twidere.twiderex.MR.strings.accessibility_scene_search_history ) ) }, @@ -165,9 +164,9 @@ fun SearchInputScene(initial: String? = null) { } ) { Icon( - painter = painterResource(id = R.drawable.ic_x), + painter = painterResource(res = com.twidere.twiderex.MR.files.x), contentDescription = stringResource( - id = com.twidere.common.R.string.common_controls_actions_remove + res = com.twidere.twiderex.MR.strings.common_controls_actions_remove ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt similarity index 96% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt index 8f23595e7..d78b16235 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt @@ -45,15 +45,14 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.pagerTabIndicatorOffset import com.google.accompanist.pager.rememberPagerState -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarDefaults import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -146,9 +145,9 @@ fun SearchScene(keyword: String) { } ) { Icon( - painter = painterResource(id = R.drawable.ic_device_floppy), + painter = painterResource(res = com.twidere.twiderex.MR.files.device_floppy), contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_search_save + res = com.twidere.twiderex.MR.strings.accessibility_scene_search_save ) ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt similarity index 95% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt index 4488af6a2..0ddaf7578 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt @@ -27,7 +27,7 @@ import androidx.compose.material.ListItem import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items @@ -41,7 +41,7 @@ import org.koin.core.parameter.parametersOf class MastodonSearchHashtagItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_search_tabs_hashtag) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_search_tabs_hashtag) } @OptIn(ExperimentalMaterialApi::class) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt index 81f7c4784..a7ad7954b 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout @@ -34,7 +34,7 @@ import org.koin.core.parameter.parametersOf class SearchTweetsItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_search_tabs_tweets) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_search_tabs_tweets) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt index 9641847a2..e3e39906c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout @@ -35,7 +35,7 @@ import org.koin.core.parameter.parametersOf class SearchUserItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_search_tabs_users) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_search_tabs_users) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index b68f8757a..4b69aecb5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout @@ -37,7 +37,7 @@ import org.koin.core.parameter.parametersOf class TwitterSearchMediaItem : SearchSceneItem { @Composable override fun name(): String { - return stringResource(id = com.twidere.common.R.string.scene_search_tabs_media) + return stringResource(res = com.twidere.twiderex.MR.strings.scene_search_tabs_media) } @Composable diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt similarity index 85% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt index 14c025299..2be54cfac 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt @@ -50,11 +50,10 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.BuildConfig -import com.twidere.twiderex.R import com.twidere.twiderex.component.LoginLogo import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -77,7 +76,7 @@ fun AboutScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_about_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_title)) } ) } @@ -102,7 +101,7 @@ private fun AboutContent() { modifier = Modifier .weight(4F) ) { - val grayLogoPainter = painterResource(id = R.drawable.ic_about_gray_logo) + val grayLogoPainter = painterResource(res = com.twidere.twiderex.MR.files.about_gray_logo) val aspectRatio = grayLogoPainter.intrinsicSize.width / grayLogoPainter.intrinsicSize.height ParallaxLayout( @@ -116,8 +115,8 @@ private fun AboutContent() { parallaxLayoutState = parallaxLayoutState, backContent = { BlurImage( - resource = R.drawable.ic_about_gray_logo_shadow, - contentDescription = stringResource(id = com.twidere.common.R.string.scene_settings_about_logo_background_shadow), + resource = com.twidere.twiderex.MR.files.about_gray_logo_shadow, + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_logo_background_shadow), modifier = Modifier .aspectRatio(aspectRatio) .fillMaxHeight() @@ -131,7 +130,7 @@ private fun AboutContent() { ) { Image( painter = grayLogoPainter, - contentDescription = stringResource(id = com.twidere.common.R.string.scene_settings_about_logo_background), + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_logo_background), modifier = Modifier .aspectRatio(aspectRatio) .fillMaxHeight() @@ -155,7 +154,7 @@ private fun AboutContent() { Box(modifier = Modifier.width(AboutContentDefaults.Logo.IconSpacing)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( - text = stringResource(id = R.string.app_name), + text = "Twidere X", style = MaterialTheme.typography.h4, ) } @@ -169,14 +168,14 @@ private fun AboutContent() { ) { Text( text = stringResource( - id = com.twidere.common.R.string.scene_settings_about_version, + res = com.twidere.twiderex.MR.strings.scene_settings_about_version, BuildConfig.VERSION_NAME ), ) Box(modifier = Modifier.height(AboutContentDefaults.VersionName.Spacing)) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( - text = stringResource(id = com.twidere.common.R.string.scene_settings_about_description), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_description), style = MaterialTheme.typography.body2, ) } @@ -197,9 +196,9 @@ private fun AboutContent() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_twitter), + painter = painterResource(res = com.twidere.twiderex.MR.files.twitter), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twitter) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_logo_twitter) ) } Box(modifier = Modifier.width(AboutContentDefaults.Icon.Spacing)) @@ -209,9 +208,9 @@ private fun AboutContent() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_github), + painter = painterResource(res = com.twidere.twiderex.MR.files.github), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_github) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_logo_github) ) } Box(modifier = Modifier.width(AboutContentDefaults.Icon.Spacing)) @@ -221,9 +220,9 @@ private fun AboutContent() { } ) { Icon( - painter = painterResource(id = R.drawable.ic_telegram), + painter = painterResource(res = com.twidere.twiderex.MR.files.telegram), tint = MaterialTheme.colors.onBackground, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_github) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_logo_github) ) } } @@ -240,7 +239,7 @@ private fun AboutContent() { .width(IntrinsicSize.Max) ) { Text( - text = stringResource(id = com.twidere.common.R.string.scene_settings_about_license), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_license), style = MaterialTheme.typography.body1, modifier = Modifier .padding(AboutContentDefaults.License.TextPadding) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt index 872f0f210..f6a7748c2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt @@ -39,8 +39,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -64,7 +63,7 @@ fun AccountManagementScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_manage_accounts_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_manage_accounts_title)) }, actions = { val navController = LocalNavController.current @@ -76,7 +75,7 @@ fun AccountManagementScene() { Icon( imageVector = Icons.Default.Add, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_scene_manage_accounts_add + res = com.twidere.twiderex.MR.strings.accessibility_scene_manage_accounts_add ) ) } @@ -113,7 +112,7 @@ fun AccountManagementScene() { Icon( imageVector = Icons.Default.MoreVert, contentDescription = stringResource( - id = com.twidere.common.R.string.accessibility_common_more + res = com.twidere.twiderex.MR.strings.accessibility_common_more ) ) } @@ -127,7 +126,7 @@ fun AccountManagementScene() { }, ) { Text( - text = stringResource(id = com.twidere.common.R.string.common_controls_actions_remove), + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_remove), color = Color.Red, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt index b9acbc4fb..2275a2803 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt @@ -37,9 +37,8 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.BuildConfig -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.ColoredSwitch @@ -70,7 +69,7 @@ fun AccountNotificationScene( topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_title)) }, navigationIcon = { AppBarNavigationButton() @@ -104,7 +103,7 @@ fun AccountNotificationScene( } val context = LocalContext.current NotificationChannelSpec.values().filter { it.grouped } - .sortedBy { stringResource(id = it.nameRes.resourceId) } + .sortedBy { stringResource(res = it.nameRes) } .forEach { ListItem( modifier = Modifier.clickable( @@ -133,7 +132,7 @@ fun AccountNotificationScene( emptyArray() } ) { - Text(text = stringResource(id = it.nameRes.resourceId)) + Text(text = stringResource(res = it.nameRes)) } }, secondaryText = it.descriptionRes?.let { @@ -145,7 +144,7 @@ fun AccountNotificationScene( emptyArray() } ) { - Text(text = stringResource(id = it.resourceId)) + Text(text = stringResource(res = it)) } } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt similarity index 82% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index aec98f333..11bfdce17 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -53,7 +53,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -85,7 +85,7 @@ fun AppearanceScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_title)) }, ) } @@ -111,7 +111,7 @@ fun AppearanceScene() { } ), text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_highlight_color)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_highlight_color)) }, trailing = { Box( @@ -136,14 +136,14 @@ fun AppearanceScene() { viewModel.setTabPosition(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_tab_position)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_section_header_tab_position)) }, itemContent = { Text( text = stringResource( arrayOf( - com.twidere.common.R.string.scene_settings_appearance_tab_position_top, - com.twidere.common.R.string.scene_settings_appearance_tab_position_bottom + com.twidere.twiderex.MR.strings.scene_settings_appearance_tab_position_top, + com.twidere.twiderex.MR.strings.scene_settings_appearance_tab_position_bottom )[it.ordinal] ) ) @@ -152,7 +152,7 @@ fun AppearanceScene() { ItemDivider() // Scrolling Timeline ItemHeader() { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_scrolling_timeline)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_section_header_scrolling_timeline)) } switchItem( value = appearance.hideTabBarWhenScroll, @@ -160,7 +160,7 @@ fun AppearanceScene() { viewModel.setHideTabBarWhenScrolling(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_tab_bar)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_scrolling_timeline_tab_bar)) }, ) switchItem( @@ -169,7 +169,7 @@ fun AppearanceScene() { viewModel.setHideAppBarWhenScrolling(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_app_bar)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_scrolling_timeline_app_bar)) }, ) switchItem( @@ -178,7 +178,7 @@ fun AppearanceScene() { viewModel.setHideFabWhenScrolling(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_scrolling_timeline_fab)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_scrolling_timeline_fab)) }, ) ItemDivider() @@ -193,15 +193,15 @@ fun AppearanceScene() { viewModel.setTheme(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_section_header_theme)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_section_header_theme)) }, itemContent = { Text( text = stringResource( arrayOf( - com.twidere.common.R.string.scene_settings_appearance_theme_auto, - com.twidere.common.R.string.scene_settings_appearance_theme_light, - com.twidere.common.R.string.scene_settings_appearance_theme_dark, + com.twidere.twiderex.MR.strings.scene_settings_appearance_theme_auto, + com.twidere.twiderex.MR.strings.scene_settings_appearance_theme_light, + com.twidere.twiderex.MR.strings.scene_settings_appearance_theme_dark, )[it.ordinal] ) ) @@ -215,7 +215,7 @@ fun AppearanceScene() { viewModel.setIsDarkModePureBlack(it) }, ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_AMOLED_optimized_mode)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_AMOLED_optimized_mode)) } } } @@ -238,7 +238,7 @@ fun PrimaryColorDialog( AlertDialog( onDismissRequest = onDismiss, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_appearance_pick_color)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_pick_color)) }, text = { Row( @@ -275,7 +275,7 @@ fun PrimaryColorDialog( }, confirmButton = { TextButton(onClick = onDismiss) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } } ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt similarity index 79% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 0ddfc3235..38c534b5a 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -38,7 +38,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.action.FakeStatusActions import com.twidere.twiderex.action.LocalStatusActions @@ -72,7 +72,7 @@ fun DisplayScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_title)) } ) } @@ -84,7 +84,7 @@ fun DisplayScene() { ) ) { ItemHeader() { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_preview)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_section_header_preview)) } CompositionLocalProvider( LocalNavigator provides FakeNavigator, @@ -94,7 +94,7 @@ fun DisplayScene() { } ItemDivider() ItemHeader() { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_text)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_section_header_text)) } switchItem( value = display.useSystemFontSize, @@ -102,7 +102,7 @@ fun DisplayScene() { viewModel.setUseSystemFontSize(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_text_use_the_system_font_size)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_text_use_the_system_font_size)) }, ) if (!display.useSystemFontSize) { @@ -111,7 +111,7 @@ fun DisplayScene() { Icon( modifier = Modifier.size(12.dp), imageVector = Icons.Default.TextFields, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_settings_display_font_size) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_settings_display_font_size) ) }, text = { @@ -129,7 +129,7 @@ fun DisplayScene() { trailing = { Icon( imageVector = Icons.Default.TextFields, - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_scene_settings_display_font_size) + contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_scene_settings_display_font_size) ) } ) @@ -145,14 +145,14 @@ fun DisplayScene() { viewModel.setAvatarStyle(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_text_avatar_style)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_text_avatar_style)) }, itemContent = { Text( text = stringResource( arrayOf( - com.twidere.common.R.string.scene_settings_display_text_circle, - com.twidere.common.R.string.scene_settings_display_text_rounded_square, + com.twidere.twiderex.MR.strings.scene_settings_display_text_circle, + com.twidere.twiderex.MR.strings.scene_settings_display_text_rounded_square, )[it.ordinal] ) ) @@ -160,7 +160,7 @@ fun DisplayScene() { ) ItemDivider() ItemHeader() { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_section_header_media)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_section_header_media)) } switchItem( value = display.urlPreview, @@ -168,7 +168,7 @@ fun DisplayScene() { viewModel.setUrlPreview(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_url_preview)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_url_preview)) } ) switchItem( @@ -177,7 +177,7 @@ fun DisplayScene() { viewModel.setMediaPreview(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_media_media_previews)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_media_media_previews)) } ) if (display.mediaPreview) { @@ -192,15 +192,15 @@ fun DisplayScene() { viewModel.setAutoPlayback(it) }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_display_media_auto_playback)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_media_auto_playback)) }, itemContent = { Text( text = stringResource( arrayOf( - com.twidere.common.R.string.scene_settings_display_media_automatic, - com.twidere.common.R.string.scene_settings_display_media_always, - com.twidere.common.R.string.scene_settings_display_media_off, + com.twidere.twiderex.MR.strings.scene_settings_display_media_automatic, + com.twidere.twiderex.MR.strings.scene_settings_display_media_always, + com.twidere.twiderex.MR.strings.scene_settings_display_media_off, )[it.ordinal] ) ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt similarity index 89% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index f31e5fe73..54d09a2f1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -44,17 +44,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.ReorderableColumn import com.twidere.twiderex.component.foundation.rememberReorderableColumnState import com.twidere.twiderex.component.lazy.ItemHeader +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.HomeMenus @@ -101,7 +100,7 @@ fun LayoutScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_title)) } ) } @@ -126,10 +125,10 @@ fun LayoutScene() { } ListItem( text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_desc_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_desc_title)) }, secondaryText = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_layout_desc_content)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_desc_content)) } ) ReorderableColumn( @@ -169,9 +168,9 @@ private fun LayoutItemContent( ItemHeader { Text( text = if (it) { - stringResource(id = com.twidere.common.R.string.scene_settings_layout_actions_tabbar) + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_actions_tabbar) } else { - stringResource(id = com.twidere.common.R.string.scene_settings_layout_actions_drawer) + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_actions_drawer) } ) } @@ -202,10 +201,10 @@ private fun LayoutItemContent( ) { Image( painter = painterResource( - id = if (visible) { - R.drawable.ic_delete_colored + res = if (visible) { + com.twidere.twiderex.MR.files.delete } else { - R.drawable.ic_add_colored + com.twidere.twiderex.MR.files.add } ), contentDescription = null, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt similarity index 81% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index d7e740011..6b6b167b2 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -52,9 +52,8 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -80,7 +79,7 @@ fun MiscScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_title)) } ) } @@ -154,7 +153,7 @@ fun ColumnScope.ProxyPreference( val proxyPassword by viewModel.proxyPassword.observeAsState("") ItemHeader { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_title)) } switchItem( value = useProxy, @@ -162,21 +161,21 @@ fun ColumnScope.ProxyPreference( viewModel.setUseProxy(it) }, describe = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_enable_description)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_enable_description)) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_enable_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_enable_title)) } ItemProxy( enable = useProxy, - title = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_title), + title = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_title), content = proxyTypeValue(type = proxyType), onClick = { showProxyTypeDialog.value = true } ) - val serverTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_server) + val serverTitle = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_server) ItemProxy( enable = useProxy, title = serverTitle, @@ -191,7 +190,7 @@ fun ColumnScope.ProxyPreference( } ) - val portTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_port_title) + val portTitle = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_port_title) ItemProxy( enable = useProxy, title = portTitle, @@ -210,7 +209,7 @@ fun ColumnScope.ProxyPreference( } ) - val userNameTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_username) + val userNameTitle = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_username) ItemProxy( enable = useProxy, title = userNameTitle, @@ -225,7 +224,7 @@ fun ColumnScope.ProxyPreference( } ) - val passwordTitle = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_password) + val passwordTitle = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_password) ItemProxy( enable = useProxy, title = passwordTitle, @@ -244,9 +243,9 @@ fun ColumnScope.ProxyPreference( @Composable fun proxyTypeValue(type: MiscPreferences.ProxyType): String { return when (type) { - MiscPreferences.ProxyType.HTTP -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_http) - MiscPreferences.ProxyType.REVERSE -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_reverse) - else -> stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_http) + MiscPreferences.ProxyType.HTTP -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_http) + MiscPreferences.ProxyType.REVERSE -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_reverse) + else -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_http) } } @@ -310,13 +309,13 @@ fun NitterPreference(viewModel: MiscViewModel) { } ) { Icon( - painter = painterResource(id = R.drawable.ic_info_circle), + painter = painterResource(res = com.twidere.twiderex.MR.files.info_circle), contentDescription = null, ) } } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_title)) } ListItem( text = { @@ -325,7 +324,7 @@ fun NitterPreference(viewModel: MiscViewModel) { onValueChange = { viewModel.setNitterInstance(it) }, placeholder = { Text( - text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_input_placeholder) + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_placeholder) ) }, trailingIcon = { @@ -335,7 +334,7 @@ fun NitterPreference(viewModel: MiscViewModel) { } ) { Icon( - painter = painterResource(id = R.drawable.ic_info_circle), + painter = painterResource(res = com.twidere.twiderex.MR.files.info_circle), contentDescription = null, ) } @@ -343,7 +342,7 @@ fun NitterPreference(viewModel: MiscViewModel) { ) }, secondaryText = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_input_description)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) } ) } @@ -356,14 +355,14 @@ fun NitterUsageDialog( AlertDialog( onDismissRequest = onDismissRequest, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_title)) }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_content)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_content)) }, confirmButton = { TextButton(onClick = onDismissRequest) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } }, dismissButton = { @@ -375,7 +374,7 @@ fun NitterUsageDialog( ) } ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_usage_project_button)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_project_button)) } } ) @@ -390,16 +389,16 @@ fun NitterInformationDialog( onDismissRequest.invoke() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_information_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_information_title)) }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_nitter_dialog_information_content)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_information_content)) }, confirmButton = { TextButton( onClick = onDismissRequest, ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } } ) @@ -417,7 +416,7 @@ fun ProxyTypeSelectDialog( AlertDialog( onDismissRequest = onDismissRequest, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_misc_proxy_type_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_title)) }, text = { Column { @@ -446,7 +445,7 @@ fun ProxyTypeSelectDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } } ) @@ -493,7 +492,7 @@ fun ProxyInputDialog( onDismissRequest.invoke() } ) { - Text(text = stringResource(id = com.twidere.common.R.string.common_controls_actions_ok)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } } ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt similarity index 93% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index a0d442ccb..fd4836f74 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -36,8 +36,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.ColoredSwitch @@ -66,7 +65,7 @@ fun NotificationScene() { topBar = { AppBar( title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_title)) }, navigationIcon = { AppBarNavigationButton() @@ -83,7 +82,7 @@ fun NotificationScene() { viewModel.setEnabled(!notificationEnabled) }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_notification_switch)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_notification_switch)) }, trailing = { ColoredSwitch( @@ -99,7 +98,7 @@ fun NotificationScene() { ) } ItemHeader { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_notification_accounts)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_accounts)) } val navController = LocalNavController.current LazyColumn { diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt similarity index 70% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt index 1901debe5..a7f16720f 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt @@ -32,9 +32,8 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -53,42 +52,42 @@ data class SettingItem( fun SettingsScene() { val settings = mapOf( - stringResource(id = com.twidere.common.R.string.scene_settings_section_header_general) to listOf( + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_section_header_general) to listOf( SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_appearance_title), - painterResource(id = R.drawable.ic_shirt), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_title), + painterResource(res = com.twidere.twiderex.MR.files.shirt), route = RootRoute.Settings.Appearance, ), SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_display_title), - painterResource(id = R.drawable.ic_template), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_title), + painterResource(res = com.twidere.twiderex.MR.files.template), route = RootRoute.Settings.Display, ), SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_layout_title), - painterResource(id = R.drawable.ic_layout_sidebar), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_title), + painterResource(res = com.twidere.twiderex.MR.files.layout_sidebar), route = RootRoute.Settings.Layout, ), SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_notification_title), - painterResource(id = R.drawable.ic_settings_notification), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_title), + painterResource(res = com.twidere.twiderex.MR.files.settings_notification), route = RootRoute.Settings.Notification, ), SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_storage_title), - painterResource(id = R.drawable.ic_database), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_title), + painterResource(res = com.twidere.twiderex.MR.files.database), route = RootRoute.Settings.Storage, ), SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_misc_title), - painterResource(id = R.drawable.ic_triangle_square_circle), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_title), + painterResource(res = com.twidere.twiderex.MR.files.triangle_square_circle), route = RootRoute.Settings.Misc, ), ), - stringResource(id = com.twidere.common.R.string.scene_settings_section_header_about) to listOf( + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_section_header_about) to listOf( SettingItem( - stringResource(id = com.twidere.common.R.string.scene_settings_about_title), - painterResource(id = R.drawable.ic_info_circle), + stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_title), + painterResource(res = com.twidere.twiderex.MR.files.info_circle), route = RootRoute.Settings.About, ), ) @@ -102,7 +101,7 @@ fun SettingsScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_title)) } ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt similarity index 81% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 147425d24..8b210a142 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -32,7 +32,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.twidere.twiderex.component.foundation.AppBar @@ -69,7 +69,7 @@ fun StorageScene() { AppBarNavigationButton() }, title = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_title)) } ) } @@ -83,7 +83,7 @@ fun StorageScene() { viewModel.clearSearchHistory() }, ) { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_search_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_search_title)) } ListItem( modifier = Modifier @@ -91,10 +91,10 @@ fun StorageScene() { viewModel.clearImageCache() }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_media_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_media_title)) }, secondaryText = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_media_sub_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_media_sub_title)) }, ) ListItem( @@ -103,10 +103,10 @@ fun StorageScene() { viewModel.clearDatabaseCache() }, text = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_all_title), color = Color.Red) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_all_title), color = Color.Red) }, secondaryText = { - Text(text = stringResource(id = com.twidere.common.R.string.scene_settings_storage_all_sub_title)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_all_sub_title)) }, ) } diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt index 44b1302cf..3a44b0d39 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -48,7 +48,7 @@ fun FollowersScene( AppBarNavigationButton() }, title = { - Text(stringResource(id = com.twidere.common.R.string.scene_followers_title)) + Text(stringResource(res = com.twidere.twiderex.MR.strings.scene_followers_title)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt similarity index 92% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt index e0bc41255..718f7a2c4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -48,7 +48,7 @@ fun FollowingScene( AppBarNavigationButton() }, title = { - Text(stringResource(id = com.twidere.common.R.string.scene_following_title)) + Text(stringResource(res = com.twidere.twiderex.MR.strings.scene_following_title)) } ) }, diff --git a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt similarity index 94% rename from android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index 62c281506..6ced912d4 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -25,10 +25,9 @@ import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp -import com.twidere.twiderex.R import com.twidere.twiderex.component.UserComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -84,9 +83,9 @@ fun UserScene( } ) { Icon( - painter = painterResource(id = R.drawable.ic_mail), + painter = painterResource(res = com.twidere.twiderex.MR.files.mail), contentDescription = stringResource( - id = com.twidere.common.R.string.scene_messages_title + res = com.twidere.twiderex.MR.strings.scene_messages_title ), tint = MaterialTheme.colors.onSurface ) diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/ui/Ambient.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Color.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/ui/Color.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/ui/Shape.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/ui/Theme.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt diff --git a/common/src/commonMain/resources/MR/files/svg/Blocked_Badge.svg b/common/src/commonMain/resources/MR/files/svg/Blocked_Badge.svg new file mode 100644 index 000000000..00c352124 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Blocked_Badge.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-1.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-1.svg new file mode 100644 index 000000000..31fbc437b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-2.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-2.svg new file mode 100644 index 000000000..d16f24291 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-3.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-3.svg new file mode 100644 index 000000000..e81cf14d1 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-4.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-4.svg new file mode 100644 index 000000000..ee86d284f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-5.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-5.svg new file mode 100644 index 000000000..509913ec1 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-5.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-6.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-6.svg new file mode 100644 index 000000000..00570a58a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-6.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-7.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-7.svg new file mode 100644 index 000000000..0a1072d3b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-7.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-8.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-8.svg new file mode 100644 index 000000000..560eb8cc4 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-8.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-9.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-9.svg new file mode 100644 index 000000000..abdc4585a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-9.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Drafts-9more.svg b/common/src/commonMain/resources/MR/files/svg/Drafts-9more.svg new file mode 100644 index 000000000..b7ca78b0e --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Drafts-9more.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Mastodon_Badge.svg b/common/src/commonMain/resources/MR/files/svg/Mastodon_Badge.svg new file mode 100644 index 000000000..68a9792b3 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Mastodon_Badge.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/Twitter_Badge.svg b/common/src/commonMain/resources/MR/files/svg/Twitter_Badge.svg new file mode 100644 index 000000000..53341a498 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/Twitter_Badge.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/add.svg b/common/src/commonMain/resources/MR/files/svg/add.svg new file mode 100644 index 000000000..998a1efb9 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/add.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/adjustments-horizontal.svg b/common/src/commonMain/resources/MR/files/svg/adjustments-horizontal.svg new file mode 100644 index 000000000..a9334471b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/adjustments-horizontal.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/alarm.svg b/common/src/commonMain/resources/MR/files/svg/alarm.svg new file mode 100644 index 000000000..498bb2179 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/alarm.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/alert-circle-warn.svg b/common/src/commonMain/resources/MR/files/svg/alert-circle-warn.svg new file mode 100644 index 000000000..8061c781d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/alert-circle-warn.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/alert-circle.svg b/common/src/commonMain/resources/MR/files/svg/alert-circle.svg new file mode 100644 index 000000000..4875a3422 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/alert-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/alert-octagon.svg b/common/src/commonMain/resources/MR/files/svg/alert-octagon.svg new file mode 100644 index 000000000..58a15a7ed --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/alert-octagon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/alert-triangle.svg b/common/src/commonMain/resources/MR/files/svg/alert-triangle.svg new file mode 100644 index 000000000..c35bdec61 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/alert-triangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/align-center.svg b/common/src/commonMain/resources/MR/files/svg/align-center.svg new file mode 100644 index 000000000..85768cf0e --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/align-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/align-justified.svg b/common/src/commonMain/resources/MR/files/svg/align-justified.svg new file mode 100644 index 000000000..cb372e12d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/align-justified.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/align-left.svg b/common/src/commonMain/resources/MR/files/svg/align-left.svg new file mode 100644 index 000000000..8564c201b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/align-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/align-right.svg b/common/src/commonMain/resources/MR/files/svg/align-right.svg new file mode 100644 index 000000000..32cf80825 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/align-right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/aperture.svg b/common/src/commonMain/resources/MR/files/svg/aperture.svg new file mode 100644 index 000000000..d09a8b14c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/aperture.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-back-up.svg b/common/src/commonMain/resources/MR/files/svg/arrow-back-up.svg new file mode 100644 index 000000000..cf08ac9bb --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-back-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-down-left.svg b/common/src/commonMain/resources/MR/files/svg/arrow-down-left.svg new file mode 100644 index 000000000..0d1acd566 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-down-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-down-right.svg b/common/src/commonMain/resources/MR/files/svg/arrow-down-right.svg new file mode 100644 index 000000000..b80f06b5a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-down-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-down.svg b/common/src/commonMain/resources/MR/files/svg/arrow-down.svg new file mode 100644 index 000000000..d924ab3dd --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-down.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-left.svg b/common/src/commonMain/resources/MR/files/svg/arrow-left.svg new file mode 100644 index 000000000..081b9b82b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-right.svg b/common/src/commonMain/resources/MR/files/svg/arrow-right.svg new file mode 100644 index 000000000..03f80b044 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-up-left.svg b/common/src/commonMain/resources/MR/files/svg/arrow-up-left.svg new file mode 100644 index 000000000..8b3cd20d0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-up-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-up-right.svg b/common/src/commonMain/resources/MR/files/svg/arrow-up-right.svg new file mode 100644 index 000000000..27d572b4d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-up-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/arrow-up.svg b/common/src/commonMain/resources/MR/files/svg/arrow-up.svg new file mode 100644 index 000000000..b0792016c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/at-sign.svg b/common/src/commonMain/resources/MR/files/svg/at-sign.svg new file mode 100644 index 000000000..ee2e14e79 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/at-sign.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/ban.svg b/common/src/commonMain/resources/MR/files/svg/ban.svg new file mode 100644 index 000000000..0b8fcba3d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ban.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bell-off.svg b/common/src/commonMain/resources/MR/files/svg/bell-off.svg new file mode 100644 index 000000000..26499366f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bell-off.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bell-ringing-2_1.svg b/common/src/commonMain/resources/MR/files/svg/bell-ringing-2_1.svg new file mode 100644 index 000000000..09dc6c069 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bell-ringing-2_1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bell-ringing.svg b/common/src/commonMain/resources/MR/files/svg/bell-ringing.svg new file mode 100644 index 000000000..bf0c8d159 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bell-ringing.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bell.svg b/common/src/commonMain/resources/MR/files/svg/bell.svg new file mode 100644 index 000000000..42c7c1339 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bell.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/blockquote-16px.svg b/common/src/commonMain/resources/MR/files/svg/blockquote-16px.svg new file mode 100644 index 000000000..992785789 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/blockquote-16px.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/blockquote.svg b/common/src/commonMain/resources/MR/files/svg/blockquote.svg new file mode 100644 index 000000000..824878272 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/blockquote.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bookmark-off.svg b/common/src/commonMain/resources/MR/files/svg/bookmark-off.svg new file mode 100644 index 000000000..129f453bc --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bookmark-off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bookmark.svg b/common/src/commonMain/resources/MR/files/svg/bookmark.svg new file mode 100644 index 000000000..cc7cba78d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/bookmarks.svg b/common/src/commonMain/resources/MR/files/svg/bookmarks.svg new file mode 100644 index 000000000..3f0f9c222 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/bookmarks.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/browser.svg b/common/src/commonMain/resources/MR/files/svg/browser.svg new file mode 100644 index 000000000..e9ca69da6 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/browser.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/camera.svg b/common/src/commonMain/resources/MR/files/svg/camera.svg new file mode 100644 index 000000000..a855cdeae --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/camera.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/check.svg b/common/src/commonMain/resources/MR/files/svg/check.svg new file mode 100644 index 000000000..3227c3c52 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/checkbox.svg b/common/src/commonMain/resources/MR/files/svg/checkbox.svg new file mode 100644 index 000000000..3cb73dca2 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/checkbox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/checks.svg b/common/src/commonMain/resources/MR/files/svg/checks.svg new file mode 100644 index 000000000..258672ab4 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/checks.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/circle-check.svg b/common/src/commonMain/resources/MR/files/svg/circle-check.svg new file mode 100644 index 000000000..a4bac6d5b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/circle-check.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/circle-x_1.svg b/common/src/commonMain/resources/MR/files/svg/circle-x_1.svg new file mode 100644 index 000000000..950f56146 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/circle-x_1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/circle.svg b/common/src/commonMain/resources/MR/files/svg/circle.svg new file mode 100644 index 000000000..0029934a4 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/clock.svg b/common/src/commonMain/resources/MR/files/svg/clock.svg new file mode 100644 index 000000000..e36a9e854 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-down-left.svg b/common/src/commonMain/resources/MR/files/svg/corner-down-left.svg new file mode 100644 index 000000000..700642003 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-down-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-down-right.svg b/common/src/commonMain/resources/MR/files/svg/corner-down-right.svg new file mode 100644 index 000000000..71683e50f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-down-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-left-down.svg b/common/src/commonMain/resources/MR/files/svg/corner-left-down.svg new file mode 100644 index 000000000..974840cfa --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-left-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-left-up.svg b/common/src/commonMain/resources/MR/files/svg/corner-left-up.svg new file mode 100644 index 000000000..b359839b0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-left-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-right-down.svg b/common/src/commonMain/resources/MR/files/svg/corner-right-down.svg new file mode 100644 index 000000000..a3f884e78 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-right-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-right-up.svg b/common/src/commonMain/resources/MR/files/svg/corner-right-up.svg new file mode 100644 index 000000000..c6aa613d5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-right-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-up-left-16px.svg b/common/src/commonMain/resources/MR/files/svg/corner-up-left-16px.svg new file mode 100644 index 000000000..73071d764 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-up-left-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-up-left.svg b/common/src/commonMain/resources/MR/files/svg/corner-up-left.svg new file mode 100644 index 000000000..77449facf --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-up-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/corner-up-right.svg b/common/src/commonMain/resources/MR/files/svg/corner-up-right.svg new file mode 100644 index 000000000..4d1e45ed9 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/corner-up-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/database.svg b/common/src/commonMain/resources/MR/files/svg/database.svg new file mode 100644 index 000000000..68a82573b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/database.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/delete.svg b/common/src/commonMain/resources/MR/files/svg/delete.svg new file mode 100644 index 000000000..b0d3dae37 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/device-floppy.svg b/common/src/commonMain/resources/MR/files/svg/device-floppy.svg new file mode 100644 index 000000000..b1fc2f990 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/device-floppy.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-16px.svg b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-16px.svg new file mode 100644 index 000000000..0e3703c82 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-16px.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-fill-16px.svg b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-fill-16px.svg new file mode 100644 index 000000000..af93d2805 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal-fill-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal.svg b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal.svg new file mode 100644 index 000000000..4b7928a44 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/dots-circle-horizontal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/dots-vertical.svg b/common/src/commonMain/resources/MR/files/svg/dots-vertical.svg new file mode 100644 index 000000000..f734db7a2 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/dots-vertical.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/dots.svg b/common/src/commonMain/resources/MR/files/svg/dots.svg new file mode 100644 index 000000000..35e6292a4 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/dots.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/download.svg b/common/src/commonMain/resources/MR/files/svg/download.svg new file mode 100644 index 000000000..68701c04e --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/download.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/external-link.svg b/common/src/commonMain/resources/MR/files/svg/external-link.svg new file mode 100644 index 000000000..cf21bb2c5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/external-link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/eye-off-1.svg b/common/src/commonMain/resources/MR/files/svg/eye-off-1.svg new file mode 100644 index 000000000..92b077ecb --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/eye-off-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/eye-off.svg b/common/src/commonMain/resources/MR/files/svg/eye-off.svg new file mode 100644 index 000000000..c308f624c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/eye-off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/eye.svg b/common/src/commonMain/resources/MR/files/svg/eye.svg new file mode 100644 index 000000000..904ecae7b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/eye.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/filter.svg b/common/src/commonMain/resources/MR/files/svg/filter.svg new file mode 100644 index 000000000..96851d370 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/flame.svg b/common/src/commonMain/resources/MR/files/svg/flame.svg new file mode 100644 index 000000000..f86f3568b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/flame.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/float-center.svg b/common/src/commonMain/resources/MR/files/svg/float-center.svg new file mode 100644 index 000000000..62267c133 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/float-center.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/float-left.svg b/common/src/commonMain/resources/MR/files/svg/float-left.svg new file mode 100644 index 000000000..2a234ffe7 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/float-left.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/float-none.svg b/common/src/commonMain/resources/MR/files/svg/float-none.svg new file mode 100644 index 000000000..72e1daa02 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/float-none.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/float-right.svg b/common/src/commonMain/resources/MR/files/svg/float-right.svg new file mode 100644 index 000000000..1f9dca642 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/float-right.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/gif.svg b/common/src/commonMain/resources/MR/files/svg/gif.svg new file mode 100644 index 000000000..c6d8a72e3 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/gif.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/globe-16px.svg b/common/src/commonMain/resources/MR/files/svg/globe-16px.svg new file mode 100644 index 000000000..5d25bf4c6 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/globe-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/globe.svg b/common/src/commonMain/resources/MR/files/svg/globe.svg new file mode 100644 index 000000000..fe5925985 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/globe.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/hash.svg b/common/src/commonMain/resources/MR/files/svg/hash.svg new file mode 100644 index 000000000..7b79e0821 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/hash.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/heart-16px.svg b/common/src/commonMain/resources/MR/files/svg/heart-16px.svg new file mode 100644 index 000000000..678ef9af1 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/heart-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/heart-fill-16px.svg b/common/src/commonMain/resources/MR/files/svg/heart-fill-16px.svg new file mode 100644 index 000000000..9a49f4bd7 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/heart-fill-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/heart-fill.svg b/common/src/commonMain/resources/MR/files/svg/heart-fill.svg new file mode 100644 index 000000000..778b64a9b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/heart-fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/heart.svg b/common/src/commonMain/resources/MR/files/svg/heart.svg new file mode 100644 index 000000000..66137fec4 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/history.svg b/common/src/commonMain/resources/MR/files/svg/history.svg new file mode 100644 index 000000000..9acfe8b1b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/history.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/home.svg b/common/src/commonMain/resources/MR/files/svg/home.svg new file mode 100644 index 000000000..0007d9f12 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/home.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/info-circle.svg b/common/src/commonMain/resources/MR/files/svg/info-circle.svg new file mode 100644 index 000000000..2e41c083f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/info-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/keyboard.svg b/common/src/commonMain/resources/MR/files/svg/keyboard.svg new file mode 100644 index 000000000..4241fd6cb --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/keyboard.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layers-intersect.svg b/common/src/commonMain/resources/MR/files/svg/layers-intersect.svg new file mode 100644 index 000000000..4ee6df92d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layers-intersect.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layout-2.svg b/common/src/commonMain/resources/MR/files/svg/layout-2.svg new file mode 100644 index 000000000..ac990363c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layout-2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layout-bottombar.svg b/common/src/commonMain/resources/MR/files/svg/layout-bottombar.svg new file mode 100644 index 000000000..598879a01 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layout-bottombar.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layout-list.svg b/common/src/commonMain/resources/MR/files/svg/layout-list.svg new file mode 100644 index 000000000..d26902e66 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layout-list.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layout-sidebar.svg b/common/src/commonMain/resources/MR/files/svg/layout-sidebar.svg new file mode 100644 index 000000000..7e2fc84cf --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layout-sidebar.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/layout.svg b/common/src/commonMain/resources/MR/files/svg/layout.svg new file mode 100644 index 000000000..2073d4ffb --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/layout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/link.svg b/common/src/commonMain/resources/MR/files/svg/link.svg new file mode 100644 index 000000000..0e2764172 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/link.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/list.svg b/common/src/commonMain/resources/MR/files/svg/list.svg new file mode 100644 index 000000000..4962f4ff5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/list.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/lock-16px.svg b/common/src/commonMain/resources/MR/files/svg/lock-16px.svg new file mode 100644 index 000000000..3e2b44f29 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/lock-16px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/lock-open-16px.svg b/common/src/commonMain/resources/MR/files/svg/lock-open-16px.svg new file mode 100644 index 000000000..8c42287bb --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/lock-open-16px.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/lock-open.svg b/common/src/commonMain/resources/MR/files/svg/lock-open.svg new file mode 100644 index 000000000..2c63d4261 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/lock-open.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/lock.svg b/common/src/commonMain/resources/MR/files/svg/lock.svg new file mode 100644 index 000000000..b0ed00cd0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/lock_and_repeat-16px.svg b/common/src/commonMain/resources/MR/files/svg/lock_and_repeat-16px.svg new file mode 100644 index 000000000..8e616a4b9 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/lock_and_repeat-16px.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/login.svg b/common/src/commonMain/resources/MR/files/svg/login.svg new file mode 100644 index 000000000..2decd2719 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/login.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/logout.svg b/common/src/commonMain/resources/MR/files/svg/logout.svg new file mode 100644 index 000000000..deaf81c10 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/logout.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mail-16px.svg b/common/src/commonMain/resources/MR/files/svg/mail-16px.svg new file mode 100644 index 000000000..2763a8357 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mail-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mail-plus.svg b/common/src/commonMain/resources/MR/files/svg/mail-plus.svg new file mode 100644 index 000000000..9e28b74a7 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mail-plus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mail.svg b/common/src/commonMain/resources/MR/files/svg/mail.svg new file mode 100644 index 000000000..91232d74f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/map-pin-16px.svg b/common/src/commonMain/resources/MR/files/svg/map-pin-16px.svg new file mode 100644 index 000000000..666ae1300 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/map-pin-16px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/map-pin.svg b/common/src/commonMain/resources/MR/files/svg/map-pin.svg new file mode 100644 index 000000000..d0e127923 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/map-pin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/message-2-16px.svg b/common/src/commonMain/resources/MR/files/svg/message-2-16px.svg new file mode 100644 index 000000000..9929b0ec0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/message-2-16px.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/message-circle-16px.svg b/common/src/commonMain/resources/MR/files/svg/message-circle-16px.svg new file mode 100644 index 000000000..8bba0267d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/message-circle-16px.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/message-circle-add.svg b/common/src/commonMain/resources/MR/files/svg/message-circle-add.svg new file mode 100644 index 000000000..341e3affd --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/message-circle-add.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/message-circle.svg b/common/src/commonMain/resources/MR/files/svg/message-circle.svg new file mode 100644 index 000000000..67dde60ca --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/message-circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/microphone.svg b/common/src/commonMain/resources/MR/files/svg/microphone.svg new file mode 100644 index 000000000..87d43ae17 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/microphone.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mood-neutral.svg b/common/src/commonMain/resources/MR/files/svg/mood-neutral.svg new file mode 100644 index 000000000..66a8010ea --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mood-neutral.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mood-sad.svg b/common/src/commonMain/resources/MR/files/svg/mood-sad.svg new file mode 100644 index 000000000..f19eb2bce --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mood-sad.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/mood-smile.svg b/common/src/commonMain/resources/MR/files/svg/mood-smile.svg new file mode 100644 index 000000000..5fadd52c3 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/mood-smile.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/note.svg b/common/src/commonMain/resources/MR/files/svg/note.svg new file mode 100644 index 000000000..562375885 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/note.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/notification.svg b/common/src/commonMain/resources/MR/files/svg/notification.svg new file mode 100644 index 000000000..72f12969a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/notification.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/open-source.svg b/common/src/commonMain/resources/MR/files/svg/open-source.svg new file mode 100644 index 000000000..05ad33627 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/open-source.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/paperclip.svg b/common/src/commonMain/resources/MR/files/svg/paperclip.svg new file mode 100644 index 000000000..2296c82f5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/paperclip.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pencil.svg b/common/src/commonMain/resources/MR/files/svg/pencil.svg new file mode 100644 index 000000000..15c2c8525 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pencil.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/photo.svg b/common/src/commonMain/resources/MR/files/svg/photo.svg new file mode 100644 index 000000000..dcf75621f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/photo.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/photos.svg b/common/src/commonMain/resources/MR/files/svg/photos.svg new file mode 100644 index 000000000..90644c269 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/photos.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pin-1.svg b/common/src/commonMain/resources/MR/files/svg/pin-1.svg new file mode 100644 index 000000000..f612add92 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pin-1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pin.svg b/common/src/commonMain/resources/MR/files/svg/pin.svg new file mode 100644 index 000000000..f612add92 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pinned-16px.svg b/common/src/commonMain/resources/MR/files/svg/pinned-16px.svg new file mode 100644 index 000000000..523b8e5e1 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pinned-16px.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pinned-off.svg b/common/src/commonMain/resources/MR/files/svg/pinned-off.svg new file mode 100644 index 000000000..85bc5c04a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pinned-off.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/pinned.svg b/common/src/commonMain/resources/MR/files/svg/pinned.svg new file mode 100644 index 000000000..9d9d00403 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/pinned.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/planet.svg b/common/src/commonMain/resources/MR/files/svg/planet.svg new file mode 100644 index 000000000..5451b5654 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/planet.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/player-pause.svg b/common/src/commonMain/resources/MR/files/svg/player-pause.svg new file mode 100644 index 000000000..0426169b3 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/player-pause.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/player-play.svg b/common/src/commonMain/resources/MR/files/svg/player-play.svg new file mode 100644 index 000000000..0100fa63b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/player-play.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/plus.svg b/common/src/commonMain/resources/MR/files/svg/plus.svg new file mode 100644 index 000000000..161e9133d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/plus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/poll.svg b/common/src/commonMain/resources/MR/files/svg/poll.svg new file mode 100644 index 000000000..bd035dff8 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/poll.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/qrcode.svg b/common/src/commonMain/resources/MR/files/svg/qrcode.svg new file mode 100644 index 000000000..cddede546 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/qrcode.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/refresh-alert.svg b/common/src/commonMain/resources/MR/files/svg/refresh-alert.svg new file mode 100644 index 000000000..0c9cc99ab --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/refresh-alert.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/refresh.svg b/common/src/commonMain/resources/MR/files/svg/refresh.svg new file mode 100644 index 000000000..ad63dea18 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/refresh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/repeat-12px.svg b/common/src/commonMain/resources/MR/files/svg/repeat-12px.svg new file mode 100644 index 000000000..61c492fd5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/repeat-12px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/repeat-16px.svg b/common/src/commonMain/resources/MR/files/svg/repeat-16px.svg new file mode 100644 index 000000000..d8018b90c --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/repeat-16px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/repeat.svg b/common/src/commonMain/resources/MR/files/svg/repeat.svg new file mode 100644 index 000000000..d90b3ef84 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/repeat.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/search.svg b/common/src/commonMain/resources/MR/files/svg/search.svg new file mode 100644 index 000000000..5d00af163 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/select-1.svg b/common/src/commonMain/resources/MR/files/svg/select-1.svg new file mode 100644 index 000000000..a26323056 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/select-1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/select.svg b/common/src/commonMain/resources/MR/files/svg/select.svg new file mode 100644 index 000000000..a26323056 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/select.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/selector.svg b/common/src/commonMain/resources/MR/files/svg/selector.svg new file mode 100644 index 000000000..579305930 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/selector.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/send-add.svg b/common/src/commonMain/resources/MR/files/svg/send-add.svg new file mode 100644 index 000000000..4b0169500 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/send-add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/send.svg b/common/src/commonMain/resources/MR/files/svg/send.svg new file mode 100644 index 000000000..bfeeb2efd --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/settings.svg b/common/src/commonMain/resources/MR/files/svg/settings.svg new file mode 100644 index 000000000..a0c582f07 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/share-16px.svg b/common/src/commonMain/resources/MR/files/svg/share-16px.svg new file mode 100644 index 000000000..4efecb030 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/share-16px.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/share-iOS-16px.svg b/common/src/commonMain/resources/MR/files/svg/share-iOS-16px.svg new file mode 100644 index 000000000..abe0a1b4f --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/share-iOS-16px.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/share-iOS.svg b/common/src/commonMain/resources/MR/files/svg/share-iOS.svg new file mode 100644 index 000000000..85f41aa37 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/share-iOS.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/share.svg b/common/src/commonMain/resources/MR/files/svg/share.svg new file mode 100644 index 000000000..2e1a500ef --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/share.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/shield-off.svg b/common/src/commonMain/resources/MR/files/svg/shield-off.svg new file mode 100644 index 000000000..7a8e32358 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/shield-off.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/shield.svg b/common/src/commonMain/resources/MR/files/svg/shield.svg new file mode 100644 index 000000000..a8eb92253 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/shirt.svg b/common/src/commonMain/resources/MR/files/svg/shirt.svg new file mode 100644 index 000000000..16cc1b62a --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/shirt.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/square-check.svg b/common/src/commonMain/resources/MR/files/svg/square-check.svg new file mode 100644 index 000000000..28582f674 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/square-check.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/square.svg b/common/src/commonMain/resources/MR/files/svg/square.svg new file mode 100644 index 000000000..28de5d43b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/square.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/template.svg b/common/src/commonMain/resources/MR/files/svg/template.svg new file mode 100644 index 000000000..5ca25b154 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/template.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/trash.svg b/common/src/commonMain/resources/MR/files/svg/trash.svg new file mode 100644 index 000000000..de127ccb0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/common/src/commonMain/resources/MR/files/svg/trending-up.svg b/common/src/commonMain/resources/MR/files/svg/trending-up.svg new file mode 100644 index 000000000..25e155d65 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/trending-up.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/triangle-square-circle.svg b/common/src/commonMain/resources/MR/files/svg/triangle-square-circle.svg new file mode 100644 index 000000000..d280ba5ea --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/triangle-square-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/upload.svg b/common/src/commonMain/resources/MR/files/svg/upload.svg new file mode 100644 index 000000000..6ec183db6 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/upload.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/user-check_1.svg b/common/src/commonMain/resources/MR/files/svg/user-check_1.svg new file mode 100644 index 000000000..ef3ba4046 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/user-check_1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/user-exclamation_1.svg b/common/src/commonMain/resources/MR/files/svg/user-exclamation_1.svg new file mode 100644 index 000000000..4cd1da0ae --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/user-exclamation_1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/user-plus.svg b/common/src/commonMain/resources/MR/files/svg/user-plus.svg new file mode 100644 index 000000000..dc5a7bfb7 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/user-plus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/user-x.svg b/common/src/commonMain/resources/MR/files/svg/user-x.svg new file mode 100644 index 000000000..106cc0b00 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/user-x.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/user.svg b/common/src/commonMain/resources/MR/files/svg/user.svg new file mode 100644 index 000000000..1b3762c7d --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/users.svg b/common/src/commonMain/resources/MR/files/svg/users.svg new file mode 100644 index 000000000..1a1c88202 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/users.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/video-off.svg b/common/src/commonMain/resources/MR/files/svg/video-off.svg new file mode 100644 index 000000000..1509bcc4b --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/video-off.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/video.svg b/common/src/commonMain/resources/MR/files/svg/video.svg new file mode 100644 index 000000000..a04029ee0 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/volume-2.svg b/common/src/commonMain/resources/MR/files/svg/volume-2.svg new file mode 100644 index 000000000..4576d7ea5 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/volume-2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/volume-3.svg b/common/src/commonMain/resources/MR/files/svg/volume-3.svg new file mode 100644 index 000000000..4ea84aac6 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/volume-3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/volume.svg b/common/src/commonMain/resources/MR/files/svg/volume.svg new file mode 100644 index 000000000..874035469 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/volume.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/x.svg b/common/src/commonMain/resources/MR/files/svg/x.svg new file mode 100644 index 000000000..433d4af28 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/x.svg @@ -0,0 +1,3 @@ + + + From 1a2598099dca1cb6c4170ab1b89e03251e93a738 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Sep 2021 11:27:11 +0800 Subject: [PATCH 260/615] add sqlDelight table for UiList/NotificationCursor and add tests --- .../sqldelight/adapter/ListAdapterFactory.kt | 32 +++++ .../NotificationCursorAdapterFactory.kt | 33 +++++ .../sqldelight/dao/SqlDelightListsDaoImpl.kt | 86 ++++++++++++ .../SqlDelightNotificationCursorDaoImpl.kt | 38 ++++++ .../twiderex/db/sqldelight/database.kt | 6 +- .../db/sqldelight/transform/ListTransform.kt | 50 +++++++ .../transform/NotificationCursorTransform.kt | 40 ++++++ .../twidere/twiderex/sqldelight/table/List.sq | 38 ++++++ .../sqldelight/table/NotificationCursor.sq | 18 +++ .../db/dao/SqlDelightListsDaoImplTest.kt | 124 ++++++++++++++++++ .../SqlDelightNotificationCursorImplTest.kt | 49 +++++++ .../db/sqldelight/ListQueriesImplTest.kt | 107 +++++++++++++++ .../NotificationCursorQueriesImplTest.kt | 61 +++++++++ .../sqldelight/transform/ListTransformTest.kt | 63 +++++++++ .../NotificationCursorTransformTest.kt | 55 ++++++++ 15 files changed, 799 insertions(+), 1 deletion(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq create mode 100644 common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt create mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt new file mode 100644 index 000000000..5388dc79a --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt @@ -0,0 +1,32 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.twidere.twiderex.sqldelight.table.List + +object ListAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + List.Adapter( + accountKeyAdapter = it, + listKeyAdapter = it, + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt new file mode 100644 index 000000000..5dcbcc963 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt @@ -0,0 +1,33 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.adapter + +import com.squareup.sqldelight.EnumColumnAdapter +import com.twidere.twiderex.sqldelight.table.DbNotificationCursor + +object NotificationCursorAdapterFactory { + fun create() = MicroBlogKeyColumnAdapter().let { + DbNotificationCursor.Adapter( + accountKeyAdapter = it, + typeAdapter = EnumColumnAdapter() + ) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt new file mode 100644 index 000000000..297516586 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt @@ -0,0 +1,86 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.dao + +import androidx.paging.PagingSource +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull +import com.twidere.twiderex.db.dao.ListsDao +import com.twidere.twiderex.db.sqldelight.paging.QueryPagingSource +import com.twidere.twiderex.db.sqldelight.query.flatMap +import com.twidere.twiderex.db.sqldelight.transform.toDbList +import com.twidere.twiderex.db.sqldelight.transform.toUi +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.sqldelight.table.ListQueries +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +internal class SqlDelightListsDaoImpl(private val listQueries: ListQueries) : ListsDao { + override fun getPagingSource(accountKey: MicroBlogKey): PagingSource { + return QueryPagingSource( + countQuery = listQueries.getPagingCount(accountKey = accountKey), + transacter = listQueries, + queryProvider = { limit, offset -> + listQueries.getPagingList(accountKey = accountKey, limit = limit, offSet = offset) + .flatMap { it.toUi() } + } + ) + } + + override fun findWithListKeyWithFlow( + listKey: MicroBlogKey, + accountKey: MicroBlogKey + ): Flow { + return listQueries.findWithListKey(listKey = listKey, accountKey = accountKey) + .asFlow() + .mapToOneOrNull() + .map { it?.toUi() } + } + + override suspend fun insertAll(listOf: List) { + listQueries.transaction { + listOf.forEach { listQueries.insert(it.toDbList()) } + } + } + + override suspend fun findWithListKey(listKey: MicroBlogKey, accountKey: MicroBlogKey): UiList? { + return listQueries.findWithListKey(listKey = listKey, accountKey = accountKey) + .executeAsOneOrNull() + ?.toUi() + } + + override suspend fun update(listOf: List) { + listQueries.transaction { + listOf.forEach { listQueries.insert(it.toDbList()) } + } + } + + override suspend fun delete(listOf: List) { + listQueries.transaction { + listOf.forEach { listQueries.delete(accountKey = it.accountKey, listKey = it.listKey) } + } + } + + override suspend fun clearAll(accountKey: MicroBlogKey) { + listQueries.clearAll(accountKey = accountKey) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt new file mode 100644 index 000000000..88427fc0f --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt @@ -0,0 +1,38 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.dao + +import com.twidere.twiderex.db.dao.NotificationCursorDao +import com.twidere.twiderex.db.sqldelight.transform.toDb +import com.twidere.twiderex.db.sqldelight.transform.toUi +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import com.twidere.twiderex.sqldelight.table.NotificationCursorQueries + +internal class SqlDelightNotificationCursorDaoImpl(private val notificationCursorQueries: NotificationCursorQueries) : NotificationCursorDao { + override suspend fun find( + accountKey: MicroBlogKey, + type: NotificationCursorType + ) = notificationCursorQueries.find(accountKey = accountKey, type = type).executeAsOneOrNull()?.toUi() + + override suspend fun add(notificationCursor: NotificationCursor) = notificationCursorQueries.insert(notificationCursor.toDb()) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt index 01ec115db..f0143f219 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -24,7 +24,9 @@ import com.squareup.sqldelight.db.SqlDriver import com.twidere.twiderex.db.sqldelight.adapter.DMConversationAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.DMEventAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.DraftAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.ListAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.MediaAdapterFactory +import com.twidere.twiderex.db.sqldelight.adapter.NotificationCursorAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.SearchAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.TrendAdapterFactory import com.twidere.twiderex.db.sqldelight.adapter.TrendHistoryAdapterFactory @@ -52,6 +54,8 @@ internal fun createCacheDataBase(driver: SqlDriver): SqlDelightCacheDatabase { UserAdapter = UserAdapterFactory.create(), DMConversationAdapter = DMConversationAdapterFactory.create(), TrendAdapter = TrendAdapterFactory.create(), - TrendHistoryAdapter = TrendHistoryAdapterFactory.create() + TrendHistoryAdapter = TrendHistoryAdapterFactory.create(), + DbNotificationCursorAdapter = NotificationCursorAdapterFactory.create(), + ListAdapter = ListAdapterFactory.create() ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt new file mode 100644 index 000000000..8f1eda302 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt @@ -0,0 +1,50 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.sqldelight.table.List + +internal fun UiList.toDbList() = List( + listId = id, + ownerId = ownerId, + title = title, + descriptions = descriptions, + mode = mode, + replyPolicy = replyPolicy, + accountKey = accountKey, + listKey = listKey, + isFollowed = isFollowed, + allowToSubscribe = allowToSubscribe +) + +internal fun List.toUi() = UiList( + id = listId, + ownerId = ownerId, + title = title, + descriptions = descriptions, + mode = mode, + replyPolicy = replyPolicy, + accountKey = accountKey, + listKey = listKey, + isFollowed = isFollowed, + allowToSubscribe = allowToSubscribe +) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt new file mode 100644 index 000000000..a3869e497 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt @@ -0,0 +1,40 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.paging.NotificationCursor +import com.twidere.twiderex.sqldelight.table.DbNotificationCursor + +internal fun NotificationCursor.toDb() = DbNotificationCursor( + id = _id, + accountKey = accountKey, + type = type, + value = value, + timestamp = timestamp +) + +internal fun DbNotificationCursor.toUi() = NotificationCursor( + _id = id, + accountKey = accountKey, + type = type, + value = value, + timestamp = timestamp +) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq new file mode 100644 index 000000000..95bf8824b --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq @@ -0,0 +1,38 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import java.lang.Boolean; + +CREATE TABLE List ( + listId TEXT NOT NULL, + ownerId TEXT NOT NULL, + title TEXT NOT NULL, + descriptions TEXT NOT NULL, + mode TEXT NOT NULL, + replyPolicy TEXT NOT NULL, + accountKey TEXT AS MicroBlogKey NOT NULL, + listKey TEXT AS MicroBlogKey NOT NULL, + isFollowed INTEGER AS Boolean NOT NULL, + allowToSubscribe INTEGER AS Boolean NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS index_listKey_accountKey ON List (listKey, accountKey); + +insert: +INSERT OR REPLACE INTO List( + listId, listKey, title, ownerId, descriptions, mode, + replyPolicy, accountKey, listKey, isFollowed, allowToSubscribe +) VALUES ?; + +findWithListKey: +SELECT * FROM List WHERE accountKey == :accountKey AND listKey == :listKey; + +getPagingList: +SELECT * FROM List WHERE accountKey == :accountKey LIMIT :limit OFFSET :offSet; + +getPagingCount: +SELECT COUNT(*) FROM List WHERE accountKey == :accountKey; + +delete: +DELETE FROM List WHERE accountKey == :accountKey AND listKey == :listKey; + +clearAll: +DELETE FROM List WHERE accountKey == :accountKey; \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq new file mode 100644 index 000000000..d0c1acfc9 --- /dev/null +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq @@ -0,0 +1,18 @@ +import com.twidere.twiderex.model.MicroBlogKey; +import com.twidere.twiderex.model.enums.NotificationCursorType; + +CREATE TABLE DbNotificationCursor ( + id TEXT NOT NULL PRIMARY KEY, + accountKey TEXT AS MicroBlogKey NOT NULL, + type TEXT AS NotificationCursorType NOT NULL, + value TEXT NOT NULL, + timestamp INTEGER NOT NULL +); + +insert: +INSERT OR REPLACE INTO DbNotificationCursor( + id, accountKey, type, value, timestamp +) VALUES ?; + +find: +SELECT * FROM DbNotificationCursor WHERE accountKey == :accountKey AND type == :type; \ No newline at end of file diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt new file mode 100644 index 000000000..de1fb1340 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt @@ -0,0 +1,124 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import androidx.paging.PagingSource +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightListsDaoImpl +import com.twidere.twiderex.mock.model.mockIListModel +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals + +internal class SqlDelightListsDaoImplTest : BaseCacheDatabaseTest() { + private lateinit var dao: SqlDelightListsDaoImpl + private val accountKey = MicroBlogKey.twitter("account") + override fun setUp() { + super.setUp() + dao = SqlDelightListsDaoImpl( + listQueries = database.listQueries + ) + } + + @Test + fun insertAll_InsertAllUiList() = runBlocking { + val list = listOf( + mockIListModel(name = "1").toUi(accountKey), + mockIListModel(name = "2").toUi(accountKey), + mockIListModel(name = "3").toUi(accountKey), + ) + dao.insertAll(list) + assertEquals(3, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + } + + @Test + fun getPagingSource_PagingSourceGenerateCorrectKeyForNext() = runBlocking { + val list = listOf( + mockIListModel(name = "1").toUi(accountKey), + mockIListModel(name = "2").toUi(accountKey), + mockIListModel(name = "3").toUi(accountKey), + ) + dao.insertAll(list) + val pagingSource = dao.getPagingSource( + accountKey = accountKey, + ) + val limit = 2 + val result = pagingSource.load(params = PagingSource.LoadParams.Refresh(0, limit, false)) + assert(result is PagingSource.LoadResult.Page) + assertEquals(limit, (result as PagingSource.LoadResult.Page).nextKey) + assertEquals(limit, result.data.size) + + val loadMoreResult = pagingSource.load(params = PagingSource.LoadParams.Append(result.nextKey ?: 0, limit, false)) + assert(loadMoreResult is PagingSource.LoadResult.Page) + assertEquals(null, (loadMoreResult as PagingSource.LoadResult.Page).nextKey) + } + + @Test + fun getPagingSource_pagingSourceInvalidateAfterDbUpdate() = runBlocking { + val trend = mockIListModel(name = "list1").toUi(accountKey) + var invalidate = false + dao.getPagingSource( + accountKey = accountKey, + ).apply { + registerInvalidatedCallback { + invalidate = true + } + load(PagingSource.LoadParams.Refresh(key = null, loadSize = 10, placeholdersEnabled = false)) + } + dao.insertAll(listOf(trend)) + val start = System.currentTimeMillis() + while (!invalidate && System.currentTimeMillis() - start < 3000) { + continue + } + assert(invalidate) + } + + @Test + fun update_UpdateWithGivenList() = runBlocking { + val list = listOf( + mockIListModel(name = "1").toUi(accountKey), + mockIListModel(name = "2").toUi(accountKey), + mockIListModel(name = "3").toUi(accountKey), + ) + dao.insertAll(list) + + dao.update(list.map { it.copy(title = "update") }) + assertEquals(3, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + list.forEach { + assertEquals("update", dao.findWithListKey(accountKey = it.accountKey, listKey = it.listKey)?.title) + } + } + + @Test + fun delete_DeleteWithGivenList() = runBlocking { + val list = listOf( + mockIListModel(name = "1").toUi(accountKey), + mockIListModel(name = "2").toUi(accountKey), + mockIListModel(name = "3").toUi(accountKey), + ) + dao.insertAll(list) + assertEquals(3, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + dao.delete(list.subList(0, 2)) + assertEquals(1, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt new file mode 100644 index 000000000..78216259c --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.dao + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.db.sqldelight.dao.SqlDelightNotificationCursorDaoImpl +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class SqlDelightNotificationCursorImplTest : BaseCacheDatabaseTest() { + + @Test + fun find_transformToUiData() = runBlocking { + val accountKey = MicroBlogKey.twitter("account") + val cursor = NotificationCursor( + _id = UUID.randomUUID().toString(), + accountKey = accountKey, + type = NotificationCursorType.Mentions, + value = "value", + timestamp = System.currentTimeMillis() + ) + val dao = SqlDelightNotificationCursorDaoImpl(database.notificationCursorQueries) + dao.add(cursor) + assertEquals(cursor, dao.find(accountKey = accountKey, type = cursor.type)) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt new file mode 100644 index 000000000..cb8138c76 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt @@ -0,0 +1,107 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.squareup.sqldelight.runtime.coroutines.asFlow +import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.dataprovider.mapper.toUi +import com.twidere.twiderex.db.sqldelight.transform.toDbList +import com.twidere.twiderex.mock.model.mockIListModel +import com.twidere.twiderex.model.MicroBlogKey +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +internal class ListQueriesImplTest : BaseCacheDatabaseTest() { + private val accountKey = MicroBlogKey.twitter("account") + @Test + fun insert_ReplaceWhenPrimaryKeyEquals() = runBlocking { + val insert = mockIListModel("insert").toUi(accountKey) + database.listQueries.insert(insert.toDbList()) + assertEquals( + "insert", + database.listQueries.findWithListKey( + accountKey = insert.accountKey, + listKey = insert.listKey + ).executeAsOneOrNull()?.title + ) + database.listQueries.insert(insert.toDbList().copy(title = "replace")) + assertEquals( + "replace", + database.listQueries.findWithListKey( + accountKey = insert.accountKey, + listKey = insert.listKey + ).executeAsOneOrNull()?.title + ) + } + + @Test + fun getPagingList_ReturnResultsWithGiveLimitAndOffset() = runBlocking { + val list = listOf( + mockIListModel(name = "1"), + mockIListModel(name = "2"), + mockIListModel(name = "3"), + mockIListModel(name = "4"), + ).map { it.toUi(accountKey) } + database.listQueries.transaction { + list.forEach { database.listQueries.insert(it.toDbList()) } + } + assertEquals(4, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + assertEquals(2, database.listQueries.getPagingList(accountKey = accountKey, limit = 2, offSet = 0).executeAsList().size) + assertEquals("3", database.listQueries.getPagingList(accountKey = accountKey, limit = 2, offSet = 2).executeAsList().first().title) + } + + @Test + fun delete_DeleteListWithGiveUniqueIndex() = runBlocking { + val insert = mockIListModel("insert").toUi(accountKey) + database.listQueries.insert(insert.toDbList()) + val flow = database.listQueries.findWithListKey(accountKey = insert.accountKey, listKey = insert.listKey).asFlow().mapToOneOrNull() + assertNotNull(flow.firstOrNull()) + database.listQueries.delete(accountKey = insert.accountKey, listKey = insert.listKey) + assertNull(flow.firstOrNull()) + } + + @Test + fun clearAll_DeleteAllListMatchesAccountKey() = runBlocking { + val list = mutableListOf( + mockIListModel(name = "1"), + mockIListModel(name = "2"), + mockIListModel(name = "3"), + mockIListModel(name = "4"), + ).map { it.toUi(accountKey) } + database.listQueries.transaction { + list.forEach { database.listQueries.insert(it.toDbList()) } + } + val otherAcct = MicroBlogKey.twitter("other") + database.listQueries.insert(mockIListModel().toUi(otherAcct).toDbList()) + assertEquals(4, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + assertEquals(1, database.listQueries.getPagingCount(accountKey = otherAcct).executeAsOne()) + + database.listQueries.clearAll(accountKey = accountKey) + + assertEquals(0, database.listQueries.getPagingCount(accountKey = accountKey).executeAsOne()) + assertEquals(1, database.listQueries.getPagingCount(accountKey = otherAcct).executeAsOne()) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt new file mode 100644 index 000000000..17cad85a6 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt @@ -0,0 +1,61 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight + +import com.twidere.twiderex.base.BaseCacheDatabaseTest +import com.twidere.twiderex.db.sqldelight.transform.toDb +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class NotificationCursorQueriesImplTest : BaseCacheDatabaseTest() { + private val accountKey = MicroBlogKey.twitter("account") + private val cursor = NotificationCursor( + _id = UUID.randomUUID().toString(), + accountKey = accountKey, + type = NotificationCursorType.Mentions, + value = "value", + timestamp = System.currentTimeMillis() + ) + @Test + fun insert_ReplaceWhenPrimaryKeyEquals() = runBlocking { + database.notificationCursorQueries.insert(cursor.toDb().copy(value = "insert")) + assertEquals( + "insert", + database.notificationCursorQueries.find( + accountKey = cursor.accountKey, + type = cursor.type + ).executeAsOneOrNull()?.value + ) + database.notificationCursorQueries.insert(cursor.toDb().copy(value = "replace")) + assertEquals( + "replace", + database.notificationCursorQueries.find( + accountKey = cursor.accountKey, + type = cursor.type + ).executeAsOneOrNull()?.value + ) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt new file mode 100644 index 000000000..79ab4d466 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt @@ -0,0 +1,63 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiList +import com.twidere.twiderex.sqldelight.table.List +import org.junit.Test +import kotlin.test.assertEquals + +internal class ListTransformTest { + @Test + fun transform() { + val ui = UiList( + accountKey = MicroBlogKey.twitter("account"), + id = "id", + ownerId = "ownerId", + title = "title", + descriptions = "desc", + mode = "mode", + replyPolicy = "private", + listKey = MicroBlogKey.twitter("list"), + isFollowed = true, + allowToSubscribe = false + ) + val db = ui.toDbList() + assertSuccess(db, ui) + + val uiFromDb = db.toUi() + assertSuccess(db, uiFromDb) + } + + private fun assertSuccess(db: List, ui: UiList) { + assertEquals(db.accountKey, ui.accountKey) + assertEquals(db.listId, ui.id) + assertEquals(db.ownerId, ui.ownerId) + assertEquals(db.title, ui.title) + assertEquals(db.descriptions, ui.descriptions) + assertEquals(db.mode, ui.mode) + assertEquals(db.replyPolicy, ui.replyPolicy) + assertEquals(db.listKey, ui.listKey) + assertEquals(db.isFollowed, ui.isFollowed) + assertEquals(db.allowToSubscribe, ui.allowToSubscribe) + } +} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt new file mode 100644 index 000000000..1388fdba9 --- /dev/null +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt @@ -0,0 +1,55 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.transform + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.NotificationCursorType +import com.twidere.twiderex.model.paging.NotificationCursor +import com.twidere.twiderex.sqldelight.table.DbNotificationCursor +import org.junit.Test +import java.util.UUID +import kotlin.test.assertEquals + +internal class NotificationCursorTransformTest { + @Test + fun transform() { + val ui = NotificationCursor( + accountKey = MicroBlogKey.twitter("account"), + _id = UUID.randomUUID().toString(), + type = NotificationCursorType.Mentions, + value = "value", + timestamp = System.currentTimeMillis() + ) + val db = ui.toDb() + assertSuccess(db, ui) + + val uiFromDb = db.toUi() + assertSuccess(db, uiFromDb) + } + + private fun assertSuccess(db: DbNotificationCursor, ui: NotificationCursor) { + assertEquals(db.accountKey, ui.accountKey) + assertEquals(db.id, ui._id) + assertEquals(db.type, ui.type) + assertEquals(db.value, ui.value) + assertEquals(db.timestamp, ui.timestamp) + } +} From d3213b5987e789f7c64fccd2852402fe98255f49 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Sep 2021 14:53:57 +0800 Subject: [PATCH 261/615] fix androidAndroidTest failed issue --- .../cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq | 2 ++ .../com/twidere/twiderex/sqldelight/table/DMConversation.sq | 2 +- .../cache/com/twidere/twiderex/sqldelight/table/List.sq | 2 +- .../com/twidere/twiderex/sqldelight/table/NotificationCursor.sq | 2 +- .../cache/com/twidere/twiderex/sqldelight/table/Trend.sq | 2 +- .../cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq index 24438dbe4..fe09be178 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/CacheDrop.sq @@ -6,4 +6,6 @@ clearAllTables{ DELETE FROM DMConversation; DELETE FROM Trend; DELETE FROM TrendHistory; + DELETE FROM DbNotificationCursor; + DELETE FROM List; } \ No newline at end of file diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq index 766d60ae2..1e6b7e530 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/DMConversation.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.ui.UiDMConversation; -CREATE TABLE DMConversation ( +CREATE TABLE IF NOT EXISTS DMConversation ( accountKey TEXT AS MicroBlogKey NOT NULL, conversationKey TEXT AS MicroBlogKey NOT NULL, recipientKey TEXT AS MicroBlogKey NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq index 95bf8824b..f60b7f0f6 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/List.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import java.lang.Boolean; -CREATE TABLE List ( +CREATE TABLE IF NOT EXISTS List ( listId TEXT NOT NULL, ownerId TEXT NOT NULL, title TEXT NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq index d0c1acfc9..7335ecb05 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/NotificationCursor.sq @@ -1,7 +1,7 @@ import com.twidere.twiderex.model.MicroBlogKey; import com.twidere.twiderex.model.enums.NotificationCursorType; -CREATE TABLE DbNotificationCursor ( +CREATE TABLE IF NOT EXISTS DbNotificationCursor ( id TEXT NOT NULL PRIMARY KEY, accountKey TEXT AS MicroBlogKey NOT NULL, type TEXT AS NotificationCursorType NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq index 765b9ecb4..a2ce4c913 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/Trend.sq @@ -1,6 +1,6 @@ import com.twidere.twiderex.model.MicroBlogKey; -CREATE TABLE Trend ( +CREATE TABLE IF NOT EXISTS Trend ( trendKey TEXT AS MicroBlogKey NOT NULL, accountKey TEXT AS MicroBlogKey NOT NULL, displayName TEXT NOT NULL, diff --git a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq index 0d329d3c7..9fa2d278c 100644 --- a/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq +++ b/common/src/commonMain/sqldelight/cache/com/twidere/twiderex/sqldelight/table/TrendHistory.sq @@ -1,6 +1,6 @@ import com.twidere.twiderex.model.MicroBlogKey; -CREATE TABLE TrendHistory ( +CREATE TABLE IF NOT EXISTS TrendHistory ( trendKey TEXT AS MicroBlogKey NOT NULL, accountKey TEXT AS MicroBlogKey NOT NULL, day Integer NOT NULL, From 2ac0634f48c47d8c8731a165c8bd012b3e8f19ef Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 29 Sep 2021 15:04:28 +0800 Subject: [PATCH 262/615] [WIP] add external package source --- .../twiderex/extensions/ComposeExtensions.kt | 36 -- .../paging/compose/LazyPagingItems.kt | 22 +- .../kotlin/com/mxalbert/zoomable/Zoomable.kt | 235 ++++++++++++ .../com/mxalbert/zoomable/ZoomableState.kt | 352 ++++++++++++++++++ .../twiderex/component/UserComponent.kt | 2 - .../component/foundation/BlurImage.kt | 2 +- .../component/foundation/SignInScaffold.kt | 2 +- .../component/foundation/VideoPlayer.kt | 2 +- .../component/lazy/ui/LazyUiDMEventList.kt | 4 +- .../component/lazy/ui/LazyUiListsList.kt | 6 +- .../component/lazy/ui/LazyUiStatusList.kt | 4 +- .../component/lazy/ui/LazyUiUserList.kt | 2 +- .../lists/MastodonListsModifyComponent.kt | 2 +- .../lists/TwitterListsModifyComponent.kt | 2 +- .../status/DetailedStatusComponent.kt | 4 +- .../twiderex/component/status/MastodonPoll.kt | 2 +- .../component/status/StatusActions.kt | 4 +- .../component/status/StatusMediaComponent.kt | 2 +- .../twiderex/component/status/StatusText.kt | 2 +- .../twiderex/component/status/StatusThread.kt | 2 +- .../status/TimelineStatusComponent.kt | 4 +- .../twiderex/component/status/UserAvatar.kt | 2 +- .../twiderex/extensions/MastodonExtensions.kt | 21 +- .../twiderex/extensions/NumberExtensions.kt | 0 .../twidere/twiderex/scenes/DraftListScene.kt | 2 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 4 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 4 +- .../twidere/twiderex/scenes/PureMediaScene.kt | 4 +- .../twidere/twiderex/scenes/SignInScene.kt | 4 +- .../twidere/twiderex/scenes/StatusScene.kt | 2 +- .../twiderex/scenes/compose/ComposeScene.kt | 10 +- .../compose/ComposeSearchHashtagScene.kt | 2 +- .../scenes/compose/ComposeSearchUserScene.kt | 2 +- .../scenes/dm/DMConversationListScene.kt | 4 +- .../twiderex/scenes/dm/DMConversationScene.kt | 4 +- .../scenes/dm/DMNewConversationScene.kt | 4 +- .../scenes/home/AllNotificationItem.kt | 4 +- .../twiderex/scenes/home/HomeTimelineItem.kt | 4 +- .../twidere/twiderex/scenes/home/MeItem.kt | 4 +- .../twiderex/scenes/home/MentionItem.kt | 4 +- .../twiderex/scenes/home/NotificationItem.kt | 4 +- .../twiderex/scenes/home/SearchItem.kt | 4 +- .../home/mastodon/FederatedTimelineItem.kt | 4 +- .../scenes/home/mastodon/LocalTimelineItem.kt | 4 +- .../home/mastodon/MastodonNotificationItem.kt | 4 +- .../scenes/lists/ListsAddMembersScene.kt | 4 +- .../scenes/lists/ListsMembersScene.kt | 4 +- .../twiderex/scenes/lists/ListsScene.kt | 4 +- .../scenes/lists/ListsSubscribersScene.kt | 2 +- .../scenes/lists/ListsTimelineScene.kt | 6 +- .../platform/MastodonListsCreateDialog.kt | 2 +- .../lists/platform/MastodonListsEditDialog.kt | 2 +- .../lists/platform/TwitterListsCreateScene.kt | 2 +- .../lists/platform/TwitterListsEditScene.kt | 2 +- .../scenes/mastodon/MastodonSignInScene.kt | 4 +- .../scenes/search/SearchInputScene.kt | 4 +- .../twiderex/scenes/search/SearchScene.kt | 4 +- .../search/tabs/MastodonSearchHashtagItem.kt | 2 +- .../scenes/search/tabs/SearchTweetsItem.kt | 2 +- .../scenes/search/tabs/SearchUserItem.kt | 2 +- .../search/tabs/TwitterSearchMediaItem.kt | 2 +- .../twiderex/scenes/settings/AboutScene.kt | 4 +- .../scenes/settings/AccountManagementScene.kt | 2 +- .../settings/AccountNotificationScene.kt | 2 +- .../scenes/settings/AppearanceScene.kt | 4 +- .../twiderex/scenes/settings/DisplayScene.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 4 +- .../scenes/settings/NotificationScene.kt | 2 +- .../twiderex/scenes/settings/SettingsScene.kt | 4 +- .../twiderex/scenes/settings/StorageScene.kt | 2 +- .../twiderex/scenes/user/FollowersScene.kt | 2 +- .../twiderex/scenes/user/FollowingScene.kt | 2 +- .../twidere/twiderex/scenes/user/UserScene.kt | 4 +- .../kotlin/com/twidere/twiderex/ui/Color.kt | 1 - .../kotlin/com/twidere/twiderex/ui/Theme.kt | 19 +- .../nestedscrollview/NestedScrollView.kt | 137 +++++++ .../nestedscrollview/NestedScrollViewState.kt | 177 +++++++++ .../moe/tlaster/placeholder/PlaceHolder.kt | 134 +++++++ .../kotlin/moe/tlaster/swiper/Swiper.kt | 94 +++++ .../kotlin/moe/tlaster/swiper/SwiperState.kt | 129 +++++++ 80 files changed, 1404 insertions(+), 167 deletions(-) delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt create mode 100644 common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt create mode 100644 common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt (53%) rename {android/src/main => common/src/commonMain}/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt (100%) create mode 100644 common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt create mode 100644 common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt deleted file mode 100644 index b6e1cdac7..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/ComposeExtensions.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.extensions - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable -import com.twidere.twiderex.preferences.LocalAppearancePreferences -import com.twidere.twiderex.preferences.model.AppearancePreferences - -@Composable -fun isDarkTheme(): Boolean { - return when (LocalAppearancePreferences.current.theme) { - AppearancePreferences.Theme.Auto -> isSystemInDarkTheme() - AppearancePreferences.Theme.Light -> false - AppearancePreferences.Theme.Dark -> true - else -> false - } -} diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt index f6c16af50..891a139d9 100644 --- a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt +++ b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt @@ -1,3 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package androidx.paging.compose import android.annotation.SuppressLint @@ -307,4 +327,4 @@ public fun LazyListScope.itemsIndexed( } } -private data class PagingPlaceholderKey(private val index: Int) \ No newline at end of file +private data class PagingPlaceholderKey(private val index: Int) diff --git a/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt new file mode 100644 index 000000000..7f9abf7bf --- /dev/null +++ b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt @@ -0,0 +1,235 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.mxalbert.zoomable + +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation +import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.drag +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.gestures.rememberTransformableState +import androidx.compose.foundation.gestures.transformable +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.geometry.center +import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.PointerInputScope +import androidx.compose.ui.input.pointer.consumePositionChange +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.positionChange +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.toSize +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlin.math.roundToInt + +/** + * A zoomable layout that supports zooming in and out, dragging, double tap and dismiss gesture. + * + * @param modifier The modifier to apply to this layout. + * @param state The state object to be used to control or observe the state. + * @param enabled Controls the enabled state. When false, all gestures will be ignored. + * @param dismissGestureEnabled Whether to enable dismiss gesture detection. + * @param onDismiss Will be called when dismiss gesture is detected. Should return a boolean + * indicating whether the dismiss request is handled. + * @param content a block which describes the content. + */ +@Composable +fun Zoomable( + modifier: Modifier = Modifier, + state: ZoomableState = rememberZoomableState(), + enabled: Boolean = true, + dismissGestureEnabled: Boolean = false, + onDismiss: () -> Boolean = { false }, + content: @Composable () -> Unit +) { + val dismissGestureEnabledState = rememberUpdatedState(dismissGestureEnabled) + val scope = rememberCoroutineScope() + val gesturesModifier = if (!enabled) Modifier else Modifier + .pointerInput(Unit) { + detectTapAndDragGestures( + state = state, + dismissGestureEnabled = dismissGestureEnabledState, + onDismiss = onDismiss + ) + } + .transformable( + state = rememberTransformableState { zoomChange, panChange, _ -> + if (state.dismissDragAbsoluteOffsetY == 0f) { + scope.launch { + state.onZoomChange(zoomChange) + state.onDrag(panChange) + } + } + } + ) + + Box( + modifier = modifier + .then(gesturesModifier) + .layout { measurable, constraints -> + val width = constraints.maxWidth + val height = constraints.maxHeight + val placeable = measurable.measure( + Constraints( + maxWidth = (width * state.scale).roundToInt(), + maxHeight = (height * state.scale).roundToInt() + ) + ) + state.size = IntSize(width, height) + state.childSize = Size( + placeable.width / state.scale, + placeable.height / state.scale + ) + layout(width, height) { + placeable.placeWithLayer(0, 0) { + translationX = state.translationX - state.boundOffset.x + translationY = + state.translationY - state.boundOffset.y + state.dismissDragOffsetY + } + } + } + ) { + content() + } +} + +internal suspend fun PointerInputScope.detectTapAndDragGestures( + state: ZoomableState, + dismissGestureEnabled: State, + onDismiss: () -> Boolean +) = coroutineScope { + launch { + detectTapGestures( + onDoubleTap = { offset -> + launch { + val isZooming = state.isZooming + val targetScale = + if (isZooming) state.minScale else state.doubleTapScale + state.animateScaleTo( + targetScale = targetScale, + targetTranslation = if (isZooming) { + Offset.Zero + } else { + state.calculateTargetTranslation(offset) * targetScale + } + ) + } + }, + onPress = { + state.onPress() + } + ) + } + launch { + detectDragGestures( + state = state, + dismissGestureEnabled = dismissGestureEnabled, + onDrag = { change, dragAmount -> + if (state.isZooming) { + launch { + state.onDrag(dragAmount) + state.addPosition( + change.uptimeMillis, + change.position + ) + } + } else { + state.onDismissDrag(dragAmount.y) + } + }, + onDragCancel = { + if (state.isZooming) { + state.resetTracking() + } else { + launch { + state.onDismissDragEnd() + } + } + }, + onDragEnd = { + launch { + if (state.isZooming) { + state.onDragEnd() + } else { + if (!(state.shouldDismiss && onDismiss())) { + state.onDismissDragEnd() + } + } + } + } + ) + } +} + +private suspend fun PointerInputScope.detectDragGestures( + state: ZoomableState, + dismissGestureEnabled: State, + onDragEnd: () -> Unit = {}, + onDragCancel: () -> Unit = {}, + onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit +) { + forEachGesture { + awaitPointerEventScope { + // We have to always call this or we'll get a crash if we do nothing + val down = awaitFirstDown(requireUnconsumed = false) + if (state.isZooming || dismissGestureEnabled.value) { + var overSlop = Offset.Zero + val drag = if (state.isZooming) { + awaitTouchSlopOrCancellation(down.id) { change, over -> + change.consumePositionChange() + overSlop = over + } + } else { + awaitVerticalTouchSlopOrCancellation(down.id) { change, over -> + change.consumePositionChange() + overSlop = Offset(0f, over) + } + } + if (drag != null) { + onDrag(drag, overSlop) + if ( + !drag(drag.id) { + onDrag(it, it.positionChange()) + it.consumePositionChange() + } + ) { + onDragCancel() + } else { + onDragEnd() + } + } + } + } + } +} + +private fun ZoomableState.calculateTargetTranslation(doubleTapPoint: Offset): Offset = + (size.toSize().center + Offset(translationX, translationY) - doubleTapPoint) / scale diff --git a/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt b/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt new file mode 100644 index 000000000..7be23d913 --- /dev/null +++ b/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt @@ -0,0 +1,352 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.mxalbert.zoomable + +import androidx.annotation.FloatRange +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.animate +import androidx.compose.animation.core.exponentialDecay +import androidx.compose.animation.core.spring +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.geometry.lerp +import androidx.compose.ui.input.pointer.util.VelocityTracker +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlin.math.PI +import kotlin.math.abs +import kotlin.math.roundToInt +import kotlin.math.sin + +/** + * Create a [ZoomableState] that is remembered across compositions. + * + * @param minScale The minimum [ZoomableState.scale] value. + * @param maxScale The maximum [ZoomableState.scale] value. + * @param doubleTapScale The [ZoomableState.scale] Value to animate to when a double tap happens. + * @param initialScale The initial value for [ZoomableState.scale]. + * @param initialTranslationX The initial value for [ZoomableState.translationX]. + * @param initialTranslationY The initial value for [ZoomableState.translationY]. + */ +@Composable +fun rememberZoomableState( + @FloatRange(from = 0.0) minScale: Float = ZoomableDefaults.MinScale, + @FloatRange(from = 0.0) maxScale: Float = ZoomableDefaults.MaxScale, + @FloatRange(from = 0.0) doubleTapScale: Float = ZoomableDefaults.DoubleTapScale, + @FloatRange(from = 0.0) initialScale: Float = minScale, + @FloatRange(from = 0.0) initialTranslationX: Float = 0f, + @FloatRange(from = 0.0) initialTranslationY: Float = 0f +): ZoomableState { + return rememberSaveable(saver = ZoomableState.Saver) { + ZoomableState(initialScale, initialTranslationX, initialTranslationY) + }.apply { + this.minScale = minScale + this.maxScale = maxScale + this.doubleTapScale = doubleTapScale + } +} + +/** + * A state object that can be hoisted to observe scale and translate for [Zoomable]. + * + * @param initialScale The initial value for [scale]. + * @param initialTranslationX The initial value for [translationX]. + * @param initialTranslationY The initial value for [translationY]. + * @see rememberZoomableState + */ +@Stable +class ZoomableState( + @FloatRange(from = 0.0) initialScale: Float = ZoomableDefaults.MinScale, + @FloatRange(from = 0.0) initialTranslationX: Float = 0f, + @FloatRange(from = 0.0) initialTranslationY: Float = 0f +) { + /** + * The minimum [scale] value. + */ + @FloatRange(from = 0.0) + var minScale: Float = ZoomableDefaults.MinScale + set(value) { + if (field != value) { + field = value + scale = scale // Make sure scale is in range + } + } + + /** + * The maximum [scale] value. + */ + @FloatRange(from = 0.0) + var maxScale: Float = ZoomableDefaults.MaxScale + set(value) { + if (field != value) { + field = value + scale = scale // Make sure scale is in range + } + } + + /** + * The [scale] value to animate to when a double tap happens. + */ + @FloatRange(from = 0.0) + var doubleTapScale: Float = ZoomableDefaults.DoubleTapScale + + private val velocityTracker = VelocityTracker() + private var _scale by mutableStateOf(initialScale) + private var _translationX = Animatable(initialTranslationX) + private var _translationY = Animatable(initialTranslationY) + private var _childSize by mutableStateOf(Size.Zero) + + internal var boundOffset by mutableStateOf(IntOffset.Zero) + private set + + internal var dismissDragAbsoluteOffsetY by mutableStateOf(0f) + private set + + internal val dismissDragOffsetY: Float + get() { + val maxOffset = childSize.height + return if (maxOffset == 0f) 0f else { + val progress = (dismissDragAbsoluteOffsetY / maxOffset).coerceIn(-1f, 1f) + maxOffset / DismissDragResistanceFactor * sin(progress * PI.toFloat() / 2) + } + } + + internal val shouldDismiss: Boolean + get() = abs(dismissDragAbsoluteOffsetY) > size.height * DismissDragThreshold + + internal var size = IntSize.Zero + set(value) { + if (field != value) { + field = value + updateBounds() + } + } + + internal var childSize: Size + get() = _childSize + set(value) { + if (_childSize != value) { + _childSize = value + updateBounds() + } + } + + /** + * Current scale of [Zoomable]. + */ + @get:FloatRange(from = 0.0) + var scale: Float + get() = _scale + internal set(value) { + _scale = value.coerceIn(minimumValue = minScale, maximumValue = maxScale) + updateBounds() + } + + /** + * Current translationX of [Zoomable]. + */ + @get:FloatRange(from = 0.0) + val translationX: Float + get() = _translationX.value + + /** + * Current translationY of [Zoomable]. + */ + @get:FloatRange(from = 0.0) + val translationY: Float + get() = _translationY.value + + val isZooming: Boolean + get() = scale > minScale && scale <= maxScale + + private fun updateBounds() { + val offsetX = childSize.width * scale - size.width + val offsetY = childSize.height * scale - size.height + boundOffset = IntOffset((offsetX / 2f).roundToInt(), (offsetY / 2f).roundToInt()) + val maxX = offsetX.coerceAtLeast(0f) / 2f + val maxY = offsetY.coerceAtLeast(0f) / 2f + _translationX.updateBounds(-maxX, maxX) + _translationY.updateBounds(-maxY, maxY) + } + + /** + * Animate [scale] to [targetScale]. + * + * @param targetScale The [scale] value to animate to. + * @param targetTranslation The [translationX] and [translationY] value to animate to. Use the + * default value to maintain current center point. Use [Offset.Unspecified] to leave + * translation unchanged. + * @param animationSpec [AnimationSpec] to be used for this scaling. + */ + suspend fun animateScaleTo( + targetScale: Float, + targetTranslation: Offset = Offset(translationX, translationY) / scale * targetScale, + animationSpec: AnimationSpec = spring() + ) = coroutineScope { + val initialTranslation = Offset(translationX, translationY) + val initialScale = scale + val range = targetScale - initialScale + animate( + initialValue = initialScale, + targetValue = targetScale, + animationSpec = animationSpec + ) { value, _ -> + launch { + // Update scale here to ensure scale and translation values are updated + // in the same snapshot + scale = value + if (targetTranslation != Offset.Unspecified) { + val fraction = if (range == 0f) 1f else (value - initialScale) / range + val translation = lerp(initialTranslation, targetTranslation, fraction) + _translationX.snapTo(translation.x) + _translationY.snapTo(translation.y) + } + } + } + } + + /** + * Animate [translationX] and [translationY] to [targetTranslation]. + * + * @param targetTranslation The [translationX] and [translationY] value to animate to. + * @param animationSpec [AnimationSpec] to be used for this scaling. + */ + suspend fun animateTranslateTo( + targetTranslation: Offset, + animationSpec: AnimationSpec = spring() + ) = coroutineScope { + animate( + typeConverter = Offset.VectorConverter, + initialValue = Offset(translationX, translationY), + targetValue = targetTranslation, + animationSpec = animationSpec + ) { value, _ -> + launch { + _translationX.snapTo(value.x) + _translationY.snapTo(value.y) + } + } + } + + private suspend fun fling(velocity: Offset) = coroutineScope { + val spec = exponentialDecay() + launch { _translationX.animateDecay(initialVelocity = velocity.x, animationSpec = spec) } + launch { _translationY.animateDecay(initialVelocity = velocity.y, animationSpec = spec) } + } + + internal suspend fun onDrag(dragAmount: Offset) { + _translationX.snapTo(_translationX.value + dragAmount.x) + _translationY.snapTo(_translationY.value + dragAmount.y) + } + + internal suspend fun onDragEnd() { + val velocity = velocityTracker.calculateVelocity() + fling(Offset(velocity.x, velocity.y)) + } + + internal suspend fun onPress() { + _translationX.stop() + _translationY.stop() + } + + internal fun onZoomChange(zoomChange: Float) { + scale *= zoomChange + } + + internal fun addPosition(timeMillis: Long, position: Offset) { + velocityTracker.addPosition(timeMillis = timeMillis, position = position) + } + + internal fun resetTracking() { + velocityTracker.resetTracking() + } + + internal fun onDismissDrag(dragAmountY: Float) { + dismissDragAbsoluteOffsetY += dragAmountY + } + + internal suspend fun onDismissDragEnd() { + animate( + initialValue = dismissDragAbsoluteOffsetY, + targetValue = 0f + ) { value, _ -> + dismissDragAbsoluteOffsetY = value + } + } + + override fun toString(): String = + "ZoomableState(translateX=%.1f,translateY=%.1f,scale=%.2f)".format( + translationX, translationY, scale + ) + + companion object { + /** + * The default [Saver] implementation for [ZoomableState]. + */ + val Saver: Saver = listSaver( + save = { + listOf( + it.translationX, + it.translationY, + it.scale + ) + }, + restore = { + ZoomableState( + initialTranslationX = it[0], + initialTranslationY = it[1], + initialScale = it[2] + ) + } + ) + } +} + +internal const val DismissDragResistanceFactor = 2f +internal const val DismissDragThreshold = 0.25f + +object ZoomableDefaults { + /** + * The default value for [ZoomableState.minScale]. + */ + const val MinScale = 1f + + /** + * The default value for [ZoomableState.maxScale]. + */ + const val MaxScale = 4f + + /** + * The default value for [ZoomableState.doubleTapScale]. + */ + const val DoubleTapScale = 2f +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt index 89049b27e..1d8f7dfef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -72,8 +72,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt index 020f77977..1430b5488 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/BlurImage.kt @@ -41,9 +41,9 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource import androidx.core.graphics.drawable.toBitmap import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.twidere.twiderex.component.painterResource @Composable fun BlurImage( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt index 7263f008b..1cff790e1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt @@ -43,10 +43,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.LoginLogo +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.ui.TwidereScene @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 509f2db75..2194f3a56 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -43,7 +43,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver @@ -58,6 +57,7 @@ import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.twidere.twiderex.component.status.UserAvatarDefaults +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig import com.twidere.twiderex.preferences.model.DisplayPreferences diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index b626bdf08..943aaf2bf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -62,13 +62,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.placeholder.UiUserPlaceholder import com.twidere.twiderex.component.status.HtmlText import com.twidere.twiderex.component.status.ResolvedLink @@ -76,6 +75,7 @@ import com.twidere.twiderex.component.status.StatusMediaDefaults import com.twidere.twiderex.component.status.StatusMediaPreviewItem import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserAvatarDefaults +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt index ae0c36c57..c01fb5619 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt @@ -44,15 +44,14 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.StatusDivider +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.enums.ListType import com.twidere.twiderex.model.ui.UiList import moe.tlaster.placeholder.TextPlaceHolder @@ -238,7 +237,6 @@ private fun DividerListItem( } } - @Composable private fun EmptyList() { Column( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt index 21af62a18..8175031ff 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt @@ -64,17 +64,17 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.itemsIndexed import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.placeholder.UiStatusPlaceholder import com.twidere.twiderex.component.status.StatusDivider import com.twidere.twiderex.component.status.StatusThreadStyle import com.twidere.twiderex.component.status.TimelineStatusComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiStatus import kotlinx.coroutines.flow.collect diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt index 2f23156e7..616dab65f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt @@ -38,7 +38,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items @@ -47,6 +46,7 @@ import com.twidere.twiderex.component.placeholder.UiUserPlaceholder import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.ui.UiUser @OptIn(ExperimentalMaterialApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt index 9e097ea16..c2d079653 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt @@ -34,8 +34,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.stringResource @Composable fun MastodonListsModifyComponent( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt index 6b0f00806..b8168d3e4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt @@ -49,13 +49,13 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.ColoredSwitch +import com.twidere.twiderex.component.stringResource @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt index e2ecf66ac..411349e93 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt @@ -43,10 +43,10 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.FormattedTime +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.humanizedCount import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt index 44cbea33c..a1f96d25d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt @@ -64,9 +64,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.action.LocalStatusActions +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.humanizedTimestamp import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.Option diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 92f169f30..38b9bddd2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -53,14 +53,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.ExperimentalUnitApi import androidx.compose.ui.unit.dp import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.humanizedCount import com.twidere.twiderex.extensions.shareText import com.twidere.twiderex.model.enums.ComposeType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 784a5b0cd..ec7e5149c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -51,13 +51,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.GridLayout import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer import com.twidere.twiderex.component.image.ImageBlur import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiMedia diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index 1f3d636e3..9c85890a1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -43,8 +43,8 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt index 7da9f777c..c98cc6db2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt @@ -28,8 +28,8 @@ import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.ui.UiStatus @Composable diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 21a4eb413..aba084ac8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -57,8 +57,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.layoutId import androidx.compose.ui.platform.LocalLayoutDirection -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -67,6 +65,8 @@ import androidx.constraintlayout.compose.ConstraintSet import androidx.constraintlayout.compose.Dimension import com.twidere.twiderex.component.HumanizedTime import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.icon import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.PlatformType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt index 430d9caaf..3988980a9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt @@ -31,10 +31,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip -import com.twidere.twiderex.component.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.preferences.LocalDisplayPreferences diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt similarity index 53% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt index adb595d60..2a64e84ad 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt @@ -22,27 +22,26 @@ package com.twidere.twiderex.extensions import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.twidere.twiderex.R +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.enums.MastodonVisibility @Composable fun MastodonVisibility.icon(): Painter { return when (this) { - MastodonVisibility.Public -> painterResource(id = R.drawable.ic_globe) - MastodonVisibility.Unlisted -> painterResource(id = R.drawable.ic_lock_open) - MastodonVisibility.Private -> painterResource(id = R.drawable.ic_lock) - MastodonVisibility.Direct -> painterResource(id = R.drawable.ic_mail) + MastodonVisibility.Public -> painterResource(res = com.twidere.twiderex.MR.files.globe) + MastodonVisibility.Unlisted -> painterResource(res = com.twidere.twiderex.MR.files.lock_open) + MastodonVisibility.Private -> painterResource(res = com.twidere.twiderex.MR.files.lock) + MastodonVisibility.Direct -> painterResource(res = com.twidere.twiderex.MR.files.mail) } } @Composable fun MastodonVisibility.stringName(): String { return when (this) { - MastodonVisibility.Public -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_public) - MastodonVisibility.Unlisted -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_unlisted) - MastodonVisibility.Private -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_private) - MastodonVisibility.Direct -> stringResource(id = com.twidere.common.R.string.scene_compose_visibility_direct) + MastodonVisibility.Public -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_visibility_public) + MastodonVisibility.Unlisted -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_visibility_unlisted) + MastodonVisibility.Private -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_visibility_private) + MastodonVisibility.Direct -> stringResource(res = com.twidere.twiderex.MR.strings.scene_compose_visibility_direct) } } diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt similarity index 100% rename from android/src/main/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index 532edf84d..405c40472 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -40,11 +40,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index a4fc0e9ca..0aed3f9ed 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -70,8 +70,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.UserMetrics import com.twidere.twiderex.component.foundation.AppBar @@ -84,9 +82,11 @@ import com.twidere.twiderex.component.foundation.Pager import com.twidere.twiderex.component.foundation.PagerState import com.twidere.twiderex.component.foundation.rememberPagerState import com.twidere.twiderex.component.lazy.divider +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.HomeMenus diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index bb462d07a..b86364cc7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -65,8 +65,6 @@ import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsHeight @@ -83,6 +81,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.VideoPlayer +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.LikeButton import com.twidere.twiderex.component.status.ReplyButton import com.twidere.twiderex.component.status.RetweetButton @@ -91,6 +90,7 @@ import com.twidere.twiderex.component.status.StatusText import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.hideControls import com.twidere.twiderex.extensions.observeAsState diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index e04dc0532..b853fc64b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -50,8 +50,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.google.accompanist.insets.navigationBarsPadding @@ -60,6 +58,8 @@ import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.rememberPagerState import com.google.android.exoplayer2.ui.PlayerControlView import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.hideControls import com.twidere.twiderex.extensions.observeAsState diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index f8fb769f0..a47c8425f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -44,12 +44,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.DefaultConfig import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import kotlinx.coroutines.launch diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt index e0fe7c1a7..b4e874ee8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt @@ -45,7 +45,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalDensity -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -58,6 +57,7 @@ import com.twidere.twiderex.component.status.DetailedStatusComponent import com.twidere.twiderex.component.status.StatusDivider import com.twidere.twiderex.component.status.StatusThreadStyle import com.twidere.twiderex.component.status.TimelineStatusComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index 78f34cc4f..f5484687d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -96,8 +96,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.TextFieldValue @@ -110,12 +108,14 @@ import com.twidere.twiderex.component.foundation.InAppNotificationBottomSheetSca import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.itemsGridIndexed +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.StatusLineComponent import com.twidere.twiderex.component.status.TimelineStatusComponent import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserAvatarDefaults import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.icon import com.twidere.twiderex.extensions.observeAsState @@ -275,7 +275,7 @@ private fun ComposeBody( Icon( painter = painterResource(res = if (enableThreadMode) com.twidere.twiderex.MR.files.send_thread else com.twidere.twiderex.MR.files.send), contentDescription = stringResource( - id = if (enableThreadMode) com.twidere.twiderex.MR.strings.accessibility_scene_compose_thread else com.twidere.twiderex.MR.strings.accessibility_scene_compose_send + res = if (enableThreadMode) com.twidere.twiderex.MR.strings.accessibility_scene_compose_thread else com.twidere.twiderex.MR.strings.accessibility_scene_compose_send ), tint = if (canSend) MaterialTheme.colors.primary else LocalContentColor.current.copy( alpha = LocalContentAlpha.current @@ -1264,7 +1264,7 @@ private fun ComposeActions( Icon( painter = painterResource(res = com.twidere.twiderex.MR.files.map_pin), contentDescription = stringResource( - id = if (locationEnabled) { + res = if (locationEnabled) { com.twidere.twiderex.MR.strings.accessibility_scene_compose_location_disable } else { com.twidere.twiderex.MR.strings.accessibility_scene_compose_location_enable @@ -1296,7 +1296,7 @@ private fun ComposeActions( Box { Icon( painter = painterResource( - id = if (draftCount.value > 9) + res = if (draftCount.value > 9) com.twidere.twiderex.MR.files.drafts_more else com.twidere.twiderex.MR.files.draft_number diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index dd37ae5ad..f6a3b4d0f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -37,7 +37,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items @@ -46,6 +45,7 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.loadState +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.LocalNavController diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index c4f6c9f60..fefc8e5f6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -34,7 +34,6 @@ import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.AppBar @@ -43,6 +42,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.loadState import com.twidere.twiderex.component.lazy.ui.LazyUiUserList +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.LocalActiveAccount diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index e2d2345c8..e3209cf94 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -26,8 +26,6 @@ import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.AppBar @@ -36,6 +34,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.lazy.ui.LazyUiDMConversationList +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index 326ac8757..b1fb1f38c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -63,8 +63,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.compose.collectAsLazyPagingItems @@ -75,6 +73,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiDMEventList +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index a9282a95e..535dd0f41 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -37,8 +37,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems @@ -47,6 +45,8 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiUserList +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.ui.UiUser diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt index 35e5ca6d4..662bd4b50 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.ui.LocalActiveAccount diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index a09217564..b2ea5ab01 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -25,8 +25,6 @@ import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.TimelineComponent @@ -35,6 +33,8 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.model.enums.ComposeType diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index 6293091da..cc135ad27 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -23,12 +23,12 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index ef31f34ec..fdc617218 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -23,13 +23,13 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index 9ed88d15f..010908997 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.scenes.home import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index a0f113039..dc5b0b757 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -44,14 +44,14 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.trend.MastodonTrendItem import com.twidere.twiderex.component.trend.TwitterTrendItem import com.twidere.twiderex.di.ext.getViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index 6b1e2efa5..2481a5765 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.scenes.home.mastodon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index ba22feb19..0a5e0ebd2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -23,14 +23,14 @@ package com.twidere.twiderex.scenes.home.mastodon import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.services.mastodon.MastodonService import com.twidere.twiderex.component.TimelineComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 114968ce8..c2024a559 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -27,8 +27,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.services.microblog.NotificationService import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -37,6 +35,8 @@ import com.twidere.twiderex.component.foundation.Pager import com.twidere.twiderex.component.foundation.TextTabsComponent import com.twidere.twiderex.component.foundation.rememberPagerState import com.twidere.twiderex.component.lazy.LazyListController +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.scenes.home.AllNotificationItem diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index 51c2ddfd8..9284aae48 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -40,8 +40,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.paging.LoadState @@ -55,6 +53,8 @@ import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.refreshOrRetry diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index f36965705..cd7541ad5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -41,8 +41,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -52,6 +50,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index 3b652d112..a9874cb21 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -31,8 +31,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems @@ -41,6 +39,8 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiListsList +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt index 1fc6c0b71..29eb7f725 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt @@ -22,11 +22,11 @@ package com.twidere.twiderex.scenes.lists import androidx.compose.material.Text import androidx.compose.runtime.Composable -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index 35fa29cfe..2d9b188c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -45,8 +45,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -58,6 +56,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey @@ -145,7 +145,7 @@ fun ListTimeLineScene( ) { Text( text = stringResource( - id = if (following) + res = if (following) com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_unfollow else com.twidere.twiderex.MR.strings.scene_lists_details_menu_actions_follow diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index 3047beb9d..bd38c28da 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -26,10 +26,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt index 059beacbf..0b131cfd0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt @@ -25,10 +25,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.MastodonListsModifyComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index bc4465d60..2c66f1e72 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -36,13 +36,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.TwitterListsModifyComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt index df61cdb5b..9325d70ee 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt @@ -32,13 +32,13 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Done import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.LoadingProgress import com.twidere.twiderex.component.lists.TwitterListsModifyComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt index ed539315b..67094148e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt @@ -45,14 +45,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.SignInButton import com.twidere.twiderex.component.foundation.SignInScaffold +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.LocalNavController diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index d8ba715e0..f324fa9ad 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -43,8 +43,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.GraphicsLayerScope -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue @@ -53,6 +51,8 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.TextInput import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt index d78b16235..5c1672c85 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt @@ -45,8 +45,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.google.accompanist.pager.ExperimentalPagerApi @@ -58,6 +56,8 @@ import com.twidere.twiderex.component.foundation.AppBarDefaults import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt index 0ddaf7578..c8eaecaef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt @@ -27,12 +27,12 @@ import androidx.compose.material.ListItem import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.items import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.viewmodel.mastodon.MastodonSearchHashtagViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt index a7ad7954b..63ab9d759 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable -import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusList +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.viewmodel.search.SearchTweetsViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt index e3e39906c..f25ce7938 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt @@ -21,12 +21,12 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable -import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiUserList import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.viewmodel.search.SearchUserViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index 4b69aecb5..9e7029ae6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -22,11 +22,11 @@ package com.twidere.twiderex.scenes.search.tabs import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import com.twidere.twiderex.component.stringResource import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.twidere.twiderex.component.foundation.SwipeToRefreshLayout import com.twidere.twiderex.component.lazy.ui.LazyUiStatusImageList +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.preferences.model.DisplayPreferences diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt index 2be54cfac..523523461 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt @@ -50,8 +50,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.BuildConfig import com.twidere.twiderex.component.LoginLogo @@ -62,6 +60,8 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.foundation.ParallaxLayout import com.twidere.twiderex.component.foundation.rememberParallaxLayoutState import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.navigation.RootDeepLinksRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt index f6a7748c2..87c2971b6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt @@ -39,13 +39,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccountViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt index 2275a2803..45204efa2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt @@ -37,7 +37,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.BuildConfig import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -45,6 +44,7 @@ import com.twidere.twiderex.component.foundation.ColoredSwitch import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index 11bfdce17..fcfc99b4b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -53,7 +53,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -63,11 +62,12 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.component.status.UserAvatarDefaults +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel -import com.twidere.twiderex.extensions.isDarkTheme import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences import com.twidere.twiderex.ui.TwidereScene +import com.twidere.twiderex.ui.isDarkTheme import com.twidere.twiderex.ui.primaryColors import com.twidere.twiderex.viewmodel.settings.AppearanceViewModel diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 38c534b5a..85acf560c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -38,7 +38,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.action.FakeStatusActions import com.twidere.twiderex.action.LocalStatusActions @@ -52,6 +51,7 @@ import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.component.status.TimelineStatusComponent +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.preferences.LocalDisplayPreferences diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 6b6b167b2..aaafb3343 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -52,15 +52,15 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.navigation.LocalNavigator +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.settings.RadioItem import com.twidere.twiderex.component.settings.switchItem +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.preferences.model.MiscPreferences diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index fd4836f74..c0e5583dd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -36,7 +36,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.ColoredSwitch @@ -45,6 +44,7 @@ import com.twidere.twiderex.component.lazy.ItemHeader import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.navigation.RootRoute diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt index a7f16720f..8ecc4335f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt @@ -32,11 +32,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.painterResource +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 8b210a142..262a39d9b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -32,12 +32,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt index 3a44b0d39..7ddad4ea0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt @@ -22,11 +22,11 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt index 718f7a2c4..b26183c9d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt @@ -22,11 +22,11 @@ package com.twidere.twiderex.scenes.user import androidx.compose.material.Text import androidx.compose.runtime.Composable -import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.UserListComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.ui.TwidereScene diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index 6ced912d4..b5a58afd9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -25,14 +25,14 @@ import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import com.twidere.twiderex.component.painterResource -import com.twidere.twiderex.component.stringResource import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.UserComponent import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold +import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.UserName +import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt index db2dddeb7..e267f1507 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt @@ -24,7 +24,6 @@ import androidx.compose.material.ContentAlpha import androidx.compose.material.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import com.twidere.twiderex.extensions.isDarkTheme import com.twidere.twiderex.preferences.LocalAppearancePreferences val mediumEmphasisContentContentColor: Color diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt index fbe46da0e..57bbd2fd8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.google.accompanist.insets.HorizontalSide import com.google.accompanist.insets.Insets @@ -268,7 +269,7 @@ private fun provideSystemInsets( } @Composable -private fun isDarkTheme(requireDarkTheme: Boolean): Boolean { +private fun isDarkTheme(requireDarkTheme: Boolean = false): Boolean { val appearance = LocalAppearancePreferences.current val theme = appearance.theme return if (requireDarkTheme) { @@ -289,14 +290,14 @@ private fun provideTypography(): Typography { val useSystemFontSize = display.useSystemFontSize val fontScale = display.fontScale val baseTypography = Typography() - // classical text rendering (like the old twidere) - // .let { - // it.copy( - // body1 = it.body1.copy(fontSize = 14.sp, letterSpacing = 0.sp), - // body2 = it.body2.copy(letterSpacing = 0.sp), - // caption = it.caption.copy(letterSpacing = 0.sp), - // ) - // } + // classical text rendering (like the old twidere) + .let { + it.copy( + body1 = it.body1.copy(fontSize = 14.sp, letterSpacing = 0.sp), + body2 = it.body2.copy(letterSpacing = 0.sp), + caption = it.caption.copy(letterSpacing = 0.sp), + ) + } return if (useSystemFontSize) { baseTypography } else { diff --git a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt new file mode 100644 index 000000000..843702170 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt @@ -0,0 +1,137 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.nestedscrollview + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.rememberScrollableState +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.unit.Constraints +import kotlin.math.roundToInt + +/** + * Define a [VerticalNestedScrollView]. + * + * @param state the state object to be used to observe the [VerticalNestedScrollView] state. + * @param modifier the modifier to apply to this layout. + * @param content a block which describes the header. + * @param content a block which describes the content. + */ +@Composable +fun VerticalNestedScrollView( + modifier: Modifier = Modifier, + state: NestedScrollViewState, + header: @Composable () -> Unit = {}, + content: @Composable () -> Unit = {}, +) { + NestedScrollView( + modifier = modifier, + state = state, + orientation = Orientation.Vertical, + header = header, + content = content, + ) +} + +/** + * Define a [HorizontalNestedScrollView]. + * + * @param state the state object to be used to observe the [HorizontalNestedScrollView] state. + * @param modifier the modifier to apply to this layout. + * @param content a block which describes the header. + * @param content a block which describes the content. + */ +@Composable +fun HorizontalNestedScrollView( + modifier: Modifier = Modifier, + state: NestedScrollViewState, + header: @Composable () -> Unit = {}, + content: @Composable () -> Unit = {}, +) { + NestedScrollView( + modifier = modifier, + state = state, + orientation = Orientation.Horizontal, + header = header, + content = content, + ) +} + +@Composable +private fun NestedScrollView( + modifier: Modifier = Modifier, + state: NestedScrollViewState, + orientation: Orientation, + header: @Composable () -> Unit, + content: @Composable () -> Unit, +) { + Layout( + modifier = modifier + .scrollable( + orientation = orientation, + state = rememberScrollableState { + state.drag(it) + } + ) + .nestedScroll(state.nestedScrollConnectionHolder), + content = { + Box { + header.invoke() + } + Box { + content.invoke() + } + }, + ) { measurables, constraints -> + layout(constraints.maxWidth, constraints.maxHeight) { + when (orientation) { + Orientation.Vertical -> { + val headerPlaceable = + measurables[0].measure(constraints.copy(maxHeight = Constraints.Infinity)) + headerPlaceable.place(0, state.offset.roundToInt()) + state.updateBounds(-(headerPlaceable.height.toFloat())) + val contentPlaceable = + measurables[1].measure(constraints.copy(maxHeight = constraints.maxHeight)) + contentPlaceable.place( + 0, + state.offset.roundToInt() + headerPlaceable.height + ) + } + Orientation.Horizontal -> { + val headerPlaceable = + measurables[0].measure(constraints.copy(maxWidth = Constraints.Infinity)) + headerPlaceable.place(state.offset.roundToInt(), 0) + state.updateBounds(-(headerPlaceable.width.toFloat())) + val contentPlaceable = + measurables[1].measure(constraints.copy(maxWidth = constraints.maxWidth)) + contentPlaceable.place( + state.offset.roundToInt() + headerPlaceable.width, + 0, + ) + } + } + } + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt new file mode 100644 index 000000000..b504d5a22 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt @@ -0,0 +1,177 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.nestedscrollview + +import androidx.annotation.FloatRange +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.exponentialDecay +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.unit.Velocity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlin.math.absoluteValue +import kotlin.math.withSign + +/** + * Create a [NestedScrollViewState] that is remembered across compositions. + */ +@Composable +fun rememberNestedScrollViewState(): NestedScrollViewState { + val scope = rememberCoroutineScope() + val saver = remember { + NestedScrollViewState.Saver(scope = scope) + } + return rememberSaveable( + saver = saver + ) { + NestedScrollViewState(scope = scope) + } +} + +/** + * A state object that can be hoisted to observe scale and translate for [NestedScrollView]. + * + * In most cases, this will be created via [rememberNestedScrollViewState]. + */ +@Stable +class NestedScrollViewState( + private val scope: CoroutineScope, + initialOffset: Float = 0f, + initialMaxOffset: Float = 0f, +) { + companion object { + fun Saver( + scope: CoroutineScope, + ): Saver = listSaver( + save = { + listOf(it.offset, it._maxOffset.value) + }, + restore = { + NestedScrollViewState( + scope = scope, + initialOffset = it[0], + initialMaxOffset = it[1], + ) + } + ) + } + + /** + * The maximum value for [NestedScrollView] Content to translate + */ + @get:FloatRange(from = 0.0) + val maxOffset: Float + get() = _maxOffset.value.absoluteValue + + /** + * The current value for [NestedScrollView] Content translate + */ + @get:FloatRange(from = 0.0) + val offset: Float + get() = _offset.value + + internal val nestedScrollConnectionHolder = object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + return takeIf { + available.y < 0 && source == NestedScrollSource.Drag + }?.let { + Offset(0f, drag(available.y)) + } ?: Offset.Zero + } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset { + return takeIf { + available.y > 0 && source == NestedScrollSource.Drag + }?.let { + Offset(0f, drag(available.y)) + } ?: Offset.Zero + } + + override suspend fun onPreFling(available: Velocity): Velocity { + return Velocity(0f, fling(available.y)) + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + return Velocity(0f, fling(available.y)) + } + } + + private var changes = 0f + private var _offset = Animatable(initialOffset) + private val _maxOffset = mutableStateOf(initialMaxOffset) + + private suspend fun snapTo(value: Float) { + _offset.snapTo(value) + } + + internal suspend fun fling(velocity: Float): Float { + if (velocity == 0f || velocity > 0 && offset == 0f) { + return velocity + } + val realVelocity = velocity.withSign(changes) + changes = 0f + return if (offset > _maxOffset.value && offset <= 0) { + _offset.animateDecay( + realVelocity, + exponentialDecay() + ).endState.velocity.let { + if (offset == 0f) { + velocity + } else { + it + } + } + } else { + 0f + } + } + + internal fun drag(delta: Float): Float { + return if (delta < 0 && offset > _maxOffset.value || delta > 0 && offset < 0f) { + changes = delta + scope.launch { + snapTo((offset + delta).coerceIn(_maxOffset.value, 0f)) + } + delta + } else { + 0f + } + } + + internal fun updateBounds(maxOffset: Float) { + _maxOffset.value = maxOffset + _offset.updateBounds(maxOffset, 0f) + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt b/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt new file mode 100644 index 000000000..ef8b4a669 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt @@ -0,0 +1,134 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.placeholder + +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.foundation.text.appendInlineContent +import androidx.compose.material.LocalTextStyle +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.PlaceholderVerticalAlign +import androidx.compose.ui.text.buildAnnotatedString +import kotlinx.coroutines.delay + +private const val ID_PlaceHolder = "placeholder" + +@Composable +fun TextPlaceHolder( + modifier: Modifier = Modifier, + length: Int, + durationMillis: Int = PlaceholderConstants.DefaultDurationMillis, + delayMillis: Long = 0, + color: PlaceholderColors = PlaceholderConstants.DefaultColor, +) { + val value = buildAnnotatedString { + repeat(length) { + appendInlineContent(ID_PlaceHolder) + } + } + Text( + modifier = modifier, + text = value, + inlineContent = mapOf( + ID_PlaceHolder to InlineTextContent( + androidx.compose.ui.text.Placeholder( + width = LocalTextStyle.current.fontSize, + height = LocalTextStyle.current.fontSize, + placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter + ) + ) { + Placeholder( + modifier = Modifier.fillMaxSize(), + durationMillis = durationMillis, + delayMillis = delayMillis, + color = color, + ) + }, + ) + ) +} + +@Composable +fun Placeholder( + modifier: Modifier, + durationMillis: Int = PlaceholderConstants.DefaultDurationMillis, + delayMillis: Long = 0, + color: PlaceholderColors = PlaceholderConstants.DefaultColor, +) { + var started by rememberSaveable(delayMillis) { + mutableStateOf(delayMillis == 0L) + } + if (!started) { + LaunchedEffect(delayMillis) { + delay(delayMillis) + started = true + } + } + val colorAnimation = if (started) { + val transition = rememberInfiniteTransition() + val colorAnimation by transition.animateColor( + initialValue = color.start, + targetValue = color.end, + animationSpec = infiniteRepeatable( + animation = tween( + durationMillis = durationMillis, + easing = CubicBezierEasing(1.0f, 0.0f, 0.8f, 0.3f) + ), + repeatMode = RepeatMode.Reverse + ) + ) + colorAnimation + } else { + color.start + } + + Box(modifier = modifier.background(color = colorAnimation)) +} + +object PlaceholderConstants { + const val DefaultDurationMillis: Int = 1500 + val DefaultColor = PlaceholderColors( + start = Color.Black.copy(alpha = 0.1f), + end = Color.Black.copy(alpha = 0.025f) + ) +} + +data class PlaceholderColors( + val start: Color, + val end: Color, +) diff --git a/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt b/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt new file mode 100644 index 000000000..95f617ae2 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt @@ -0,0 +1,94 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.swiper + +import androidx.compose.foundation.gestures.DraggableState +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Layout +import kotlinx.coroutines.launch +import kotlin.math.roundToInt + +@Composable +fun Swiper( + modifier: Modifier = Modifier, + state: SwiperState, + orientation: Orientation = Orientation.Vertical, + enabled: Boolean = true, + reverseDirection: Boolean = false, + content: @Composable () -> Unit, +) { + val scope = rememberCoroutineScope() + BoxWithConstraints( + modifier = modifier, + ) { + LaunchedEffect(constraints.maxHeight) { + state.maxHeight = constraints.maxHeight + } + Layout( + modifier = Modifier.draggable( + orientation = orientation, + enabled = enabled && !state.dismissed, + reverseDirection = reverseDirection, + onDragStopped = { velocity -> + scope.launch { + state.fling(velocity) + } + }, + onDragStarted = { + state.onStart.invoke() + }, + state = DraggableState { dy -> + scope.launch { + with(state) { + snap(offset + dy) + } + } + }, + ), + content = content, + ) { measurables, constraints -> + layout(constraints.maxWidth, constraints.maxHeight) { + val offset = state.offset + val childConstraints = constraints.copy(minWidth = 0, minHeight = 0) + measurables + .map { + it.measure(childConstraints) + } + .forEach { placeable -> + // TODO: current this centers each page. We should investigate reading + // gravity modifiers on the child, or maybe as a param to Swiper. + val xCenterOffset = (constraints.maxWidth - placeable.width) / 2 + val yCenterOffset = (constraints.maxHeight - placeable.height) / 2 + placeable.place( + x = xCenterOffset, + y = yCenterOffset + offset.roundToInt(), + ) + } + } + } + } +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt b/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt new file mode 100644 index 000000000..a775b4f88 --- /dev/null +++ b/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt @@ -0,0 +1,129 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package moe.tlaster.swiper + +import androidx.compose.animation.core.Animatable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import kotlin.math.absoluteValue +import kotlin.math.withSign + +@Composable +fun rememberSwiperState( + onStart: () -> Unit = {}, + onDismiss: () -> Unit = {}, + onEnd: () -> Unit = {}, +): SwiperState { + return rememberSaveable( + saver = SwiperState.Saver( + onStart, onDismiss, onEnd + ) + ) { + SwiperState( + onStart = onStart, + onDismiss = onDismiss, + onEnd = onEnd, + ) + } +} + +@Stable +class SwiperState( + internal val onStart: () -> Unit = {}, + internal val onDismiss: () -> Unit = {}, + internal val onEnd: () -> Unit = {}, + initialOffset: Float = 0f, +) { + internal var maxHeight: Int = 0 + set(value) { + field = value + _offset.updateBounds(lowerBound = -value.toFloat(), upperBound = value.toFloat()) + } + internal var dismissed by mutableStateOf(false) + private var _offset = Animatable(initialOffset) + + val offset: Float + get() = _offset.value + + val progress: Float + get() = (offset.absoluteValue / (if (maxHeight == 0) 1 else maxHeight)).coerceIn( + maximumValue = 1f, + minimumValue = 0f + ) + + internal suspend fun snap(value: Float) { + _offset.snapTo(value) + } + + internal suspend fun fling(velocity: Float) { + val value = _offset.value + when { + velocity.absoluteValue > 4000f -> { + dismiss(velocity) + } + value.absoluteValue < maxHeight * 0.5 -> { + restore() + } + value.absoluteValue < maxHeight -> { + dismiss(velocity) + } + } + } + + private suspend fun dismiss(velocity: Float) { + dismissed = true + _offset.animateTo(maxHeight.toFloat().withSign(_offset.value), initialVelocity = velocity) + onDismiss.invoke() + } + + private suspend fun restore() { + onEnd.invoke() + _offset.animateTo(0f) + } + + companion object { + fun Saver( + onStart: () -> Unit = {}, + onDismiss: () -> Unit = {}, + onEnd: () -> Unit = {}, + ): Saver = listSaver( + save = { + listOf( + it.offset, + ) + }, + restore = { + SwiperState( + onStart = onStart, + onDismiss = onDismiss, + onEnd = onEnd, + initialOffset = it[0], + ) + } + ) + } +} From e2be99b3d72998eb9310660ab341a02325e51809 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Sep 2021 15:54:18 +0800 Subject: [PATCH 263/615] rename sqldelight tables and opt transform test --- .../adapter/DMConversationAdapterFactory.kt | 4 +- .../adapter/DMEventAdapterFactory.kt | 4 +- .../sqldelight/adapter/ListAdapterFactory.kt | 4 +- .../sqldelight/adapter/MediaAdapterFactory.kt | 4 +- .../sqldelight/adapter/TrendAdapterFactory.kt | 8 +-- .../adapter/UrlEntityAdapterFactory.kt | 4 +- .../sqldelight/adapter/UserAdapterFactory.kt | 4 +- .../twiderex/db/sqldelight/database.kt | 16 ++--- .../model/DbDMConversationWithEvent.kt | 8 +-- .../model/DbDMEventWithAttachments.kt | 18 ++--- .../db/sqldelight/model/DbStatusElements.kt | 72 +++++++++++++++++++ .../db/sqldelight/model/DbTrendWithHistory.kt | 10 +-- .../transform/DMConversationTransform.kt | 6 +- .../sqldelight/transform/DMEventTransform.kt | 4 +- .../db/sqldelight/transform/ListTransform.kt | 6 +- .../db/sqldelight/transform/MediaTransform.kt | 6 +- .../db/sqldelight/transform/TrendTransform.kt | 10 +-- .../transform/UrlEntityTransform.kt | 6 +- .../db/sqldelight/transform/UserTransform.kt | 6 +- .../model/ui/mastodon/MastodonStatusExtra.kt | 3 + .../model/ui/twitter/TwitterStatusExtra.kt | 2 + .../twiderex/sqldelight/table/CacheDrop.sq | 16 ++--- .../sqldelight/table/DMConversation.sq | 10 +-- .../twiderex/sqldelight/table/DMEvent.sq | 28 ++++---- .../twidere/twiderex/sqldelight/table/List.sq | 16 ++--- .../twiderex/sqldelight/table/Media.sq | 8 +-- .../twiderex/sqldelight/table/Trend.sq | 12 ++-- .../twiderex/sqldelight/table/TrendHistory.sq | 10 +-- .../twiderex/sqldelight/table/UrlEntity.sq | 8 +-- .../twidere/twiderex/sqldelight/table/User.sq | 6 +- .../db/sqldelight/TrendQueriesImplTest.kt | 4 +- .../transform/DMEventTransformTest.kt | 25 +------ .../transform/DraftTransformTest.kt | 32 +-------- .../sqldelight/transform/ListTransformTest.kt | 19 +---- .../transform/MediaTransformTest.kt | 39 +--------- .../NotificationCursorTransformTest.kt | 14 +--- .../transform/SearchTransformTest.kt | 22 +----- .../transform/TrendTransformTest.kt | 19 +---- .../transform/UrlEntityTransformTest.kt | 33 +-------- .../sqldelight/transform/UserTransformTest.kt | 27 +------ 40 files changed, 214 insertions(+), 339 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt index 94f5e3f1e..980ad4a2a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.EnumColumnAdapter -import com.twidere.twiderex.sqldelight.table.DMConversation +import com.twidere.twiderex.sqldelight.table.DbDMConversation object DMConversationAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - DMConversation.Adapter( + DbDMConversation.Adapter( accountKeyAdapter = it, conversationKeyAdapter = it, recipientKeyAdapter = it, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt index 88da73a89..6a422769e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.EnumColumnAdapter -import com.twidere.twiderex.sqldelight.table.DMEvent +import com.twidere.twiderex.sqldelight.table.DbDMEvent object DMEventAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - DMEvent.Adapter( + DbDMEvent.Adapter( accountKeyAdapter = it, conversationKeyAdapter = it, messageKeyAdapter = it, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt index 5388dc79a..c471d6e88 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt @@ -20,11 +20,11 @@ */ package com.twidere.twiderex.db.sqldelight.adapter -import com.twidere.twiderex.sqldelight.table.List +import com.twidere.twiderex.sqldelight.table.DbList object ListAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - List.Adapter( + DbList.Adapter( accountKeyAdapter = it, listKeyAdapter = it, ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt index 7014d91c3..7983adf37 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt @@ -21,10 +21,10 @@ package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.EnumColumnAdapter -import com.twidere.twiderex.sqldelight.table.Media +import com.twidere.twiderex.sqldelight.table.DbMedia internal object MediaAdapterFactory { - fun create() = Media.Adapter( + fun create() = DbMedia.Adapter( belongToKeyAdapter = MicroBlogKeyColumnAdapter(), typeAdapter = EnumColumnAdapter() ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt index 7d571cfc7..4aac1b040 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt @@ -20,12 +20,12 @@ */ package com.twidere.twiderex.db.sqldelight.adapter -import com.twidere.twiderex.sqldelight.table.Trend -import com.twidere.twiderex.sqldelight.table.TrendHistory +import com.twidere.twiderex.sqldelight.table.DbTrend +import com.twidere.twiderex.sqldelight.table.DbTrendHistory object TrendAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - Trend.Adapter( + DbTrend.Adapter( trendKeyAdapter = it, accountKeyAdapter = it, ) @@ -34,7 +34,7 @@ object TrendAdapterFactory { object TrendHistoryAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - TrendHistory.Adapter( + DbTrendHistory.Adapter( trendKeyAdapter = it, accountKeyAdapter = it, ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt index 986d26767..4bc789db1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt @@ -20,10 +20,10 @@ */ package com.twidere.twiderex.db.sqldelight.adapter -import com.twidere.twiderex.sqldelight.table.UrlEntity +import com.twidere.twiderex.sqldelight.table.DbUrlEntity internal object UrlEntityAdapterFactory { - fun create() = UrlEntity.Adapter( + fun create() = DbUrlEntity.Adapter( belongToKeyAdapter = MicroBlogKeyColumnAdapter(), ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt index 25ebf943e..8c8ffe466 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt @@ -21,11 +21,11 @@ package com.twidere.twiderex.db.sqldelight.adapter import com.squareup.sqldelight.EnumColumnAdapter -import com.twidere.twiderex.sqldelight.table.User +import com.twidere.twiderex.sqldelight.table.DbUser internal object UserAdapterFactory { fun create() = MicroBlogKeyColumnAdapter().let { - User.Adapter( + DbUser.Adapter( userKeyAdapter = it, acctAdapter = it, platformTypeAdapter = EnumColumnAdapter(), diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt index f0143f219..3419336ac 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -48,14 +48,14 @@ internal fun createCacheDataBase(driver: SqlDriver): SqlDelightCacheDatabase { SqlDelightCacheDatabase.Schema.create(driver) return SqlDelightCacheDatabase( driver = driver, - DMEventAdapter = DMEventAdapterFactory.create(), - MediaAdapter = MediaAdapterFactory.create(), - UrlEntityAdapter = UrlEntityAdapterFactory.create(), - UserAdapter = UserAdapterFactory.create(), - DMConversationAdapter = DMConversationAdapterFactory.create(), - TrendAdapter = TrendAdapterFactory.create(), - TrendHistoryAdapter = TrendHistoryAdapterFactory.create(), + DbDMEventAdapter = DMEventAdapterFactory.create(), + DbMediaAdapter = MediaAdapterFactory.create(), + DbUrlEntityAdapter = UrlEntityAdapterFactory.create(), + DbUserAdapter = UserAdapterFactory.create(), + DbDMConversationAdapter = DMConversationAdapterFactory.create(), + DbTrendAdapter = TrendAdapterFactory.create(), + DbTrendHistoryAdapter = TrendHistoryAdapterFactory.create(), DbNotificationCursorAdapter = NotificationCursorAdapterFactory.create(), - ListAdapter = ListAdapterFactory.create() + DbListAdapter = ListAdapterFactory.create() ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt index cce6a8149..c1b199a3c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt @@ -22,15 +22,15 @@ package com.twidere.twiderex.db.sqldelight.model import com.twidere.twiderex.db.sqldelight.model.DbDMEventWithAttachments.Companion.withAttachments import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase -import com.twidere.twiderex.sqldelight.table.DMConversation -import com.twidere.twiderex.sqldelight.table.DMEvent +import com.twidere.twiderex.sqldelight.table.DbDMConversation +import com.twidere.twiderex.sqldelight.table.DbDMEvent internal data class DbDMConversationWithEvent( val event: DbDMEventWithAttachments, - val conversation: DMConversation + val conversation: DbDMConversation ) { companion object { - fun DMEvent.toDbDMConversationWithEvent(database: SqlDelightCacheDatabase): DbDMConversationWithEvent { + fun DbDMEvent.toDbDMConversationWithEvent(database: SqlDelightCacheDatabase): DbDMConversationWithEvent { return withAttachments(database) .let { DbDMConversationWithEvent( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt index b7a60e09d..7871c5c2e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt @@ -21,19 +21,19 @@ package com.twidere.twiderex.db.sqldelight.model import com.twidere.twiderex.sqldelight.SqlDelightCacheDatabase -import com.twidere.twiderex.sqldelight.table.DMEvent -import com.twidere.twiderex.sqldelight.table.Media -import com.twidere.twiderex.sqldelight.table.UrlEntity -import com.twidere.twiderex.sqldelight.table.User +import com.twidere.twiderex.sqldelight.table.DbDMEvent +import com.twidere.twiderex.sqldelight.table.DbMedia +import com.twidere.twiderex.sqldelight.table.DbUrlEntity +import com.twidere.twiderex.sqldelight.table.DbUser internal data class DbDMEventWithAttachments( - val event: DMEvent, - val media: List, - val url: List, - val sender: User + val event: DbDMEvent, + val media: List, + val url: List, + val sender: DbUser ) { companion object { - fun DMEvent.withAttachments(database: SqlDelightCacheDatabase): DbDMEventWithAttachments { + fun DbDMEvent.withAttachments(database: SqlDelightCacheDatabase): DbDMEventWithAttachments { return database.transactionWithResult { DbDMEventWithAttachments( event = this@withAttachments, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt new file mode 100644 index 000000000..119a48575 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt @@ -0,0 +1,72 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.db.sqldelight.model + +import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.enums.ReferenceType +import kotlinx.serialization.Serializable + +@Serializable +internal data class DbStatusReference( + val referenceType: ReferenceType, + val statusKey: MicroBlogKey, +) + +@Serializable +internal data class DbStatusReaction( + var liked: Boolean, + var retweeted: Boolean, +) + +@Serializable +internal data class DbGeo( + val name: String, + val lat: Long? = null, + val long: Long? = null, +) + +@Serializable +internal data class DbCard( + val link: String, + val displayLink: String?, + val title: String?, + val description: String?, + val image: String?, +) + +@Serializable +internal data class UiPoll( + val id: String, + val options: List
    +?tIXOe@) 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 diff --git a/android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-mdpi-v4/ic_mastodon_logo_blue.png deleted file mode 100644 index 08d4001722680ed0df943c2ef26aad14ad48eb16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1035 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 TOLOSe00000NkvXXu0mjfpxB*^ diff --git a/android/src/main/res/drawable-mdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-mdpi/ic_profile_image_twidere.png deleted file mode 100644 index 63d3af8964ef9a12f7945be0fb6f93d946cf6fde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3807 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 V!+F3=qj~@U002ovPDHLkV1lYARTTgL diff --git a/android/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml b/android/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml deleted file mode 100644 index dced2a302..000000000 --- a/android/src/main/res/drawable-v24/ic_mastodon_logo_blue.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/android/src/main/res/drawable-v24/ic_mastodon_logo_white.xml b/android/src/main/res/drawable-v24/ic_mastodon_logo_white.xml deleted file mode 100644 index 42be7aab6..000000000 --- a/android/src/main/res/drawable-v24/ic_mastodon_logo_white.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xhdpi-v4/ic_mastodon_logo_blue.png deleted file mode 100644 index 697db3cd5eec4f2ce053c53490ac47cc2848b2ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1965 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 diff --git a/android/src/main/res/drawable-xhdpi/featured_graphics.png b/android/src/main/res/drawable-xhdpi/featured_graphics.png deleted file mode 100644 index b19e5fe35fadfa2326641261fd6a1cc559cb747e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54684 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 diff --git a/android/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xhdpi/ic_profile_image_twidere.png deleted file mode 100644 index efa3f5f4d977de0e17bb7a1742b269a65e1bf3dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11345 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 T$qh8S00000NkvXXu0mjf_rSwa diff --git a/android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xxhdpi-v4/ic_mastodon_logo_blue.png deleted file mode 100644 index 6900ca0aef8f4bf6c99df61c7d8e9ad9f76ff052..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2976 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

    +9^}7-nV-N&gH#P%F(}l22k*L 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 Ef*=lvs{jB1 diff --git a/android/src/main/res/drawable/ic_about_gray_logo.xml b/android/src/main/res/drawable/ic_about_gray_logo.xml deleted file mode 100644 index 291ae2151..000000000 --- a/android/src/main/res/drawable/ic_about_gray_logo.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_about_gray_logo_shadow.xml b/android/src/main/res/drawable/ic_about_gray_logo_shadow.xml deleted file mode 100644 index 010db3129..000000000 --- a/android/src/main/res/drawable/ic_about_gray_logo_shadow.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_add.xml b/android/src/main/res/drawable/ic_add.xml deleted file mode 100644 index 03ece2493..000000000 --- a/android/src/main/res/drawable/ic_add.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_add_colored.xml b/android/src/main/res/drawable/ic_add_colored.xml deleted file mode 100644 index 00de90654..000000000 --- a/android/src/main/res/drawable/ic_add_colored.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_adjustments_horizontal.xml b/android/src/main/res/drawable/ic_adjustments_horizontal.xml deleted file mode 100644 index 87c1ff1f4..000000000 --- a/android/src/main/res/drawable/ic_adjustments_horizontal.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_alert.xml b/android/src/main/res/drawable/ic_alert.xml deleted file mode 100644 index 5a27847f4..000000000 --- a/android/src/main/res/drawable/ic_alert.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_alert_octagon.xml b/android/src/main/res/drawable/ic_alert_octagon.xml deleted file mode 100644 index 440fa1f5c..000000000 --- a/android/src/main/res/drawable/ic_alert_octagon.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_alert_triangle.xml b/android/src/main/res/drawable/ic_alert_triangle.xml deleted file mode 100644 index 078b74a5f..000000000 --- a/android/src/main/res/drawable/ic_alert_triangle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_at_sign.xml b/android/src/main/res/drawable/ic_at_sign.xml deleted file mode 100644 index 18cddbe3e..000000000 --- a/android/src/main/res/drawable/ic_at_sign.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_bell.xml b/android/src/main/res/drawable/ic_bell.xml deleted file mode 100644 index 770c44b60..000000000 --- a/android/src/main/res/drawable/ic_bell.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_bell_ringing.xml b/android/src/main/res/drawable/ic_bell_ringing.xml deleted file mode 100644 index f1f24dbfb..000000000 --- a/android/src/main/res/drawable/ic_bell_ringing.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_blockquote.xml b/android/src/main/res/drawable/ic_blockquote.xml deleted file mode 100644 index 4b814f496..000000000 --- a/android/src/main/res/drawable/ic_blockquote.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_browser.xml b/android/src/main/res/drawable/ic_browser.xml deleted file mode 100644 index baeb6fda6..000000000 --- a/android/src/main/res/drawable/ic_browser.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_camera.xml b/android/src/main/res/drawable/ic_camera.xml deleted file mode 100644 index d5fbb3730..000000000 --- a/android/src/main/res/drawable/ic_camera.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_corner_up_left.xml b/android/src/main/res/drawable/ic_corner_up_left.xml deleted file mode 100644 index d39ad28de..000000000 --- a/android/src/main/res/drawable/ic_corner_up_left.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_cw.xml b/android/src/main/res/drawable/ic_cw.xml deleted file mode 100644 index 0bd51d024..000000000 --- a/android/src/main/res/drawable/ic_cw.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_database.xml b/android/src/main/res/drawable/ic_database.xml deleted file mode 100644 index dff7bb7e3..000000000 --- a/android/src/main/res/drawable/ic_database.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_delete_colored.xml b/android/src/main/res/drawable/ic_delete_colored.xml deleted file mode 100644 index 69f990a9a..000000000 --- a/android/src/main/res/drawable/ic_delete_colored.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_device_floppy.xml b/android/src/main/res/drawable/ic_device_floppy.xml deleted file mode 100644 index 1b3601ebf..000000000 --- a/android/src/main/res/drawable/ic_device_floppy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_dots_circle_horiz.xml b/android/src/main/res/drawable/ic_dots_circle_horiz.xml deleted file mode 100644 index 280a9a987..000000000 --- a/android/src/main/res/drawable/ic_dots_circle_horiz.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_dots_vertical.xml b/android/src/main/res/drawable/ic_dots_vertical.xml deleted file mode 100644 index b5f79b338..000000000 --- a/android/src/main/res/drawable/ic_dots_vertical.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_draft_number.xml b/android/src/main/res/drawable/ic_draft_number.xml deleted file mode 100644 index 87c045cc9..000000000 --- a/android/src/main/res/drawable/ic_draft_number.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_drafts_more.xml b/android/src/main/res/drawable/ic_drafts_more.xml deleted file mode 100644 index 3fecc7191..000000000 --- a/android/src/main/res/drawable/ic_drafts_more.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_empty_column.xml b/android/src/main/res/drawable/ic_empty_column.xml deleted file mode 100644 index 393462ba5..000000000 --- a/android/src/main/res/drawable/ic_empty_column.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_empty_list.xml b/android/src/main/res/drawable/ic_empty_list.xml deleted file mode 100644 index 2d2cc7387..000000000 --- a/android/src/main/res/drawable/ic_empty_list.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_empty_status.xml b/android/src/main/res/drawable/ic_empty_status.xml deleted file mode 100644 index 5efaf20e5..000000000 --- a/android/src/main/res/drawable/ic_empty_status.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_expand_more.xml b/android/src/main/res/drawable/ic_expand_more.xml deleted file mode 100644 index 8532fd8cd..000000000 --- a/android/src/main/res/drawable/ic_expand_more.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_eye_off.xml b/android/src/main/res/drawable/ic_eye_off.xml deleted file mode 100644 index 0fcffecd7..000000000 --- a/android/src/main/res/drawable/ic_eye_off.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_feather.xml b/android/src/main/res/drawable/ic_feather.xml deleted file mode 100644 index df89fc8de..000000000 --- a/android/src/main/res/drawable/ic_feather.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_filter.xml b/android/src/main/res/drawable/ic_filter.xml deleted file mode 100644 index 05de1c368..000000000 --- a/android/src/main/res/drawable/ic_filter.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_float_left.xml b/android/src/main/res/drawable/ic_float_left.xml deleted file mode 100644 index ea7e512e1..000000000 --- a/android/src/main/res/drawable/ic_float_left.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_gif.xml b/android/src/main/res/drawable/ic_gif.xml deleted file mode 100644 index 87ef512f5..000000000 --- a/android/src/main/res/drawable/ic_gif.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_gif_tag.xml b/android/src/main/res/drawable/ic_gif_tag.xml deleted file mode 100644 index 003a25d7a..000000000 --- a/android/src/main/res/drawable/ic_gif_tag.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_github.xml b/android/src/main/res/drawable/ic_github.xml deleted file mode 100644 index 49e697b96..000000000 --- a/android/src/main/res/drawable/ic_github.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_globe.xml b/android/src/main/res/drawable/ic_globe.xml deleted file mode 100644 index a34919187..000000000 --- a/android/src/main/res/drawable/ic_globe.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_gray_logo_shadow.xml b/android/src/main/res/drawable/ic_gray_logo_shadow.xml deleted file mode 100644 index 6aa6c421f..000000000 --- a/android/src/main/res/drawable/ic_gray_logo_shadow.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/android/src/main/res/drawable/ic_hash.xml b/android/src/main/res/drawable/ic_hash.xml deleted file mode 100644 index 2f27f536c..000000000 --- a/android/src/main/res/drawable/ic_hash.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_heart.xml b/android/src/main/res/drawable/ic_heart.xml deleted file mode 100644 index 673c8b054..000000000 --- a/android/src/main/res/drawable/ic_heart.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_home.xml b/android/src/main/res/drawable/ic_home.xml deleted file mode 100644 index 9e73895c6..000000000 --- a/android/src/main/res/drawable/ic_home.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_info_circle.xml b/android/src/main/res/drawable/ic_info_circle.xml deleted file mode 100644 index 42e3c3b67..000000000 --- a/android/src/main/res/drawable/ic_info_circle.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_keyboard.xml b/android/src/main/res/drawable/ic_keyboard.xml deleted file mode 100644 index 2062b8868..000000000 --- a/android/src/main/res/drawable/ic_keyboard.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - diff --git a/android/src/main/res/drawable/ic_layout_sidebar.xml b/android/src/main/res/drawable/ic_layout_sidebar.xml deleted file mode 100644 index 93f26b1da..000000000 --- a/android/src/main/res/drawable/ic_layout_sidebar.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_lists.xml b/android/src/main/res/drawable/ic_lists.xml deleted file mode 100644 index e0344932f..000000000 --- a/android/src/main/res/drawable/ic_lists.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - diff --git a/android/src/main/res/drawable/ic_lock.xml b/android/src/main/res/drawable/ic_lock.xml deleted file mode 100644 index 3a8a2ca0b..000000000 --- a/android/src/main/res/drawable/ic_lock.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_lock_open.xml b/android/src/main/res/drawable/ic_lock_open.xml deleted file mode 100644 index 7622a9e96..000000000 --- a/android/src/main/res/drawable/ic_lock_open.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_mail.xml b/android/src/main/res/drawable/ic_mail.xml deleted file mode 100644 index d93c8f9ed..000000000 --- a/android/src/main/res/drawable/ic_mail.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_map_pin.xml b/android/src/main/res/drawable/ic_map_pin.xml deleted file mode 100644 index 0db441f91..000000000 --- a/android/src/main/res/drawable/ic_map_pin.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_mastodon_badge.xml b/android/src/main/res/drawable/ic_mastodon_badge.xml deleted file mode 100644 index 06d3e5ae3..000000000 --- a/android/src/main/res/drawable/ic_mastodon_badge.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_message_circle.xml b/android/src/main/res/drawable/ic_message_circle.xml deleted file mode 100644 index ff0c3b8d0..000000000 --- a/android/src/main/res/drawable/ic_message_circle.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_mood_smile.xml b/android/src/main/res/drawable/ic_mood_smile.xml deleted file mode 100644 index a3a938dc6..000000000 --- a/android/src/main/res/drawable/ic_mood_smile.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_note.xml b/android/src/main/res/drawable/ic_note.xml deleted file mode 100644 index ba31da97d..000000000 --- a/android/src/main/res/drawable/ic_note.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_photo.xml b/android/src/main/res/drawable/ic_photo.xml deleted file mode 100644 index feaac173a..000000000 --- a/android/src/main/res/drawable/ic_photo.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_planet.xml b/android/src/main/res/drawable/ic_planet.xml deleted file mode 100644 index 2fc2912ea..000000000 --- a/android/src/main/res/drawable/ic_planet.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_poll.xml b/android/src/main/res/drawable/ic_poll.xml deleted file mode 100644 index 948bb7b47..000000000 --- a/android/src/main/res/drawable/ic_poll.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_refresh.xml b/android/src/main/res/drawable/ic_refresh.xml deleted file mode 100644 index 0ef21b341..000000000 --- a/android/src/main/res/drawable/ic_refresh.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_repeat.xml b/android/src/main/res/drawable/ic_repeat.xml deleted file mode 100644 index 2cbeba626..000000000 --- a/android/src/main/res/drawable/ic_repeat.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_search.xml b/android/src/main/res/drawable/ic_search.xml deleted file mode 100644 index 3b9ac1d3f..000000000 --- a/android/src/main/res/drawable/ic_search.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_send.xml b/android/src/main/res/drawable/ic_send.xml deleted file mode 100644 index 87fef6869..000000000 --- a/android/src/main/res/drawable/ic_send.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_send_thread.xml b/android/src/main/res/drawable/ic_send_thread.xml deleted file mode 100644 index b33e22ed3..000000000 --- a/android/src/main/res/drawable/ic_send_thread.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_settings_notification.xml b/android/src/main/res/drawable/ic_settings_notification.xml deleted file mode 100644 index 072719262..000000000 --- a/android/src/main/res/drawable/ic_settings_notification.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_share.xml b/android/src/main/res/drawable/ic_share.xml deleted file mode 100644 index 86390e501..000000000 --- a/android/src/main/res/drawable/ic_share.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_shirt.xml b/android/src/main/res/drawable/ic_shirt.xml deleted file mode 100644 index 5a597dd5f..000000000 --- a/android/src/main/res/drawable/ic_shirt.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_telegram.xml b/android/src/main/res/drawable/ic_telegram.xml deleted file mode 100644 index ffc82e62b..000000000 --- a/android/src/main/res/drawable/ic_telegram.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_template.xml b/android/src/main/res/drawable/ic_template.xml deleted file mode 100644 index a8c1cd6eb..000000000 --- a/android/src/main/res/drawable/ic_template.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_thread_mode.xml b/android/src/main/res/drawable/ic_thread_mode.xml deleted file mode 100644 index 63a0f7c45..000000000 --- a/android/src/main/res/drawable/ic_thread_mode.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - diff --git a/android/src/main/res/drawable/ic_trash_can.xml b/android/src/main/res/drawable/ic_trash_can.xml deleted file mode 100644 index 42bbfb954..000000000 --- a/android/src/main/res/drawable/ic_trash_can.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_triangle_square_circle.xml b/android/src/main/res/drawable/ic_triangle_square_circle.xml deleted file mode 100644 index 448ad9c3a..000000000 --- a/android/src/main/res/drawable/ic_triangle_square_circle.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_twitter.xml b/android/src/main/res/drawable/ic_twitter.xml deleted file mode 100644 index 5ecb9c3d9..000000000 --- a/android/src/main/res/drawable/ic_twitter.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_twitter_badge.xml b/android/src/main/res/drawable/ic_twitter_badge.xml deleted file mode 100644 index 0458d9ffc..000000000 --- a/android/src/main/res/drawable/ic_twitter_badge.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_twitter_logo_white.xml b/android/src/main/res/drawable/ic_twitter_logo_white.xml deleted file mode 100644 index a37ba94d9..000000000 --- a/android/src/main/res/drawable/ic_twitter_logo_white.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/android/src/main/res/drawable/ic_user.xml b/android/src/main/res/drawable/ic_user.xml deleted file mode 100644 index 64f24b69a..000000000 --- a/android/src/main/res/drawable/ic_user.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_user_exclamation.xml b/android/src/main/res/drawable/ic_user_exclamation.xml deleted file mode 100644 index f4695f879..000000000 --- a/android/src/main/res/drawable/ic_user_exclamation.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_user_plus.xml b/android/src/main/res/drawable/ic_user_plus.xml deleted file mode 100644 index d3e117570..000000000 --- a/android/src/main/res/drawable/ic_user_plus.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_users.xml b/android/src/main/res/drawable/ic_users.xml deleted file mode 100644 index 30967465c..000000000 --- a/android/src/main/res/drawable/ic_users.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - diff --git a/android/src/main/res/drawable/ic_video.xml b/android/src/main/res/drawable/ic_video.xml deleted file mode 100644 index 860faca85..000000000 --- a/android/src/main/res/drawable/ic_video.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_volume.xml b/android/src/main/res/drawable/ic_volume.xml deleted file mode 100644 index f1e577fa7..000000000 --- a/android/src/main/res/drawable/ic_volume.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/android/src/main/res/drawable/ic_volume_mute.xml b/android/src/main/res/drawable/ic_volume_mute.xml deleted file mode 100644 index 21d81cbce..000000000 --- a/android/src/main/res/drawable/ic_volume_mute.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/android/src/main/res/drawable/ic_x.xml b/android/src/main/res/drawable/ic_x.xml deleted file mode 100644 index 2e3af8082..000000000 --- a/android/src/main/res/drawable/ic_x.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/common/src/commonMain/resources/MR/files/png/ic_launcher.png b/common/src/commonMain/resources/MR/files/png/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..43ba265190ea23280df667e5e6b6cfd084287a16 GIT binary patch literal 15323 zcmeIZ`9IWO_&=jC5oAv z%DzM;yD73WSqEcY-{W)N_v8K-?#KQ3{4zXvz0SGLbD!f)^EdW5+ z+>CMr037@k2MGM|$87NTB>)T`n^O+h2fIx4EaapPX0iVb#RnJH8fE6)i4c!y8=F1N z@5LXPfs81+A)gN(wvUnDFmCb2q&>#O{%&;UWxn@YGkdIeA5Q+&8e7Yo4tq5fkfZ%u zpRb{NAwz#~iOUY3s1o|*%UBGb^mA^<1g#4s0q9S|j&ei+AWtYmGknDm0MJ_m_^TWR ze)KD@+9*JTA9{D-2jc)9eu^l;kN{5&YD~t5kJhY4Fb=Z=G#|faWK+ z9-r>23wIkX*_t(6kBi9J3EYCkfU4NKcJm<1BbWDwKIAUEq9)V#aW$vQOpT(HIb+P6 z--ivB|Iw@xsOl7J7KaGu~UjZm(_5|vgXw>_rZxSEQdSu4cm?_ONkhhazv+= zZ|?JD3Hn)tI*A(<@}8K~-9FTLCz87&2l|z8wA)E)IGgjfHIv8O2PDfdS)wmtk%jet zQLibey&`KHku&Ht+C=Ob)mbwr0mk?0# zdQ--wtDd1l4|Y$_XHm%6;UWs%%WFDHSiu?X#7mr@`z-1)CPlv`~_-vhnGQS-WM;BXYXj z}V6V(Mj%~DO16wqMMg71v(&b{hL;Zwz-zbbd5Qapefs~00-ox{W*2%o5k2@YWpWo%5U>oN(Pb)}n7sV+Jf8caQb|KD<;`&Negke}DyCXpJJR+fDQ;PTH}S31 zsdv|57lQ^iNe{<67v7M0)U;mMTHR6|0;MqlB|a{zl=S~PZgXR*cO~LMJj!MFU5vz} zD~YunIo>r%&!qBcdVxz6cEwbVms2U>rbN3~>eLrp?0+A22t@$0(V^A1BzY1#U zr@bYeWfk%P4JBZ5{~r2$_j|#n&>-rgze@ed`*TW|ShS^S{j~JyS~afgqHZROzEzhy z?7?lcj;&*k#iuV!KF=bw^hH%oU{9ra z+i@b8nl@MWKC7^qi?s~-a1`Va>TV+YkOb1c$13HgSsDTK@59lo_Tc=@A6|}ma%e{3 z*d}CYZfxq$^Lmr|x{TA$*X`oUHlS7SG%iKas~RLdI0{zE{ia9bQybNAGSL9UDT6vQ zWw+PSXtj>qkTE&6N-B@(s=L?`)bZMj6ZO<+1k0f6IH`|~D)}*lsA18C_Ds@Lp&E|Y zMq%U%pH>2B8v)g7`+;0j?|+dQS8}MdQ;AIPU1)4ynH}t97eWKHy*P!nCIf@7{tPx( zx^7!!H!p|wNe))Gnbey|ApbK)CIMaBZ%nqDe?SPvdNu7E-+o-IT%iYkIj6sz;>%#= zq+TsKtnU^{@6Feg>&%@mbHW!CuCv@9gjiE z;Wh6ka{5@!Q@*p2RHu1|mQ?H`@AUB2n=eWZ9~S_5=ker-wjf+uV7e}jH_fKk=mu~W z*2wbAwYj#oUj}oV>g0GKIrj@E-n{w)>CN7IAP-kz&|f-2d_QpcsdB$h{B@hEfVnOD z+o^+7GiE*ZC!(lU*<*PHUA(C2pqaf@-5If&KuvK;|?MW;1O<4Z7u)mk>s;@0~E?Hs!kEsGHtE%0Dl*O>AZ^q2hlPlkD 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 F{|AzosQCZ@ literal 0 HcmV?d00001 diff --git a/desktop/src/jvmMain/resources/icon/ic_launcher.png b/desktop/src/jvmMain/resources/icon/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f36a3b04fe5a6e4b18c25a9c029a8dec53df5fd7 GIT binary patch literal 4689 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 P!~j57)96lvx&!9l*jJBO literal 0 HcmV?d00001 From 0e01a773e78034f60cf34a63afcf354fe7e75717 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 10 Dec 2021 17:45:49 +0800 Subject: [PATCH 460/615] add desktop app icon --- .../resources/MR/files/png/ic_launcher.png | Bin 15323 -> 4689 bytes .../kotlin/com/twidere/twiderex/DesktopApp.kt | 5 ++++- desktop/build.gradle.kts | 7 +++++++ .../jvmMain/resources/icon/ic_launcher.icns | Bin 0 -> 33603 bytes .../jvmMain/resources/icon/ic_launcher.ico | Bin 0 -> 152126 bytes 5 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 desktop/src/jvmMain/resources/icon/ic_launcher.icns create mode 100644 desktop/src/jvmMain/resources/icon/ic_launcher.ico diff --git a/common/src/commonMain/resources/MR/files/png/ic_launcher.png b/common/src/commonMain/resources/MR/files/png/ic_launcher.png index 43ba265190ea23280df667e5e6b6cfd084287a16..f36a3b04fe5a6e4b18c25a9c029a8dec53df5fd7 100644 GIT binary patch literal 4689 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 P!~j57)96lvx&!9l*jJBO literal 15323 zcmeIZ`9IWO_&=jC5oAv z%DzM;yD73WSqEcY-{W)N_v8K-?#KQ3{4zXvz0SGLbD!f)^EdW5+ z+>CMr037@k2MGM|$87NTB>)T`n^O+h2fIx4EaapPX0iVb#RnJH8fE6)i4c!y8=F1N z@5LXPfs81+A)gN(wvUnDFmCb2q&>#O{%&;UWxn@YGkdIeA5Q+&8e7Yo4tq5fkfZ%u zpRb{NAwz#~iOUY3s1o|*%UBGb^mA^<1g#4s0q9S|j&ei+AWtYmGknDm0MJ_m_^TWR ze)KD@+9*JTA9{D-2jc)9eu^l;kN{5&YD~t5kJhY4Fb=Z=G#|faWK+ z9-r>23wIkX*_t(6kBi9J3EYCkfU4NKcJm<1BbWDwKIAUEq9)V#aW$vQOpT(HIb+P6 z--ivB|Iw@xsOl7J7KaGu~UjZm(_5|vgXw>_rZxSEQdSu4cm?_ONkhhazv+= zZ|?JD3Hn)tI*A(<@}8K~-9FTLCz87&2l|z8wA)E)IGgjfHIv8O2PDfdS)wmtk%jet zQLibey&`KHku&Ht+C=Ob)mbwr0mk?0# zdQ--wtDd1l4|Y$_XHm%6;UWs%%WFDHSiu?X#7mr@`z-1)CPlv`~_-vhnGQS-WM;BXYXj z}V6V(Mj%~DO16wqMMg71v(&b{hL;Zwz-zbbd5Qapefs~00-ox{W*2%o5k2@YWpWo%5U>oN(Pb)}n7sV+Jf8caQb|KD<;`&Negke}DyCXpJJR+fDQ;PTH}S31 zsdv|57lQ^iNe{<67v7M0)U;mMTHR6|0;MqlB|a{zl=S~PZgXR*cO~LMJj!MFU5vz} zD~YunIo>r%&!qBcdVxz6cEwbVms2U>rbN3~>eLrp?0+A22t@$0(V^A1BzY1#U zr@bYeWfk%P4JBZ5{~r2$_j|#n&>-rgze@ed`*TW|ShS^S{j~JyS~afgqHZROzEzhy z?7?lcj;&*k#iuV!KF=bw^hH%oU{9ra z+i@b8nl@MWKC7^qi?s~-a1`Va>TV+YkOb1c$13HgSsDTK@59lo_Tc=@A6|}ma%e{3 z*d}CYZfxq$^Lmr|x{TA$*X`oUHlS7SG%iKas~RLdI0{zE{ia9bQybNAGSL9UDT6vQ zWw+PSXtj>qkTE&6N-B@(s=L?`)bZMj6ZO<+1k0f6IH`|~D)}*lsA18C_Ds@Lp&E|Y zMq%U%pH>2B8v)g7`+;0j?|+dQS8}MdQ;AIPU1)4ynH}t97eWKHy*P!nCIf@7{tPx( zx^7!!H!p|wNe))Gnbey|ApbK)CIMaBZ%nqDe?SPvdNu7E-+o-IT%iYkIj6sz;>%#= zq+TsKtnU^{@6Feg>&%@mbHW!CuCv@9gjiE z;Wh6ka{5@!Q@*p2RHu1|mQ?H`@AUB2n=eWZ9~S_5=ker-wjf+uV7e}jH_fKk=mu~W z*2wbAwYj#oUj}oV>g0GKIrj@E-n{w)>CN7IAP-kz&|f-2d_QpcsdB$h{B@hEfVnOD z+o^+7GiE*ZC!(lU*<*PHUA(C2pqaf@-5If&KuvK;|?MW;1O<4Z7u)mk>s;@0~E?Hs!kEsGHtE%0Dl*O>AZ^q2hlPlkD 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 F{|AzosQCZ@ diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 453b466a3..3fc0bf523 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.res.painterResource import androidx.compose.ui.window.application import com.twidere.twiderex.di.ext.get import com.twidere.twiderex.di.setupModules @@ -109,6 +110,7 @@ private fun ensureDesktopEntry() { "[Desktop Entry]${System.lineSeparator()}" + "Type=Application${System.lineSeparator()}" + "Name=Twidere X${System.lineSeparator()}" + + "Icon=\"${path.parent.parent.absolutePathString() + "/lib/Twidere X.png" + "\""}${System.lineSeparator()}" + "Exec=\"${path.absolutePathString() + "\" %u"}${System.lineSeparator()}" + "Terminal=false${System.lineSeparator()}" + "MimeType=x-scheme-handler/$twidereXSchema;" @@ -161,7 +163,8 @@ private fun startDesktopApp() { stopKoin() exitApplication() }, - title = "Twidere X" + title = "Twidere X", + icon = painterResource(MR.files.ic_launcher.filePath), ) { FilePicker.init(window) CompositionLocalProvider( diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index adf2db6c6..ed2b46779 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -48,6 +48,13 @@ compose { infoPlist { extraKeysRawXml = macExtraPlistKeys } + iconFile.set(project.file("src/jvmMain/resources/icon/ic_launcher.icns")) + } + linux { + iconFile.set(project.file("src/jvmMain/resources/icon/ic_launcher.png")) + } + windows { + iconFile.set(project.file("src/jvmMain/resources/icon/ic_launcher.ico")) } } } diff --git a/desktop/src/jvmMain/resources/icon/ic_launcher.icns b/desktop/src/jvmMain/resources/icon/ic_launcher.icns new file mode 100644 index 0000000000000000000000000000000000000000..6e2d4c2a00cbc371fbbcc8ad277ea242ca112b5b GIT binary patch literal 33603 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 literal 0 HcmV?d00001 diff --git a/desktop/src/jvmMain/resources/icon/ic_launcher.ico b/desktop/src/jvmMain/resources/icon/ic_launcher.ico new file mode 100644 index 0000000000000000000000000000000000000000..61e0037f80a955c618c5fb2398d2886fc92c77d5 GIT binary patch literal 152126 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 HOo9Ig3mArC literal 0 HcmV?d00001 From 9f18ad999bcf6bf2b8865b66ba4d647cba753abe Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sun, 12 Dec 2021 17:39:54 +0800 Subject: [PATCH 461/615] [wip] fix build error --- buildSrc/src/main/kotlin/Versions.kt | 4 ++-- .../db/sqldelight/transform/NotificationCursorTransform.kt | 4 ++-- .../kotlin/com/twidere/twiderex/image/GifPainter.kt | 4 ++-- .../kotlin/com/twidere/twiderex/image/ImagePainter.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index cf4c17694..b9c38c47f 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.6.0" + const val lang = "1.6.10-RC" const val coroutines = "1.6.0-RC" const val serialization = "1.3.1" } @@ -20,7 +20,7 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.0.0" + const val compose_jb = "0.0.0-use_kotlin_1.6.10-RC-dev521" const val paging = "3.1.0" const val paging_compose = "1.0.0-alpha14" const val activity = "1.4.0" diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt index a3869e497..ae103a9c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt @@ -27,7 +27,7 @@ internal fun NotificationCursor.toDb() = DbNotificationCursor( id = _id, accountKey = accountKey, type = type, - value = value, + value_ = value, timestamp = timestamp ) @@ -35,6 +35,6 @@ internal fun DbNotificationCursor.toUi() = NotificationCursor( _id = id, accountKey = accountKey, type = type, - value = value, + value = value_, timestamp = timestamp ) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt index 41f3071c9..a2be1ceb1 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.image import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.asComposeImageBitmap +import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.IntSize @@ -66,7 +66,7 @@ internal class GifPainter(private val codec: Codec, private val parentScope: Cor val bitmap = recycleBitmap(codec) codec.readPixels(bitmap, frameIndex.value) val intSize = IntSize(size.width.toInt(), size.height.toInt()) - drawImage(bitmap.asComposeImageBitmap(), dstSize = intSize) + drawImage(bitmap.asImageBitmap(), dstSize = intSize) } private fun recycleBitmap(codec: Codec): Bitmap { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index ca3dc5ef2..9865ae942 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -24,9 +24,9 @@ import androidx.compose.runtime.RememberObserver import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.asPainter import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.toPainter // this import might warn unused in ide, don't delete it import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects import kotlinx.coroutines.CoroutineScope @@ -118,7 +118,7 @@ internal class ImagePainter( ImageEffectsFilter.applyBlurFilter(image, it.blurRadius.toInt(), it.bitmapScale) } ?: image }?.let { - painter.value = it.toPainter() + painter.value = it.asPainter() } } } catch (e: Throwable) { From 59196e6f48cee6c2281343b6cb21669af62da090 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 13 Dec 2021 17:00:40 +0800 Subject: [PATCH 462/615] workaround for crash:'can't present size of 2147483647 in Constraints' --- .../twiderex/component/foundation/NetworkImage.kt | 11 +++++++---- .../twiderex/component/status/LinkPreview.kt | 14 +++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 7f4ec4bdc..1af7ffbed 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.component.foundation import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -91,10 +92,8 @@ fun NetworkImage( } ) } - if (state.value == NetworkImageState.LOADING) { - placeholder?.invoke() - } - if (state.value == NetworkImageState.SUCCESS) { + + Box { val size = painter.intrinsicSize Image( painter = painter, @@ -102,6 +101,10 @@ fun NetworkImage( contentScale = contentScale, contentDescription = stringResource(MR.strings.accessibility_common_network_image) ) + + if (state.value == NetworkImageState.LOADING) { + placeholder?.invoke() + } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt index b2c10e15d..419ca2961 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt @@ -197,8 +197,8 @@ private fun LinkWithTitleAndSmallImagePreview( val textPlaceable = measurables[1].measure(constraints = constraints) val imagePlaceable = measurables[0].measure( Constraints.fixed( - textPlaceable.measuredHeight, - textPlaceable.measuredHeight + textPlaceable.measuredHeight.ensureSizeNotInfinity(), + textPlaceable.measuredHeight.ensureSizeNotInfinity() ) ) layout( @@ -266,7 +266,7 @@ private fun LinkWithTitleAndLargeImagePreview( ) { measurables, constraints -> val textPlaceable = measurables[1].measure(constraints = constraints) val imagePlaceable = - measurables[0].measure(constraints = constraints.copy(minWidth = textPlaceable.measuredWidth)) + measurables[0].measure(constraints = constraints.copy(minWidth = textPlaceable.measuredWidth.ensureSizeNotInfinity())) layout( width = textPlaceable.measuredWidth, height = textPlaceable.measuredHeight + imagePlaceable.measuredHeight @@ -276,3 +276,11 @@ private fun LinkWithTitleAndLargeImagePreview( } } } + +private fun Int.ensureSizeNotInfinity(): Int { + return if (this == Constraints.Infinity) { + 0 + } else { + this + } +} From 2b0f7aab42bf87e641c49c88d3e7459d9ff51759 Mon Sep 17 00:00:00 2001 From: Philip Chung Date: Mon, 13 Dec 2021 01:13:25 -0800 Subject: [PATCH 463/615] Use "%20" instead of raw space in Mastodon OAuth URL This prevents problems with browsers that don't handle URLs with spaces correctly. Fixes #326 --- .../java/com/twidere/services/mastodon/MastodonOAuthService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt index ff9c066a2..0fbc099f7 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt @@ -58,7 +58,7 @@ class MastodonOAuthService( fun getWebOAuthUrl(response: CreateApplicationResponse) = "$host/oauth/authorize?client_id=${response.clientID}&response_type=code&redirect_uri=${response.redirectURI}&scope=${ scopes.joinToString( - " " + "%20" ) { it.name } }" From 69f2b0193e8272bac23e2d8af15403a5a2d8543a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 13 Dec 2021 17:18:21 +0800 Subject: [PATCH 464/615] fixed desktop image effect not working bug --- .../kotlin/com/twidere/twiderex/component/image/ImageBlur.kt | 2 +- .../kotlin/com/twidere/twiderex/component/image/ImageEffects.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt index 6ef950f2d..3ce6b81f0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.component.image -class ImageBlur( +data class ImageBlur( val blurRadius: Float, val bitmapScale: Float, ) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt index fee7f675a..2f7ad76b2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.component.image -class ImageEffects private constructor( +data class ImageEffects( val blur: ImageBlur?, val crossFade: Boolean ) { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 517ac032d..45154d578 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -52,7 +52,7 @@ internal actual fun rememberNetworkImagePainter( onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val scope = rememberCoroutineScope { Dispatchers.IO } - return remember(data) { + return remember(data, effects) { ImagePainter( data, scope, From 763a66c4785b4ee6910eafa4b04f6f96afd05187 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 13 Dec 2021 21:10:01 +0800 Subject: [PATCH 465/615] update ci --- .github/workflows/desktop.yml | 77 +++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 5ef4ef4a8..b8d3ec7ca 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -16,28 +16,27 @@ concurrency: cancel-in-progress: true jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK - uses: actions/setup-java@v1 - with: - java-version: 11 - - - name: Build with Gradle - run: ./gradlew :desktop:spotlessCheck :desktop:build - - - name: Upload build reports - uses: actions/upload-artifact@v2 - with: - name: build-reports - path: '**/build/reports' + # build: + # runs-on: ubuntu-latest + # timeout-minutes: 30 + # steps: + # - uses: actions/checkout@v2 + + # - name: Set up JDK + # uses: actions/setup-java@v1 + # with: + # java-version: 11 + + # - name: Build with Gradle + # run: ./gradlew :desktop:spotlessCheck :desktop:build + + # - name: Upload build reports + # uses: actions/upload-artifact@v2 + # with: + # name: build-reports + # path: '**/build/reports' - release-linux: - if: startsWith(github.ref, 'refs/tags/') + build-linux: needs: build runs-on: ubuntu-latest timeout-minutes: 30 @@ -52,6 +51,12 @@ jobs: - name: Build with Gradle run: ./gradlew :desktop:packageDeb + - name: Upload build binaries + uses: actions/upload-artifact@v2 + with: + name: build-binaries + path: '**/build/compose/binaries' + - name: Check if is prelease if: startsWith(github.ref, 'refs/tags/') id: check-tag @@ -61,7 +66,7 @@ jobs: fi - name: Create Prerelease - if: steps.check-tag.outputs.prelease == 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' run: | set -x assets=() @@ -74,7 +79,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Release - if: steps.check-tag.outputs.prelease != 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' run: | set -x assets=() @@ -86,8 +91,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release-windows: - if: startsWith(github.ref, 'refs/tags/') + build-windows: needs: build runs-on: windows-latest timeout-minutes: 30 @@ -102,6 +106,12 @@ jobs: - name: Build with Gradle run: ./gradlew :desktop:packageExe + - name: Upload build binaries + uses: actions/upload-artifact@v2 + with: + name: build-binaries + path: '**/build/compose/binaries' + - name: Check if is prelease if: startsWith(github.ref, 'refs/tags/') id: check-tag @@ -111,7 +121,7 @@ jobs: fi - name: Create Prerelease - if: steps.check-tag.outputs.prelease == 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' run: | set -x assets=() @@ -124,7 +134,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Release - if: steps.check-tag.outputs.prelease != 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' run: | set -x assets=() @@ -136,8 +146,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release-macos: - if: startsWith(github.ref, 'refs/tags/') + build-macos: needs: build runs-on: macos-latest timeout-minutes: 30 @@ -152,6 +161,12 @@ jobs: - name: Build with Gradle run: ./gradlew :desktop:packageDmg + - name: Upload build binaries + uses: actions/upload-artifact@v2 + with: + name: build-binaries + path: '**/build/compose/binaries' + - name: Check if is prelease if: startsWith(github.ref, 'refs/tags/') id: check-tag @@ -161,7 +176,7 @@ jobs: fi - name: Create Prerelease - if: steps.check-tag.outputs.prelease == 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' run: | set -x assets=() @@ -174,7 +189,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Release - if: steps.check-tag.outputs.prelease != 'true' + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' run: | set -x assets=() From 7d755a73303d463151beefb584d43ff3a464ab6a Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 00:21:57 +0800 Subject: [PATCH 466/615] Update desktop.yml --- .github/workflows/desktop.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index b8d3ec7ca..03569c5a8 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -37,7 +37,6 @@ jobs: # path: '**/build/reports' build-linux: - needs: build runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -92,7 +91,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-windows: - needs: build runs-on: windows-latest timeout-minutes: 30 steps: @@ -147,7 +145,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} build-macos: - needs: build runs-on: macos-latest timeout-minutes: 30 steps: From 667b9bad8f79572ca782c25af8a405e4f3b7f0df Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 01:04:33 +0800 Subject: [PATCH 467/615] Update desktop.yml --- .github/workflows/desktop.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 03569c5a8..832c29520 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -53,7 +53,7 @@ jobs: - name: Upload build binaries uses: actions/upload-artifact@v2 with: - name: build-binaries + name: build-binaries-ubuntu path: '**/build/compose/binaries' - name: Check if is prelease @@ -107,7 +107,7 @@ jobs: - name: Upload build binaries uses: actions/upload-artifact@v2 with: - name: build-binaries + name: build-binaries-windows path: '**/build/compose/binaries' - name: Check if is prelease @@ -161,7 +161,7 @@ jobs: - name: Upload build binaries uses: actions/upload-artifact@v2 with: - name: build-binaries + name: build-binaries-macos path: '**/build/compose/binaries' - name: Check if is prelease From 59f6ea84945d93b3cb9c2428d17ffe3cf96e36c5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 06:46:36 +0800 Subject: [PATCH 468/615] update windows package to msi --- .github/workflows/desktop.yml | 6 +++--- desktop/build.gradle.kts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index b8d3ec7ca..0ff636798 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -104,7 +104,7 @@ jobs: java-version: 15 - name: Build with Gradle - run: ./gradlew :desktop:packageExe + run: ./gradlew :desktop:packageMsi - name: Upload build binaries uses: actions/upload-artifact@v2 @@ -125,7 +125,7 @@ jobs: run: | set -x assets=() - for asset in $(find -name *.exe); do + for asset in $(find -name *.msi); do assets+=("-a" "$asset") done tag_name="${GITHUB_REF##*/}" @@ -138,7 +138,7 @@ jobs: run: | set -x assets=() - for asset in $(find -name *.exe); do + for asset in $(find -name *.msi); do assets+=("-a" "$asset") done tag_name="${GITHUB_REF##*/}" diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index ed2b46779..ad7e1335f 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -37,7 +37,7 @@ compose { application { mainClass = "com.twidere.twiderex.MainKt" nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Deb) + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = Package.name packageVersion = Package.versionName.split("-").firstOrNull() modules("java.sql") // https://github.com/JetBrains/compose-jb/issues/381 From 25c93205703faa9db8026b9cdcf43eba15abe6e1 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 06:46:46 +0800 Subject: [PATCH 469/615] fix windows reg file --- .../kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt index 323e674fc..b479ad56d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt @@ -25,7 +25,6 @@ import java.io.File import java.io.IOException import java.io.InputStream import java.io.StringWriter -import java.lang.Exception object WindowsRegistry { @@ -37,7 +36,8 @@ object WindowsRegistry { val regFile = File("${root.absolutePath}/deeplink.reg") if (!regFile.exists()) { regFile.createNewFile() - val reg = """ + } + val reg = """ Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\TwidereX] @@ -50,9 +50,8 @@ object WindowsRegistry { [HKEY_CLASSES_ROOT\TwidereX\shell\open\command] @="\"${File("").absolutePath.replace("\\", "\\\\")}\\Twidere X.exe\" \"%1\"" - """.trimIndent() - regFile.writeText(reg) - } + """.trimIndent() + regFile.writeText(reg) try { val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() process.waitFor() From 780cad1fb81009752f19e04fcb6cde8efdddb5d5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 07:15:52 +0800 Subject: [PATCH 470/615] fix windows reg file --- build.gradle.kts | 2 +- .../kotlin/com/twidere/twiderex/DesktopApp.kt | 4 ++-- .../twidere/twiderex/utils/WindowsRegistry.kt | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4731619f2..3da328ac9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,7 @@ allprojects { tasks.withType { kotlinOptions { jvmTarget = Versions.Java.jvmTarget - allWarningsAsErrors = true + // allWarningsAsErrors = true freeCompilerArgs = listOf( "-Xopt-in=kotlin.RequiresOptIn", ) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 65ae2cf55..0665b3da2 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -134,8 +134,8 @@ private fun ensureMimeInfo() { } private fun ensureWindowsRegistry() { - val protocol = WindowsRegistry.readRegistry("HKCR\\TwidereX", "URL Protocol") - if (protocol?.contains(twidereXSchema) == true) return + // val protocol = WindowsRegistry.readRegistry("HKCR\\TwidereX", "URL Protocol") + // if (protocol?.contains(twidereXSchema) == true) return WindowsRegistry.registryUrlProtocol(twidereXSchema) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt index b479ad56d..a9358e7a4 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt @@ -51,16 +51,18 @@ object WindowsRegistry { [HKEY_CLASSES_ROOT\TwidereX\shell\open\command] @="\"${File("").absolutePath.replace("\\", "\\\\")}\\Twidere X.exe\" \"%1\"" """.trimIndent() - regFile.writeText(reg) - try { - val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() - process.waitFor() - } catch (e: Throwable) { + if (regFile.readText() != reg) { + regFile.writeText(reg) try { - // if process is not working use Desktop open file - Desktop.getDesktop().open(regFile) + val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() + process.waitFor() } catch (e: Throwable) { - e.printStackTrace() + try { + // if process is not working use Desktop open file + Desktop.getDesktop().open(regFile) + } catch (e: Throwable) { + e.printStackTrace() + } } } } From 3dc90bcb569e68309b0671574479a2cc7eed1b62 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 07:42:23 +0800 Subject: [PATCH 471/615] add menu and shortcut for windows --- desktop/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index ad7e1335f..72f29d451 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -54,6 +54,8 @@ compose { iconFile.set(project.file("src/jvmMain/resources/icon/ic_launcher.png")) } windows { + shortcut = true + menu = true iconFile.set(project.file("src/jvmMain/resources/icon/ic_launcher.ico")) } } From 2203a0edb66f792a28ee46e4c2d7a15456b388e4 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 07:43:31 +0800 Subject: [PATCH 472/615] update windows reg detection --- .../src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 0665b3da2..3cdaaa87e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -67,7 +67,9 @@ fun runDesktopApp( when (currentOperatingSystem) { OperatingSystem.Windows -> { ensureWindowsDatastore() - ensureWindowsRegistry() + if (args.isEmpty()) { + ensureWindowsRegistry() + } ensureSingleAppInstance(args) } OperatingSystem.Linux -> { From 98fc67f5c7a92a317eed68af13d6b13e2ade021d Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Dec 2021 11:42:59 +0800 Subject: [PATCH 473/615] fixed coil infinite execuete also fixed blur effects not apply --- .../com/twidere/twiderex/kmp/ImagePainter.kt | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 02d7facf3..96e56f3a0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -22,15 +22,17 @@ package com.twidere.twiderex.kmp import android.os.Build import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import coil.ImageLoader import coil.annotation.ExperimentalCoilApi -import coil.compose.ImagePainter import coil.compose.LocalImageLoader import coil.compose.rememberImagePainter import coil.decode.GifDecoder import coil.decode.ImageDecoderDecoder +import coil.request.ImageRequest +import coil.request.ImageResult import coil.size.OriginalSize import coil.transform.BlurTransformation import coil.util.CoilUtils @@ -44,6 +46,7 @@ import okhttp3.Cache import okhttp3.Request import java.io.File import java.net.URL + @OptIn(coil.annotation.ExperimentalCoilApi::class) @Composable internal actual fun rememberNetworkImagePainter( @@ -55,6 +58,21 @@ internal actual fun rememberNetworkImagePainter( onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val context = LocalContext.current + val listener = remember { + object : ImageRequest.Listener { + override fun onStart(request: ImageRequest) { + onImageStateChanged(NetworkImageState.LOADING) + } + + override fun onError(request: ImageRequest, throwable: Throwable) { + onImageStateChanged(NetworkImageState.ERROR) + } + + override fun onSuccess(request: ImageRequest, metadata: ImageResult.Metadata) { + onImageStateChanged(NetworkImageState.SUCCESS) + } + } + } return rememberImagePainter( data = data, imageLoader = buildImageLoader(cacheDir), @@ -70,11 +88,7 @@ internal actual fun rememberNetworkImagePainter( ) ) } - listener( - onSuccess = { _, _ -> onImageStateChanged(NetworkImageState.SUCCESS) }, - onError = { _, _ -> onImageStateChanged(NetworkImageState.ERROR) }, - onStart = { onImageStateChanged(NetworkImageState.LOADING) } - ) + listener(listener) if (authorization.hasAuthorization) { addHeader( "Authorization", @@ -85,10 +99,6 @@ internal actual fun rememberNetworkImagePainter( ) ) } - }, - onExecute = { previous, current -> - (current.state !is ImagePainter.State.Success && previous?.request != current.request) || - current.state == ImagePainter.State.Empty } ) } From 8d4c11ff713695d54653db4705bb28089bee2f89 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 14:21:10 +0800 Subject: [PATCH 474/615] clean up dependencies --- android/build.gradle.kts | 2 - buildSrc/src/main/kotlin/Dependencies.kt | 82 ------------------------ buildSrc/src/main/kotlin/Versions.kt | 8 +-- common/build.gradle.kts | 12 ++-- 4 files changed, 5 insertions(+), 99 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 9bbd05b19..1887c1eb6 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -138,7 +138,6 @@ dependencies { implementation(projects.common) implementation("com.google.accompanist:accompanist-insets:${Versions.accompanist}") implementation("androidx.startup:startup-runtime:${Versions.startup}") - work() if (enableGoogleVariant) { // START Non-FOSS component @@ -151,6 +150,5 @@ dependencies { } junit4() - // mockito() androidTest() } diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 610972bb1..0e17a227b 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -55,88 +55,6 @@ fun DependencyHandlerScope.kspApi() { implementation("com.google.devtools.ksp:symbol-processing-api", Versions.ksp) } -fun DependencyHandlerScope.paging() { - implementation("androidx.paging:paging-common", Versions.paging) - implementation("androidx.paging:paging-compose", Versions.paging_compose) -} - -fun DependencyHandlerScope.activity() { - implementation("androidx.activity:activity-ktx", Versions.activity) - implementation("androidx.activity:activity-compose", Versions.activity) -} - -fun DependencyHandlerScope.datastore() { - implementation("androidx.datastore:datastore", Versions.datastore) - implementation("androidx.datastore:datastore-preferences", Versions.datastore) -} - -fun DependencyHandlerScope.hilt() { - implementation("com.google.dagger:hilt-android", Versions.hilt) - kapt("com.google.dagger:hilt-android-compiler", Versions.hilt) - implementation("androidx.hilt:hilt-work", Versions.androidx_hilt) - kapt("androidx.hilt:hilt-compiler", Versions.androidx_hilt) -} - -fun DependencyHandlerScope.room() { - implementation("androidx.room:room-runtime", Versions.room) - implementation("androidx.room:room-ktx", Versions.room) - implementation("androidx.room:room-paging", Versions.room) - kapt("androidx.room:room-compiler", Versions.room) - androidTestImplementation("androidx.room:room-testing", Versions.room) -} - -fun DependencyHandlerScope.lifecycle() { - implementation("androidx.lifecycle:lifecycle-runtime-ktx", Versions.lifecycle) - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx", Versions.lifecycle) - // implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate", Versions.lifecycle) - implementation("androidx.lifecycle:lifecycle-common-java8", Versions.lifecycle) - implementation("androidx.lifecycle:lifecycle-viewmodel-compose", Versions.lifecycle_compose) -} - -fun DependencyHandlerScope.android() { - work() - room() - lifecycle() - activity() - implementation("androidx.startup:startup-runtime", Versions.startup) - implementation("io.coil-kt:coil-compose", Versions.coil) - implementation("io.coil-kt:coil-gif", Versions.coil) - implementation("androidx.vectordrawable:vectordrawable:1.1.0") - implementation("androidx.exifinterface:exifinterface", Versions.androidx_exifinterface) - implementation("com.google.android.exoplayer:exoplayer", Versions.exoplayer) - implementation("com.google.android.exoplayer:extension-okhttp", Versions.exoplayer) - implementation("androidx.browser:browser", Versions.browser) - debugImplementation("com.squareup.leakcanary:leakcanary-android:2.7") -} - -fun DependencyHandlerScope.accompanist() { - implementation("com.google.accompanist:accompanist-insets", Versions.accompanist) - implementation("com.google.accompanist:accompanist-pager", Versions.accompanist) - implementation("com.google.accompanist:accompanist-pager-indicators", Versions.accompanist) -} - -fun DependencyHandlerScope.work() { - implementation("androidx.work:work-runtime-ktx", Versions.work) -} - -fun DependencyHandlerScope.widget() { - implementation("com.mxalbert.zoomable:zoomable", Versions.zoomable) - implementation("com.github.Tlaster:NestedScrollView", Versions.nestedScrollView) - implementation("com.github.Tlaster:Swiper", Versions.swiper) - implementation("com.github.Tlaster:Placeholder", Versions.placeholder) -} - -fun DependencyHandlerScope.misc() { - implementation("com.twitter.twittertext:twitter-text:3.1.0") - implementation("org.jsoup:jsoup:1.14.3") - implementation("com.google.protobuf:protobuf-javalite", Versions.protobuf) -} - -fun DependencyHandlerScope.mockito() { - testImplementation("org.mockito:mockito-core:4.1.0") - testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0") -} - fun DependencyHandlerScope.androidTest() { testImplementation("androidx.arch.core:core-testing:2.1.0") androidTestImplementation("androidx.arch.core:core-testing:2.1.0") diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b9c38c47f..3514ad2c0 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -14,15 +14,13 @@ object Versions { const val ksp = "${Kotlin.lang}-1.0.1" const val agp = "7.0.3" - const val spotless = "6.0.2" + const val spotless = "6.0.4" const val ktlint = "0.42.1" - const val hilt = "2.38.1" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" const val compose_jb = "0.0.0-use_kotlin_1.6.10-RC-dev521" const val paging = "3.1.0" - const val paging_compose = "1.0.0-alpha14" const val activity = "1.4.0" const val datastore = "1.0.0" const val androidx_hilt = "1.0.0" @@ -30,10 +28,6 @@ object Versions { const val lifecycle = "2.4.0" const val lifecycle_compose = "2.4.0" const val work = "2.7.1" - const val placeholder = "0.7.0" - const val zoomable = "1.0.1" - const val swiper = "0.6.0" - const val nestedScrollView = "0.7.0" const val startup = "1.1.0" const val coil = "2.0.0-alpha05" const val accompanist = "0.21.4-beta" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 77628b4c7..7cabdcc0d 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,4 @@ + import org.jetbrains.compose.compose import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.konan.properties.loadProperties @@ -7,7 +8,6 @@ plugins { id("org.jetbrains.compose") version Versions.compose_jb kotlin("plugin.serialization") version Versions.Kotlin.lang id("com.android.library") - kotlin("kapt") id("com.google.devtools.ksp").version(Versions.ksp) id("dev.icerock.mobile.multiplatform-resources") version Versions.moko id("com.squareup.sqldelight") @@ -87,7 +87,7 @@ kotlin { implementation("androidx.room:room-runtime:${Versions.room}") implementation("androidx.room:room-ktx:${Versions.room}") implementation("androidx.room:room-paging:${Versions.room}") - kapt("androidx.room:room-compiler:${Versions.room}") + ksp("androidx.room:room-compiler:${Versions.room}") implementation("io.coil-kt:coil-base:${Versions.coil}") implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") @@ -151,12 +151,8 @@ multiplatformResources { multiplatformResourcesPackage = Package.id } -fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kapt(dependencyNotation: String) { - configurations["kapt"].dependencies.add(project.dependencies.create(dependencyNotation)) -} - -fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.ksp(dependencyNotation: org.gradle.accessors.dm.RouteProcessorProjectDependency) { - configurations["ksp"].dependencies.add(projects.routeProcessor) +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.ksp(dependencyNotation: Any) { + configurations["ksp"].dependencies.add(project.dependencies.create(dependencyNotation)) } android { From b0560350cad886513a5fa80a752e8b4e954d4b83 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 07:43:31 +0800 Subject: [PATCH 475/615] Revert "update windows reg detection" This reverts commit 2203a0edb66f792a28ee46e4c2d7a15456b388e4. --- .../src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 3cdaaa87e..0665b3da2 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -67,9 +67,7 @@ fun runDesktopApp( when (currentOperatingSystem) { OperatingSystem.Windows -> { ensureWindowsDatastore() - if (args.isEmpty()) { - ensureWindowsRegistry() - } + ensureWindowsRegistry() ensureSingleAppInstance(args) } OperatingSystem.Linux -> { From ad8afb6beae4f8b2ae87311a9e132197223d6adc Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 07:15:52 +0800 Subject: [PATCH 476/615] Revert "fix windows reg file" This reverts commit 780cad1fb81009752f19e04fcb6cde8efdddb5d5. --- build.gradle.kts | 2 +- .../kotlin/com/twidere/twiderex/DesktopApp.kt | 4 ++-- .../twidere/twiderex/utils/WindowsRegistry.kt | 18 ++++++++---------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3da328ac9..4731619f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,7 @@ allprojects { tasks.withType { kotlinOptions { jvmTarget = Versions.Java.jvmTarget - // allWarningsAsErrors = true + allWarningsAsErrors = true freeCompilerArgs = listOf( "-Xopt-in=kotlin.RequiresOptIn", ) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 0665b3da2..65ae2cf55 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -134,8 +134,8 @@ private fun ensureMimeInfo() { } private fun ensureWindowsRegistry() { - // val protocol = WindowsRegistry.readRegistry("HKCR\\TwidereX", "URL Protocol") - // if (protocol?.contains(twidereXSchema) == true) return + val protocol = WindowsRegistry.readRegistry("HKCR\\TwidereX", "URL Protocol") + if (protocol?.contains(twidereXSchema) == true) return WindowsRegistry.registryUrlProtocol(twidereXSchema) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt index a9358e7a4..b479ad56d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt @@ -51,18 +51,16 @@ object WindowsRegistry { [HKEY_CLASSES_ROOT\TwidereX\shell\open\command] @="\"${File("").absolutePath.replace("\\", "\\\\")}\\Twidere X.exe\" \"%1\"" """.trimIndent() - if (regFile.readText() != reg) { - regFile.writeText(reg) + regFile.writeText(reg) + try { + val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() + process.waitFor() + } catch (e: Throwable) { try { - val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() - process.waitFor() + // if process is not working use Desktop open file + Desktop.getDesktop().open(regFile) } catch (e: Throwable) { - try { - // if process is not working use Desktop open file - Desktop.getDesktop().open(regFile) - } catch (e: Throwable) { - e.printStackTrace() - } + e.printStackTrace() } } } From e3cf2099b96ff874a5fa64b2cf6c5d9e965f10a2 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 06:46:46 +0800 Subject: [PATCH 477/615] Revert "fix windows reg file" This reverts commit 25c93205703faa9db8026b9cdcf43eba15abe6e1. --- .../kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt index b479ad56d..323e674fc 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt @@ -25,6 +25,7 @@ import java.io.File import java.io.IOException import java.io.InputStream import java.io.StringWriter +import java.lang.Exception object WindowsRegistry { @@ -36,8 +37,7 @@ object WindowsRegistry { val regFile = File("${root.absolutePath}/deeplink.reg") if (!regFile.exists()) { regFile.createNewFile() - } - val reg = """ + val reg = """ Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\TwidereX] @@ -50,8 +50,9 @@ object WindowsRegistry { [HKEY_CLASSES_ROOT\TwidereX\shell\open\command] @="\"${File("").absolutePath.replace("\\", "\\\\")}\\Twidere X.exe\" \"%1\"" - """.trimIndent() - regFile.writeText(reg) + """.trimIndent() + regFile.writeText(reg) + } try { val process = ProcessBuilder("cmd", "/c", "regedit", "/s", regFile.canonicalPath).start() process.waitFor() From 64db4004d2d842f9e92890d663c4536c86ad50c2 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 14:25:44 +0800 Subject: [PATCH 478/615] clean up ci config --- .github/workflows/desktop.yml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 944f462ce..50a18796b 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -16,26 +16,6 @@ concurrency: cancel-in-progress: true jobs: - # build: - # runs-on: ubuntu-latest - # timeout-minutes: 30 - # steps: - # - uses: actions/checkout@v2 - - # - name: Set up JDK - # uses: actions/setup-java@v1 - # with: - # java-version: 11 - - # - name: Build with Gradle - # run: ./gradlew :desktop:spotlessCheck :desktop:build - - # - name: Upload build reports - # uses: actions/upload-artifact@v2 - # with: - # name: build-reports - # path: '**/build/reports' - build-linux: runs-on: ubuntu-latest timeout-minutes: 30 From e8f1d240c79f69175081cea9d22996fae149a2b0 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Dec 2021 15:29:26 +0800 Subject: [PATCH 479/615] disable send button when text lenth exceed limits in ComposeScene --- .../twiderex/scenes/compose/ComposeScene.kt | 15 +++------------ .../viewmodel/compose/ComposeViewModel.kt | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d491649e1..b91416cf4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -143,7 +143,6 @@ import com.twidere.twiderex.viewmodel.compose.DraftComposeViewModel import com.twidere.twiderex.viewmodel.compose.DraftItemViewModel import com.twidere.twiderex.viewmodel.compose.VoteExpired import com.twidere.twiderex.viewmodel.compose.VoteState -import com.twitter.twittertext.TwitterTextConfiguration import com.twitter.twittertext.TwitterTextParser import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged @@ -206,6 +205,7 @@ private fun ComposeBody( val enableThreadMode by viewModel.enableThreadMode.observeAsState(initial = false) var showSaveDraftDialog by remember { mutableStateOf(false) } val scaffoldState = rememberBottomSheetScaffoldState() + val maxLength by viewModel.maxContentLength.observeAsState(initial = 1) if (showSaveDraftDialog || canSaveDraft) { BackHandler { when { @@ -396,7 +396,7 @@ private fun ComposeBody( Row( verticalAlignment = Alignment.CenterVertically, ) { - TextProgress(textFieldValue) + TextProgress(textFieldValue, maxLength) if (account.type == PlatformType.Mastodon) { ComposeMastodonVisibility( modifier = Modifier.weight(1f), @@ -644,16 +644,7 @@ private object LocationDisplayDefaults { } @Composable -private fun TextProgress(textFieldValue: TextFieldValue) { - val account = LocalActiveAccount.current ?: return - val maxLength = remember { - when (account.type) { - PlatformType.Twitter -> TwitterTextConfiguration.getDefaultConfig().maxWeightedTweetLength - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> 500 - } - } +private fun TextProgress(textFieldValue: TextFieldValue, maxLength: Int) { val textLength = remember(textFieldValue) { TwitterTextParser.parseTweet(textFieldValue.text).weightedLength } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 891600f8e..f193a9359 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -50,6 +50,7 @@ import com.twidere.twiderex.repository.UserRepository import com.twidere.twiderex.utils.MastodonEmojiCache import com.twidere.twiderex.utils.notifyError import com.twitter.twittertext.Extractor +import com.twitter.twittertext.TwitterTextConfiguration import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -243,9 +244,11 @@ open class ComposeViewModel( val contentWarningTextFieldValue = MutableStateFlow(TextFieldValue()) val textFieldValue = MutableStateFlow(TextFieldValue()) val images = MutableStateFlow>(emptyList()) - val canSend = textFieldValue - .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } - .asStateIn(viewModelScope, false) + val canSend by lazy { + combine(textFieldValue, images, maxContentLength) { text, imgs, maxLength -> + (text.text.isNotEmpty() || !imgs.isNullOrEmpty()) && text.text.length <= maxLength + }.asStateIn(viewModelScope, false) + } val canSaveDraft = textFieldValue .combine(images) { text, imgs -> text.text.isNotEmpty() || !imgs.isNullOrEmpty() } .asStateIn(viewModelScope, false) @@ -257,6 +260,14 @@ open class ComposeViewModel( ) } val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) + val maxContentLength = account.map { + when (it.type) { + PlatformType.Twitter -> TwitterTextConfiguration.getDefaultConfig().maxWeightedTweetLength + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> 500 + } + }.asStateIn(viewModelScope, 1) @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { From cd6a9fb15851a10f4dcf9f3e00bc8c911d3ed609 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Dec 2021 15:43:33 +0800 Subject: [PATCH 480/615] fixed Character count ignores quoted URL --- .../viewmodel/compose/ComposeViewModel.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index f193a9359..dff4d0c02 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -260,14 +260,6 @@ open class ComposeViewModel( ) } val enableThreadMode = MutableStateFlow(composeType == ComposeType.Thread) - val maxContentLength = account.map { - when (it.type) { - PlatformType.Twitter -> TwitterTextConfiguration.getDefaultConfig().maxWeightedTweetLength - PlatformType.StatusNet -> TODO() - PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> 500 - } - }.asStateIn(viewModelScope, 1) @OptIn(ExperimentalCoroutinesApi::class) val status by lazy { @@ -312,6 +304,20 @@ open class ComposeViewModel( }.asStateIn(viewModelScope, null) } + val maxContentLength = account.combine(status) { account, status -> + when (account.type) { + PlatformType.Twitter -> TwitterTextConfiguration.getDefaultConfig().maxWeightedTweetLength - + ( + status?.generateShareLink()?.let { + it.length + 1 // for space + } ?: 0 + ) + PlatformType.StatusNet -> TODO() + PlatformType.Fanfou -> TODO() + PlatformType.Mastodon -> 500 + } + }.asStateIn(viewModelScope, 1) + val mediaInsertMode = MutableStateFlow(MediaInsertMode.All) fun setText(value: TextFieldValue) { From e58ba999c17706b101152de981c39238686a733d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 16:17:59 +0800 Subject: [PATCH 481/615] upgrade to compose-jb 1.0.1-rc1 --- android/build.gradle.kts | 2 + buildSrc/src/main/kotlin/Versions.kt | 2 +- common/build.gradle.kts | 31 ++--- ...PagingItems.kt => PagingPlaceholderKey.kt} | 0 .../twidere/twiderex/TwidereApplication.kt | 5 +- .../foundation/RemainingTimeVideo.kt | 16 +-- .../platform/PlatformPlayerView.android.kt | 108 +++++++++--------- .../dataprovider/db/dao/DraftDaoImpl.kt | 2 +- .../twiderex/extensions/ContextExtensions.kt | 4 +- .../com/twidere/twiderex/kmp/ImagePainter.kt | 88 +++++++------- .../twidere/twiderex/kmp/RemoteNavigator.kt | 3 +- .../com/twidere/twiderex/kmp/ResLoader.kt | 14 +-- .../notification/AppNotificationManager.kt | 4 +- .../twiderex/room/db/dao/RoomDraftDao.kt | 2 +- .../twiderex/utils/BlurTransformation.kt | 69 +++++++++++ .../utils/video/CacheDataSourceFactory.kt | 9 +- .../twiderex/utils/video/VideoCache.kt | 4 +- .../paging/compose/LazyPagingItems.kt | 4 - .../paging/compose/PagingPlaceholderKey.kt | 25 ++++ .../twiderex/component/image/ImageBlur.kt | 4 +- .../twiderex/extensions/KoinExtensions.kt | 2 + .../{Twitter.kt => TwitterReplySettings.kt} | 0 .../notification/AppNotificationManager.kt | 3 +- .../paging/crud/MemoryCachePagingMediator.kt | 1 + ...PagingItems.kt => PagingPlaceholderKey.kt} | 0 .../com/twidere/twiderex/image/GifPainter.kt | 4 +- .../twidere/twiderex/image/ImagePainter.kt | 4 +- .../{ErrorCodes.kt => TwitterErrorCodes.kt} | 0 28 files changed, 255 insertions(+), 155 deletions(-) rename common/src/androidMain/kotlin/androidx.paging.compose/{LazyPagingItems.kt => PagingPlaceholderKey.kt} (100%) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt create mode 100644 common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt rename common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/{Twitter.kt => TwitterReplySettings.kt} (100%) rename common/src/desktopMain/kotlin/androidx.paging.compose/{LazyPagingItems.kt => PagingPlaceholderKey.kt} (100%) rename services/src/main/java/com/twidere/services/twitter/{ErrorCodes.kt => TwitterErrorCodes.kt} (100%) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 1887c1eb6..403d461f1 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -88,6 +88,8 @@ android { if (hasSigningProps) { signingConfig = signingConfigs.getByName("twidere") } + // isMinifyEnabled = true + // isShrinkResources = true } release { if (hasSigningProps) { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 3514ad2c0..97c608639 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -19,7 +19,7 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "0.0.0-use_kotlin_1.6.10-RC-dev521" + const val compose_jb = "1.0.1-rc1" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 7cabdcc0d..9d29f9709 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -32,9 +32,6 @@ repositories { google() } -// TODO: workaround for https://github.com/google/ksp/issues/518 -evaluationDependsOn(":routeProcessor") - kotlin { android() jvm("desktop") { @@ -60,7 +57,7 @@ kotlin { implementation("com.twitter.twittertext:twitter-text:3.1.0") implementation("org.jsoup:jsoup:1.14.3") implementation(projects.routeProcessor) - ksp(projects.routeProcessor) + kspAll(projects.routeProcessor) implementation("com.squareup.sqldelight:coroutines-extensions-jvm:${Versions.sqlDelight}") api("dev.icerock.moko:resources:${Versions.moko}") implementation("app.cash.turbine:turbine:0.7.0") @@ -87,7 +84,7 @@ kotlin { implementation("androidx.room:room-runtime:${Versions.room}") implementation("androidx.room:room-ktx:${Versions.room}") implementation("androidx.room:room-paging:${Versions.room}") - ksp("androidx.room:room-compiler:${Versions.room}") + kspAndroid("androidx.room:room-compiler:${Versions.room}") implementation("io.coil-kt:coil-base:${Versions.coil}") implementation("io.coil-kt:coil-compose:${Versions.coil}") implementation("io.coil-kt:coil-gif:${Versions.coil}") @@ -104,6 +101,7 @@ kotlin { implementation("androidx.browser:browser:${Versions.browser}") implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02") implementation("androidx.activity:activity-compose:${Versions.activity}") + implementation("com.github.android:renderscript-intrinsics-replacement-toolkit:b6363490c3") } } val androidAndroidTest by getting { @@ -151,8 +149,17 @@ multiplatformResources { multiplatformResourcesPackage = Package.id } -fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.ksp(dependencyNotation: Any) { - configurations["ksp"].dependencies.add(project.dependencies.create(dependencyNotation)) +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kspAll(dependencyNotation: Any) { + kspAndroid(dependencyNotation) + kspDesktop(dependencyNotation) +} + +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kspDesktop(dependencyNotation: Any) { + configurations["kspDesktop"].dependencies.add(project.dependencies.create(dependencyNotation)) +} + +fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.kspAndroid(dependencyNotation: Any) { + configurations["kspAndroid"].dependencies.add(project.dependencies.create(dependencyNotation)) } android { @@ -164,12 +171,6 @@ android { targetSdk = AndroidSdk.target testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments["notPackage"] = "com.twidere.twiderex.viewmodel" - - javaCompileOptions { - annotationProcessorOptions { - argument("room.schemaLocation", "$projectDir/schemas") - } - } } compileOptions { @@ -193,6 +194,10 @@ android { } } +ksp { + arg("room.schemaLocation", "$projectDir/schemas") +} + tasks.create("generateTranslation") { doLast { val localizationFolder = File(rootDir, "localization") diff --git a/common/src/androidMain/kotlin/androidx.paging.compose/LazyPagingItems.kt b/common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt similarity index 100% rename from common/src/androidMain/kotlin/androidx.paging.compose/LazyPagingItems.kt rename to common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt index fef66e17f..3120b4504 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -25,15 +25,14 @@ import com.twidere.twiderex.di.setupModules import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.androidx.workmanager.koin.workManagerFactory -import org.koin.core.KoinExperimentalAPI import org.koin.core.context.startKoin +import org.koin.core.logger.Level abstract class TwidereApplication : Application() { - @OptIn(KoinExperimentalAPI::class) override fun onCreate() { super.onCreate() startKoin { - androidLogger() + androidLogger(Level.NONE) androidContext(this@TwidereApplication) workManagerFactory() setupModules() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt index 95168829e..a17f6195c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt @@ -20,10 +20,12 @@ */ package com.twidere.twiderex.component.foundation -import com.google.android.exoplayer2.SimpleExoPlayer - -class RemainingTimeExoPlayer(builder: Builder) : SimpleExoPlayer(builder) { - override fun getContentPosition(): Long { - return super.getContentPosition() - contentDuration - } -} +// TODO: RemainingTimeExoPlayer +// import com.google.android.exoplayer2.ExoPlayer +// import com.google.android.exoplayer2.SimpleExoPlayer +// +// class RemainingTimeExoPlayer(builder: ExoPlayer.Builder) : SimpleExoPlayer(builder) { +// override fun getContentPosition(): Long { +// return super.getContentPosition() - contentDuration +// } +// } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt index 1fed38d80..ea44caa5e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt @@ -25,18 +25,17 @@ import android.view.SurfaceView import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView +import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.ui.StyledPlayerView -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.google.android.exoplayer2.upstream.DefaultDataSource import com.twidere.services.http.config.HttpConfig import com.twidere.twiderex.component.foundation.PlayerCallBack import com.twidere.twiderex.component.foundation.PlayerProgressCallBack -import com.twidere.twiderex.component.foundation.RemainingTimeExoPlayer import com.twidere.twiderex.di.ext.get import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.utils.video.CacheDataSourceFactory @@ -67,67 +66,66 @@ actual class PlatformPlayerView actual constructor( playerView.useController = false playerView.keepScreenOn = keepScreenOn }.apply { - player = RemainingTimeExoPlayer( - SimpleExoPlayer.Builder(context) - .apply { - if (httpConfig.proxyConfig.enable) { - // replace DataSource - OkHttpDataSource.Factory( - TwidereServiceFactory - .createHttpClientFactory() - .createHttpClientBuilder() - .build() - ) - .let { - DefaultDataSourceFactory(context, it) - }.let { - DefaultMediaSourceFactory(it) - }.let { - setMediaSourceFactory(it) - } - } - } - ).apply { - repeatMode = Player.REPEAT_MODE_ALL - addListener(object : Player.Listener { - override fun onPlaybackStateChanged(state: Int) { - when (state) { - Player.STATE_BUFFERING -> { - playerCallBack?.onBuffering() + player = ExoPlayer.Builder(context) + .apply { + if (httpConfig.proxyConfig.enable) { + // replace DataSource + OkHttpDataSource.Factory( + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() + ) + .let { + DefaultDataSource.Factory(context, it) + }.let { + DefaultMediaSourceFactory(it) + }.let { + setMediaSourceFactory(it) } - Player.STATE_READY -> { - playerCallBack?.onReady() + } + }.build() + .apply { + repeatMode = Player.REPEAT_MODE_ALL + addListener(object : Player.Listener { + override fun onPlaybackStateChanged(state: Int) { + when (state) { + Player.STATE_BUFFERING -> { + playerCallBack?.onBuffering() + } + Player.STATE_READY -> { + playerCallBack?.onReady() + } + else -> {} } - else -> {} } - } - override fun onIsPlayingChanged(isPlaying: Boolean) { - playerCallBack?.onIsPlayingChanged(isPlaying) - job?.cancel() - if (isPlaying) { - job = scope.launch { - while (true) { - delay(1000) - playerProgressCallBack?.onTimeChanged(contentPosition()) + override fun onIsPlayingChanged(isPlaying: Boolean) { + playerCallBack?.onIsPlayingChanged(isPlaying) + job?.cancel() + if (isPlaying) { + job = scope.launch { + while (true) { + delay(1000) + playerProgressCallBack?.onTimeChanged(contentPosition()) + } } } } + }) + + ProgressiveMediaSource.Factory( + CacheDataSourceFactory( + context, + 5L * 1024L * 1024L, + ) + ).createMediaSource(MediaItem.fromUri(url)).also { + setMediaSource(it) } - }) - - ProgressiveMediaSource.Factory( - CacheDataSourceFactory( - context, - 5L * 1024L * 1024L, - ) - ).createMediaSource(MediaItem.fromUri(url)).also { - setMediaSource(it) + playerCallBack?.onPrepareStart() + prepare() + seekTo(VideoPool.get(url)) } - playerCallBack?.onPrepareStart() - prepare() - seekTo(VideoPool.get(url)) - } } actual fun play() { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt index bb2f795f4..44e21e3d9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt @@ -33,7 +33,7 @@ internal class DraftDaoImpl(private val roomDraftDao: RoomDraftDao) : DraftDao { it.map { dbDraft -> dbDraft.toUiDraft() } } - override fun getDraftCount() = roomDraftDao.getDraftCount() + override fun getDraftCount() = roomDraftDao.getDraftCount().map { it.toLong() } override suspend fun insert(it: UiDraft) = roomDraftDao.insertAll(it.toDbDraft()) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt index 68bb769b8..c84c11699 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt @@ -44,7 +44,7 @@ fun Context.shareText(content: String) { type = "text/plain" }.let { Intent.createChooser(it, null).apply { - if (this !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + if (this@shareText !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } } ) @@ -59,7 +59,7 @@ fun Context.shareMedia(uri: Uri, mimeType: String) { addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }.let { Intent.createChooser(it, null).apply { - if (this !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + if (this@shareMedia !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } } ) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 02d7facf3..c10ea2975 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -26,24 +26,24 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import coil.ImageLoader import coil.annotation.ExperimentalCoilApi -import coil.compose.ImagePainter import coil.compose.LocalImageLoader -import coil.compose.rememberImagePainter +import coil.compose.rememberAsyncImagePainter import coil.decode.GifDecoder import coil.decode.ImageDecoderDecoder -import coil.size.OriginalSize -import coil.transform.BlurTransformation -import coil.util.CoilUtils +import coil.disk.DiskCache +import coil.request.ImageRequest +import coil.size.Size import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.preferences.LocalHttpConfig -import okhttp3.Cache +import com.twidere.twiderex.utils.BlurTransformation import okhttp3.Request import java.io.File import java.net.URL + @OptIn(coil.annotation.ExperimentalCoilApi::class) @Composable internal actual fun rememberNetworkImagePainter( @@ -55,41 +55,39 @@ internal actual fun rememberNetworkImagePainter( onImageStateChanged: (NetworkImageState) -> Unit ): Painter { val context = LocalContext.current - return rememberImagePainter( - data = data, - imageLoader = buildImageLoader(cacheDir), - builder = { - size(OriginalSize) - crossfade(effects.crossFade) - if (effects.blur != null) { - transformations( - BlurTransformation( - context = context, - radius = effects.blur.blurRadius, - sampling = effects.blur.bitmapScale + return rememberAsyncImagePainter( + model = ImageRequest + .Builder(context) + .data(data) + .apply { + size(Size.ORIGINAL) + crossfade(effects.crossFade) + if (effects.blur != null) { + transformations( + BlurTransformation( + context = context, + radius = effects.blur.blurRadius, + ) ) + } + listener( + onSuccess = { _, _ -> onImageStateChanged(NetworkImageState.SUCCESS) }, + onError = { _, _ -> onImageStateChanged(NetworkImageState.ERROR) }, + onStart = { onImageStateChanged(NetworkImageState.LOADING) } ) - } - listener( - onSuccess = { _, _ -> onImageStateChanged(NetworkImageState.SUCCESS) }, - onError = { _, _ -> onImageStateChanged(NetworkImageState.ERROR) }, - onStart = { onImageStateChanged(NetworkImageState.LOADING) } - ) - if (authorization.hasAuthorization) { - addHeader( - "Authorization", - authorization.getAuthorizationHeader( - Request.Builder() - .url(URL(data.toString())) - .build() + if (authorization.hasAuthorization) { + addHeader( + "Authorization", + authorization.getAuthorizationHeader( + Request.Builder() + .url(URL(data.toString())) + .build() + ) ) - ) + } } - }, - onExecute = { previous, current -> - (current.state !is ImagePainter.State.Success && previous?.request != current.request) || - current.state == ImagePainter.State.Empty - } + .build(), + imageLoader = buildImageLoader(cacheDir) ) } @@ -104,18 +102,22 @@ private fun buildImageLoader(cacheDir: String): ImageLoader { if (httpConfig.proxyConfig.enable && httpConfig.proxyConfig.server.isNotEmpty() ) { - callFactory( + callFactory { TwidereServiceFactory.createHttpClientFactory() .createHttpClientBuilder() - .cache(Cache(File(cacheDir), CoilUtils.createDefaultCache(context).maxSize())) .build() - ) + } + diskCache { + DiskCache.Builder(context) + .directory(File(cacheDir)) + .build() + } } - }.componentRegistry { + }.components { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - add(ImageDecoderDecoder(context)) + add(ImageDecoderDecoder.Factory()) } else { - add(GifDecoder()) + add(GifDecoder.Factory()) } } .build() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index c8b430ff7..0267fd974 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -20,7 +20,6 @@ */ package com.twidere.twiderex.kmp -import android.app.Activity import android.content.Context import android.content.Intent import android.net.Uri @@ -35,7 +34,7 @@ actual class RemoteNavigator(private val context: Context) { Intent.ACTION_VIEW, Uri.parse(deeplink).normalizeScheme() ).apply { - if (this !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + if (fromBackground) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 4acf636e8..a6d9ffcfd 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -24,7 +24,7 @@ import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import coil.compose.LocalImageLoader -import coil.compose.rememberImagePainter +import coil.compose.rememberAsyncImagePainter import coil.decode.SvgDecoder import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource @@ -44,10 +44,10 @@ actual class ResLoader( @Composable actual fun getSvg(res: FileResource): Painter { val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" - return rememberImagePainter( - data = data, + return rememberAsyncImagePainter( + model = data, imageLoader = LocalImageLoader.current.newBuilder() - .componentRegistry { add(SvgDecoder(context)) } + .components { add(SvgDecoder.Factory()) } .build(), ) } @@ -56,10 +56,10 @@ actual class ResLoader( @Composable actual fun getImage(res: ImageResource): Painter { val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" - return rememberImagePainter( - data = data, + return rememberAsyncImagePainter( + model = data, imageLoader = LocalImageLoader.current.newBuilder() - .componentRegistry { add(SvgDecoder(context)) } + .components { add(SvgDecoder.Factory()) } .build(), ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 8d3e12b44..73058045f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -28,7 +28,7 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.graphics.drawable.toBitmap -import coil.Coil +import coil.imageLoader import coil.request.ImageRequest import coil.request.SuccessResult import com.twidere.common.R @@ -80,7 +80,7 @@ actual class AppNotificationManager( ) } appNotification.largeIcon?.let { - val result = Coil.execute( + val result = context.imageLoader.execute( ImageRequest.Builder(context) .data(it) .build() diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt index b7f1d0b3c..62545f422 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt @@ -43,5 +43,5 @@ internal interface RoomDraftDao { suspend fun remove(draft: DbDraft) @Query("SELECT COUNT(*) FROM draft") - fun getDraftCount(): Flow + fun getDraftCount(): Flow } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt new file mode 100644 index 000000000..14e40ead6 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt @@ -0,0 +1,69 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils + +import android.content.Context +import android.graphics.Bitmap +import androidx.annotation.RequiresApi +import coil.size.Size +import coil.transform.Transformation +import com.google.android.renderscript.Toolkit + +@RequiresApi(18) +class BlurTransformation @JvmOverloads constructor( + private val context: Context, + private val radius: Int = DEFAULT_RADIUS, + // private val sampling: Int = DEFAULT_SAMPLING +) : Transformation { + + init { + require(radius in 0..25) { "radius must be in [0, 25]." } + // require(sampling > 0) { "sampling must be > 0." } + } + + override val cacheKey: String + get() = "${BlurTransformation::class.java.name}-$radius" + + override suspend fun transform(input: Bitmap, size: Size): Bitmap { + return Toolkit.blur(input, radius) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + return other is BlurTransformation && + context == other.context && + radius == other.radius + } + + override fun hashCode(): Int { + var result = context.hashCode() + result = 31 * result + radius.hashCode() + return result + } + + override fun toString(): String { + return "BlurTransformation(context=$context, radius=$radius)" + } + + private companion object { + private const val DEFAULT_RADIUS = 10 + } +} diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt index 259aebdc2..02f7c91e7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt @@ -23,7 +23,7 @@ package com.twidere.twiderex.utils.video import android.content.Context import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory +import com.google.android.exoplayer2.upstream.DefaultDataSource import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.upstream.FileDataSource import com.google.android.exoplayer2.upstream.cache.CacheDataSink @@ -39,7 +39,7 @@ class CacheDataSourceFactory( VideoCache.getInstance(context) } - private val defaultDatasourceFactory: DefaultDataSourceFactory + private val defaultDatasourceFactory: DefaultDataSource.Factory override fun createDataSource(): DataSource { return CacheDataSource( simpleCache, defaultDatasourceFactory.createDataSource(), @@ -55,12 +55,11 @@ class CacheDataSourceFactory( "" ) val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build() - defaultDatasourceFactory = DefaultDataSourceFactory( + defaultDatasourceFactory = DefaultDataSource.Factory( this.context, - bandwidthMeter, DefaultHttpDataSource.Factory() .setUserAgent(userAgent) .setTransferListener(bandwidthMeter) - ) + ).setTransferListener(bandwidthMeter) } } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt index 3b1bc5235..671be7cb8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.utils.video import android.content.Context -import com.google.android.exoplayer2.database.ExoDatabaseProvider +import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache import java.io.File @@ -32,7 +32,7 @@ object VideoCache { fun getInstance(context: Context): SimpleCache { val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize) if (simpleCache == null) simpleCache = - SimpleCache(File(context.cacheDir, "media"), evictor, ExoDatabaseProvider(context)) + SimpleCache(File(context.cacheDir, "media"), evictor, StandaloneDatabaseProvider(context)) return simpleCache as SimpleCache } } diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt index f7d09beb8..575255aa8 100644 --- a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt +++ b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt @@ -299,7 +299,3 @@ public fun LazyListScope.itemsIndexed( itemContent(index, items[index]) } } - -expect class PagingPlaceholderKey(index: Int) { - val index: Int -} diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt b/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt new file mode 100644 index 000000000..a7327b3cb --- /dev/null +++ b/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt @@ -0,0 +1,25 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package androidx.paging.compose + +expect class PagingPlaceholderKey(index: Int) { + val index: Int +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt index 6ef950f2d..aeda1278d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt @@ -21,12 +21,12 @@ package com.twidere.twiderex.component.image class ImageBlur( - val blurRadius: Float, + val blurRadius: Int, val bitmapScale: Float, ) { companion object { val Sensitive = ImageBlur( - blurRadius = 25f, + blurRadius = 25, bitmapScale = 0.4f ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt index b06e0d3d2..b362934ba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.extensions import moe.tlaster.precompose.viewmodel.ViewModel +import org.koin.core.annotation.KoinReflectAPI import org.koin.core.definition.Definition import org.koin.core.instance.InstanceFactory import org.koin.core.instance.newInstance @@ -34,6 +35,7 @@ inline fun Module.viewModel( return factory(qualifier, definition) } +@KoinReflectAPI inline fun Module.viewModel( qualifier: Qualifier? = null ): Pair> { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt similarity index 100% rename from common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Twitter.kt rename to common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index f9c00f13d..928e4fc69 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.notification import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime expect class AppNotificationManager { @@ -30,7 +31,7 @@ expect class AppNotificationManager { fun notifyTransient( notificationId: Int, appNotification: AppNotification, - duration: Duration = Duration.Companion.seconds(5), + duration: Duration = 5.seconds, ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt index e4159db94..e7331f07c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt @@ -47,6 +47,7 @@ abstract class MemoryCachePagingMediator(protected val m is PagingSource.LoadResult.Error -> { throw result.throwable } + is PagingSource.LoadResult.Invalid -> Unit } MediatorResult.Success(paging == null) } catch (e: Exception) { diff --git a/common/src/desktopMain/kotlin/androidx.paging.compose/LazyPagingItems.kt b/common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt similarity index 100% rename from common/src/desktopMain/kotlin/androidx.paging.compose/LazyPagingItems.kt rename to common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt index a2be1ceb1..41f3071c9 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.image import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.asComposeImageBitmap import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.IntSize @@ -66,7 +66,7 @@ internal class GifPainter(private val codec: Codec, private val parentScope: Cor val bitmap = recycleBitmap(codec) codec.readPixels(bitmap, frameIndex.value) val intSize = IntSize(size.width.toInt(), size.height.toInt()) - drawImage(bitmap.asImageBitmap(), dstSize = intSize) + drawImage(bitmap.asComposeImageBitmap(), dstSize = intSize) } private fun recycleBitmap(codec: Codec): Bitmap { diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index 9865ae942..e125713fe 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -24,9 +24,9 @@ import androidx.compose.runtime.RememberObserver import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.asPainter import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.toPainter import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects import kotlinx.coroutines.CoroutineScope @@ -118,7 +118,7 @@ internal class ImagePainter( ImageEffectsFilter.applyBlurFilter(image, it.blurRadius.toInt(), it.bitmapScale) } ?: image }?.let { - painter.value = it.asPainter() + painter.value = it.toPainter() } } } catch (e: Throwable) { diff --git a/services/src/main/java/com/twidere/services/twitter/ErrorCodes.kt b/services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt similarity index 100% rename from services/src/main/java/com/twidere/services/twitter/ErrorCodes.kt rename to services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt From 5c3d570bc60eae7b59804ab35490d1e9fbe450b7 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 16:26:31 +0800 Subject: [PATCH 482/615] fix test --- .../db/sqldelight/NotificationCursorQueriesImplTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt index 17cad85a6..dc7a54f28 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt @@ -41,21 +41,21 @@ internal class NotificationCursorQueriesImplTest : BaseCacheDatabaseTest() { ) @Test fun insert_ReplaceWhenPrimaryKeyEquals() = runBlocking { - database.notificationCursorQueries.insert(cursor.toDb().copy(value = "insert")) + database.notificationCursorQueries.insert(cursor.toDb().copy(value_ = "insert")) assertEquals( "insert", database.notificationCursorQueries.find( accountKey = cursor.accountKey, type = cursor.type - ).executeAsOneOrNull()?.value + ).executeAsOneOrNull()?.value_ ) - database.notificationCursorQueries.insert(cursor.toDb().copy(value = "replace")) + database.notificationCursorQueries.insert(cursor.toDb().copy(value_ = "replace")) assertEquals( "replace", database.notificationCursorQueries.find( accountKey = cursor.accountKey, type = cursor.type - ).executeAsOneOrNull()?.value + ).executeAsOneOrNull()?.value_ ) } } From 86526ba52b76eb3f510ba8e22010cd5cbcb3f59d Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 14 Dec 2021 16:47:49 +0800 Subject: [PATCH 483/615] refactor route processor --- .../com/twidere/twiderex/navigation/IRoute.kt | 70 +++++++++++++++++++ routeProcessor/src/main/kotlin/AppRoute.kt | 2 - .../src/main/kotlin/RouteDefinition.kt | 63 +++++++---------- .../src/main/kotlin/RouteProcessor.kt | 29 +++----- 4 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt new file mode 100644 index 000000000..421b9459a --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt @@ -0,0 +1,70 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.navigation + +import androidx.compose.runtime.Composable +import moe.tlaster.precompose.navigation.BackStackEntry +import moe.tlaster.precompose.navigation.RouteBuilder +import moe.tlaster.precompose.navigation.transition.NavTransition + +interface IRoute { + val route: String +} + +fun List.mapToString() = map { + when (it) { + is String -> it + is IRoute -> it.route + else -> it.toString() + } +} + +@Suppress("NOTHING_TO_INLINE") +inline fun RouteBuilder.scene( + route: IRoute, + deepLinks: List = emptyList(), + navTransition: NavTransition? = null, + noinline content: @Composable (BackStackEntry) -> Unit, +) = scene( + route = route.route, + deepLinks = deepLinks.mapToString(), + navTransition = navTransition, + content = content +) + +@Suppress("NOTHING_TO_INLINE") +inline fun RouteBuilder.authorizedScene( + route: IRoute, + deepLinks: List = emptyList(), + navTransition: NavTransition? = null, + noinline content: @Composable (BackStackEntry) -> Unit, +) = authorizedScene( + route = route.route, + deepLinks = deepLinks.mapToString(), + navTransition = navTransition, + content = content +) + +@Suppress("NOTHING_TO_INLINE") +inline fun RouteBuilder.authorizedDialog( + route: IRoute, + noinline content: @Composable (BackStackEntry) -> Unit, +) = authorizedDialog(route.route, content) diff --git a/routeProcessor/src/main/kotlin/AppRoute.kt b/routeProcessor/src/main/kotlin/AppRoute.kt index 6f3adf195..a0746a4ec 100644 --- a/routeProcessor/src/main/kotlin/AppRoute.kt +++ b/routeProcessor/src/main/kotlin/AppRoute.kt @@ -25,6 +25,4 @@ package com.twidere.route.processor annotation class AppRoute( val schema: String = "", val packageName: String = "", - val routeClassName: String = "", - val definitionClassName: String = "", ) diff --git a/routeProcessor/src/main/kotlin/RouteDefinition.kt b/routeProcessor/src/main/kotlin/RouteDefinition.kt index 421ad2c6f..59ffedc7e 100644 --- a/routeProcessor/src/main/kotlin/RouteDefinition.kt +++ b/routeProcessor/src/main/kotlin/RouteDefinition.kt @@ -26,7 +26,6 @@ private const val RouteDivider = "/" internal interface RouteDefinition { val name: String val parent: RouteDefinition? - fun generateDefinition(): String fun generateRoute(): String } @@ -52,8 +51,7 @@ internal val RouteDefinition.indent internal data class PrefixRouteDefinition( val schema: String, val child: NestedRouteDefinition, - val routeClassName: String, - val definitionClassName: String, + val className: String, ) : RouteDefinition { override val name: String get() = if (schema.isEmpty()) "" else "$schema:$RouteDivider" @@ -64,30 +62,43 @@ internal data class PrefixRouteDefinition( child.parent = this } - override fun generateDefinition(): String { - return child.copy(name = definitionClassName).generateDefinition() - } - override fun generateRoute(): String { - return child.copy(name = routeClassName).generateRoute() + return child.copy(name = className).generateRoute() } } internal data class NestedRouteDefinition( override val name: String, override var parent: RouteDefinition? = null, - val fullName: String, + val iRouteName: String, val childRoute: ArrayList = arrayListOf(), ) : RouteDefinition { - override fun generateDefinition(): String { - return "${indent}object $name {${System.lineSeparator()}" + - childRoute.joinToString(System.lineSeparator()) { it.generateDefinition() } + + + override fun generateRoute(): String { + return if (iRouteName.isEmpty()) generateRootRoute() else generateIRoute() + } + + private fun generateRootRoute(): String { + return "${indent}actual object $name {${System.lineSeparator()}" + + childRoute.joinToString(System.lineSeparator()) { it.generateRoute() } + System.lineSeparator() + "$indent}" } - override fun generateRoute(): String { - return "${indent}object $name: $fullName {${System.lineSeparator()}" + + private fun generateIRoute(): String { + var overrideRoute = "" + + val functions = childRoute.find { it is FunctionRouteDefinition } as? FunctionRouteDefinition + if (functions != null) { + val pathWithParameter = functions.parameters + .filter { !it.isNullable } + .joinToString(RouteDivider) { "{${it.name}}" } + .let { if (it.isNotEmpty()) RouteDivider + it else it } + overrideRoute = "override val route = \"${functions.parentPath}$pathWithParameter\"" + } + + return "${indent}actual object $name: $iRouteName {${System.lineSeparator()}" + + "${indent}$StandardIndent$overrideRoute${System.lineSeparator()}" + childRoute.joinToString(System.lineSeparator()) { it.generateRoute() } + System.lineSeparator() + "$indent}" @@ -98,12 +109,8 @@ internal data class ConstRouteDefinition( override val name: String, override val parent: RouteDefinition? = null, ) : RouteDefinition { - override fun generateDefinition(): String { - return "${indent}const val $name = \"$parentPath$RouteDivider${name}\"" - } - override fun generateRoute(): String { - return "${indent}override val $name = \"$parentPath$RouteDivider${name}\"" + return "${indent}actual val $name = \"$parentPath$RouteDivider${name}\"" } } @@ -112,22 +119,6 @@ internal data class FunctionRouteDefinition( override val parent: RouteDefinition? = null, val parameters: List, ) : RouteDefinition { - override fun generateDefinition(): String { - val path = parameters - .filter { !it.isNullable } - .joinToString(RouteDivider) { parameter -> - "{${parameter.name}}" - } - .let { - if (it.isNotEmpty()) { - "$RouteDivider$it" - } else { - it - } - } - return "${indent}const val $name = \"$parentPath$RouteDivider$name$path\"" - } - override fun generateRoute(): String { val query = parameters .filter { it.isNullable } @@ -177,7 +168,7 @@ internal data class FunctionRouteDefinition( } } - return "${indent}override fun $name($parameterStr) = \"$parentPath$RouteDivider$name$pathWithParameter${query}\"" + return "${indent}actual operator fun $name($parameterStr) = \"$parentPath$pathWithParameter${query}\"" } private fun encode(value: String) = "\${java.net.URLEncoder.encode($value, \"UTF-8\")}" diff --git a/routeProcessor/src/main/kotlin/RouteProcessor.kt b/routeProcessor/src/main/kotlin/RouteProcessor.kt index e22afe441..4b316fb34 100644 --- a/routeProcessor/src/main/kotlin/RouteProcessor.kt +++ b/routeProcessor/src/main/kotlin/RouteProcessor.kt @@ -22,7 +22,6 @@ package com.twidere.route.processor import com.google.devtools.ksp.processing.CodeGenerator import com.google.devtools.ksp.processing.Dependencies -import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.symbol.KSAnnotated @@ -37,7 +36,6 @@ import java.io.OutputStream internal class RouteProcessor( private val codeGenerator: CodeGenerator, - private val logger: KSPLogger, ) : SymbolProcessor { override fun process(resolver: Resolver): List { val routeSymbol = resolver @@ -63,10 +61,7 @@ internal class RouteProcessor( val schema = annotation.getStringValue(AppRoute::schema.name) ?: "" val packageName = annotation.getStringValue(AppRoute::packageName.name) ?: node.packageName.asString() - val routeClassName = annotation.getStringValue(AppRoute::routeClassName.name) - ?: "${node.qualifiedName?.getShortName()}Route" - val definitionClassName = annotation.getStringValue(AppRoute::definitionClassName.name) - ?: "${node.qualifiedName?.getShortName()}RouteDefinition" + val className = node.qualifiedName!!.getShortName() val route = generateRoute(declaration = node) .takeIf { @@ -75,8 +70,7 @@ internal class RouteProcessor( PrefixRouteDefinition( schema = schema, child = it as NestedRouteDefinition, - routeClassName = routeClassName, - definitionClassName = definitionClassName, + className = className, ) } ?: return @@ -87,15 +81,9 @@ internal class RouteProcessor( generateFile( dependencies, packageName, - routeClassName, + className, route.generateRoute() ) - generateFile( - dependencies, - packageName, - definitionClassName, - route.generateDefinition() - ) } private fun generateFile( @@ -122,15 +110,18 @@ internal class RouteProcessor( val name = declaration.simpleName.getShortName() return when (declaration) { is KSClassDeclaration -> { + val isIRoute = declaration.superTypes.any { + it.resolve().declaration.simpleName.getShortName() == "IRoute" + } NestedRouteDefinition( name = name, parent = parent, - fullName = declaration.qualifiedName?.asString() ?: "" + iRouteName = if (isIRoute) "com.twidere.twiderex.navigation.IRoute" else "" ).also { nestedRouteDefinition -> nestedRouteDefinition.childRoute.addAll( - declaration.declarations.map { - generateRoute(it, nestedRouteDefinition) - } + declaration.declarations + .filter { it.simpleName.getShortName() != "" } + .map { generateRoute(it, nestedRouteDefinition) } ) } } From 0ac29fd7e24573bd681b74748f81043377b864b8 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Dec 2021 16:51:14 +0800 Subject: [PATCH 484/615] fixed LayoutScene icon not display --- .../com/twidere/twiderex/scenes/settings/LayoutScene.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index 8430a4f3a..29b2921bb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -23,6 +23,7 @@ package com.twidere.twiderex.scenes.settings import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Card @@ -44,6 +45,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold @@ -204,9 +206,10 @@ private fun LayoutItemContent( res = if (visible) { com.twidere.twiderex.MR.files.ic_delete_colored } else { - com.twidere.twiderex.MR.files.ic_add + com.twidere.twiderex.MR.files.ic_add_colored } ), + modifier = Modifier.size(24.dp), contentDescription = null, ) } From e093a1f0a6976defc3a64681f4144836bcad1d56 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 16:58:22 +0800 Subject: [PATCH 485/615] fix build --- android/build.gradle.kts | 2 -- build.gradle.kts | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 403d461f1..1887c1eb6 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -88,8 +88,6 @@ android { if (hasSigningProps) { signingConfig = signingConfigs.getByName("twidere") } - // isMinifyEnabled = true - // isShrinkResources = true } release { if (hasSigningProps) { diff --git a/build.gradle.kts b/build.gradle.kts index f26e061f9..b9663d0ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,6 +23,7 @@ allprojects { allWarningsAsErrors = true freeCompilerArgs = listOf( "-Xopt-in=kotlin.RequiresOptIn", + "-Xjvm-default=all", ) } } From aa2d5522dbe6dc0e7354e041afe52e00b7f662a8 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 14 Dec 2021 16:58:38 +0800 Subject: [PATCH 486/615] replace all Routes and Definitions --- .../service/AccountAuthenticatorService.kt | 6 +- .../twiderex/navigation/Route.android.kt | 2 +- .../twiderex/component/UserComponent.kt | 10 +- .../component/lazy/ui/LazyUiDMEventList.kt | 4 +- .../component/media/MediaInsertMenu.kt | 4 +- .../component/navigation/Navigator.kt | 18 +-- .../component/requireAuthorization.kt | 4 +- .../twiderex/dataprovider/mapper/Mastodon.kt | 6 +- .../twiderex/dataprovider/mapper/Twitter.kt | 11 +- .../twiderex/jobs/common/NotificationJob.kt | 16 +-- .../twiderex/jobs/compose/ComposeJob.kt | 6 +- .../twiderex/jobs/dm/DirectMessageFetchJob.kt | 4 +- .../twiderex/jobs/dm/DirectMessageSendJob.kt | 4 +- .../com/twidere/twiderex/navigation/Root.kt | 132 +++++++++++++----- .../twiderex/navigation/RootDeepLinks.kt | 52 +++++-- .../com/twidere/twiderex/navigation/Route.kt | 118 ++++++++-------- .../twidere/twiderex/scenes/DraftListScene.kt | 4 +- .../com/twidere/twiderex/scenes/HomeScene.kt | 8 +- .../twidere/twiderex/scenes/SignInScene.kt | 8 +- .../twiderex/scenes/compose/ComposeScene.kt | 10 +- .../scenes/dm/DMConversationListScene.kt | 6 +- .../scenes/dm/DMNewConversationScene.kt | 6 +- .../scenes/home/DMConversationListItem.kt | 4 +- .../scenes/home/DraftNavigationItem.kt | 4 +- .../twiderex/scenes/home/HomeTimelineItem.kt | 4 +- .../scenes/home/ListsNavigationItem.kt | 4 +- .../twidere/twiderex/scenes/home/MeItem.kt | 4 +- .../twiderex/scenes/home/MentionItem.kt | 4 +- .../twiderex/scenes/home/NotificationItem.kt | 4 +- .../twiderex/scenes/home/SearchItem.kt | 4 +- .../home/mastodon/FederatedTimelineItem.kt | 4 +- .../scenes/home/mastodon/LocalTimelineItem.kt | 4 +- .../home/mastodon/MastodonNotificationItem.kt | 4 +- .../scenes/lists/ListsMembersScene.kt | 4 +- .../twiderex/scenes/lists/ListsScene.kt | 8 +- .../scenes/lists/ListsTimelineScene.kt | 8 +- .../platform/MastodonListsCreateDialog.kt | 4 +- .../lists/platform/TwitterListsCreateScene.kt | 6 +- .../twiderex/scenes/settings/AboutScene.kt | 4 +- .../scenes/settings/AccountManagementScene.kt | 4 +- .../scenes/settings/NotificationScene.kt | 4 +- .../twiderex/scenes/settings/SettingsScene.kt | 16 +-- .../scenes/twitter/user/TwitterUserScene.kt | 4 +- .../twidere/twiderex/scenes/user/UserScene.kt | 4 +- .../twiderex/utils/CustomTabSignInChannel.kt | 6 +- .../mastodon/MastodonSignInViewModel.kt | 4 +- .../twitter/TwitterSignInViewModel.kt | 4 +- 47 files changed, 326 insertions(+), 237 deletions(-) diff --git a/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt b/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt index 1de3d9f3b..0a35129f3 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt @@ -31,7 +31,7 @@ import android.net.Uri import android.os.Bundle import android.os.IBinder import androidx.core.os.bundleOf -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks class AccountAuthenticatorService : Service() { @@ -56,7 +56,7 @@ class AccountAuthenticatorService : Service() { requiredFeatures: Array?, options: Bundle? ): Bundle { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(RootDeepLinksRoute.SignIn)) + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(RootDeepLinks.SignIn)) intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) return bundleOf( AccountManager.KEY_INTENT to intent, @@ -72,7 +72,7 @@ class AccountAuthenticatorService : Service() { val am = AccountManager.get(context) val authToken = am.peekAuthToken(account, authTokenType) if (authToken.isNullOrEmpty()) { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(RootDeepLinksRoute.SignIn)) + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(RootDeepLinks.SignIn)) intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) return bundleOf( AccountManager.KEY_INTENT to intent, diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt index fe73870ba..86c3f37a4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt @@ -27,7 +27,7 @@ import java.net.URLDecoder actual fun RouteBuilder.platformScene() { scene( - RootRouteDefinition.SignIn.Web.Twitter, + Root.SignIn.Web.Twitter, ) { backStackEntry -> backStackEntry.path("target")?.let { TwitterWebSignInScene(target = URLDecoder.decode(it, "UTF-8")) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt index 65debd946..07990678d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -101,7 +101,7 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiUrlEntity import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.navigation.twidereXSchema import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.user.UserFavouriteTimelineViewModel @@ -460,7 +460,7 @@ fun UserInfo( user = user, size = UserInfoDefaults.AvatarSize ) { - navController.navigate(RootRoute.Media.Raw(MediaType.photo, user.profileImage)) + navController.navigate(Root.Media.Raw(MediaType.photo, user.profileImage)) } } } @@ -735,7 +735,7 @@ private fun UserBanner( .heightIn(max = maxBannerSize) .clickable( onClick = { - navController.navigate(RootRoute.Media.Raw(MediaType.photo, bannerUrl)) + navController.navigate(Root.Media.Raw(MediaType.photo, bannerUrl)) }, indication = null, interactionSource = remember { MutableInteractionSource() }, @@ -763,7 +763,7 @@ fun UserMetrics( modifier = Modifier .weight(1f) .clickable { - navController.navigate(RootRoute.Following(user.userKey)) + navController.navigate(Root.Following(user.userKey)) }, primaryText = user.metrics.follow.toString(), secondaryText = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_following), @@ -775,7 +775,7 @@ fun UserMetrics( modifier = Modifier .weight(1f) .clickable { - navController.navigate(RootRoute.Followers(user.userKey)) + navController.navigate(Root.Followers(user.userKey)) }, primaryText = user.metrics.fans.toString(), secondaryText = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_profile_dashboard_followers), diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 8e8127bfe..5e1ee30b8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -77,7 +77,7 @@ import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.kmp.TimeUtils import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMedia -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.preferences.model.DisplayPreferences import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.LocalVideoPlayback @@ -243,7 +243,7 @@ private fun MessageBody(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> MediaMessage( media = event.media.firstOrNull(), onClick = { - navController.navigate(RootRoute.Media.Pure(event.messageKey, 0)) + navController.navigate(Root.Media.Pure(event.messageKey, 0)) } ) if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer(modifier = Modifier.height(MessageBodyDefaults.ContentSpacing)) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index 2c1a45d32..24c5cf7b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -48,7 +48,7 @@ import com.twidere.twiderex.kmp.PlatformMediaWrapper import com.twidere.twiderex.kmp.currentPlatform import com.twidere.twiderex.model.enums.MediaInsertType import com.twidere.twiderex.model.ui.UiMediaInsert -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import kotlinx.coroutines.launch import moe.tlaster.kfilepicker.FilePicker @@ -110,7 +110,7 @@ fun MediaInsertMenu( } } MediaInsertType.GIF -> scope.launch { - navController.navigateForResult(RootRoute.Gif.Home) + navController.navigateForResult(Root.Gif.Home) ?.let { result -> onResult( listOf(result as String).map { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt index ba2f3c825..226794325 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.navigation.twidereXSchema import moe.tlaster.precompose.navigation.NavController import moe.tlaster.precompose.navigation.NavOptions @@ -67,7 +67,7 @@ class Navigator( private val remoteNavigator: RemoteNavigator, ) : INavigator { override fun user(user: UiUser, navOptions: NavOptions?) { - navController.navigate(RootRoute.User(user.userKey), navOptions) + navController.navigate(Root.User(user.userKey), navOptions) } override fun status(status: UiStatus, navOptions: NavOptions?) { @@ -89,7 +89,7 @@ class Navigator( } if (statusKey != null) { navController.navigate( - RootRoute.Status(statusKey), + Root.Status(statusKey), navOptions ) } @@ -100,16 +100,16 @@ class Navigator( selectedIndex: Int, navOptions: NavOptions? ) { - navController.navigate(RootRoute.Media.Status(statusKey, selectedIndex), navOptions) + navController.navigate(Root.Media.Status(statusKey, selectedIndex), navOptions) } override fun search(keyword: String) { - navController.navigate(RootRoute.Search.Result(keyword)) + navController.navigate(Root.Search.Result(keyword)) } override fun searchInput(initial: String?) { navController.navigate( - RootRoute.Search.Input(initial), + Root.Search.Input(initial), ) } @@ -118,7 +118,7 @@ class Navigator( statusKey: MicroBlogKey?, navOptions: NavOptions? ) { - navController.navigate(RootRoute.Compose.Home(composeType, statusKey)) + navController.navigate(Root.Compose.Home(composeType, statusKey)) } override fun openLink(it: String, deepLink: Boolean) { @@ -138,12 +138,12 @@ class Navigator( override suspend fun twitterSignInWeb(target: String): String { clearCookie() return navController.navigateForResult( - RootRoute.SignIn.Web.Twitter(target) + Root.SignIn.Web.Twitter(target) ).toString() } override fun hashtag(name: String) { - navController.navigate(RootRoute.Mastodon.Hashtag(name)) + navController.navigate(Root.Mastodon.Hashtag(name)) } override fun goBack() { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt index 9f6025cc4..0ed801e3b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt @@ -22,7 +22,7 @@ package com.twidere.twiderex.component import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import moe.tlaster.precompose.ui.LocalBackDispatcherOwner @@ -36,7 +36,7 @@ fun RequireAuthorization( val navController = LocalNavController.current val backDispatcher = LocalBackDispatcherOwner.current?.backDispatcher LaunchedEffect(Unit) { - val result = navController.navigateForResult(RootRoute.SignIn.General) + val result = navController.navigateForResult(Root.SignIn.General) if (result == null) { backDispatcher?.onBackPress() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index 4d4b21e1d..c7434cc57 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -54,7 +54,7 @@ import com.twidere.twiderex.model.ui.mastodon.Field import com.twidere.twiderex.model.ui.mastodon.MastodonMention import com.twidere.twiderex.model.ui.mastodon.MastodonStatusExtra import com.twidere.twiderex.model.ui.mastodon.MastodonUserExtra -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -411,7 +411,7 @@ private fun replaceMention(mentions: List, node: Node, accountKey: Micr if (id != null) { node.attr( "href", - RootDeepLinksRoute.User(MicroBlogKey(id, accountKey.host)) + RootDeepLinks.User(MicroBlogKey(id, accountKey.host)) ) } } else { @@ -431,7 +431,7 @@ private fun replaceHashTag(node: Node) { ) { node.attr( "href", - RootDeepLinksRoute.Mastodon.Hashtag(node.text().trimStart('#')) + RootDeepLinks.Mastodon.Hashtag(node.text().trimStart('#')) ) } else { node.childNodes().forEach { replaceHashTag(it) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index bcb5bf8d1..02f9d0186 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -50,18 +50,21 @@ import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.model.ui.UserMetrics import com.twidere.twiderex.model.ui.twitter.TwitterStatusExtra import com.twidere.twiderex.model.ui.twitter.TwitterUserExtra -import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition +import com.twidere.twiderex.navigation.IRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twitter.twittertext.Autolink val autolink by lazy { Autolink().apply { setUsernameIncludeSymbol(true) - hashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%23" - cashtagUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Search)}/%24" - usernameUrlBase = "${generateDeepLinkBase(RootDeepLinksRouteDefinition.Twitter.User)}/" + hashtagUrlBase = "${generateDeepLinkBase(RootDeepLinks.Search)}/%23" + cashtagUrlBase = "${generateDeepLinkBase(RootDeepLinks.Search)}/%24" + usernameUrlBase = "${generateDeepLinkBase(RootDeepLinks.Twitter.User)}/" } } +private fun generateDeepLinkBase(route: IRoute) = generateDeepLinkBase(route.route) + private fun generateDeepLinkBase(deeplink: String): String { return deeplink.substring( 0, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index 8eb720941..11c2ed433 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -26,7 +26,7 @@ import com.twidere.twiderex.model.enums.MastodonStatusType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.enums.ReferenceType import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec @@ -83,7 +83,7 @@ class NotificationJob( status.user.displayName ), htmlContent = status.htmlText, - deepLink = RootDeepLinksRoute.Twitter.Status(status.statusId), + deepLink = RootDeepLinks.Twitter.Status(status.statusId), profileImage = status.user.profileImage, ) } @@ -125,7 +125,7 @@ class NotificationJob( com.twidere.twiderex.MR.strings.common_notification_follow, actualStatus.user.displayName ), - deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey), + deepLink = RootDeepLinks.User(actualStatus.user.userKey), profileImage = actualStatus.user.profileImage, ) } @@ -135,7 +135,7 @@ class NotificationJob( com.twidere.twiderex.MR.strings.common_notification_follow_request, actualStatus.user.displayName ), - deepLink = RootDeepLinksRoute.User(actualStatus.user.userKey) + deepLink = RootDeepLinks.User(actualStatus.user.userKey) ) } MastodonStatusType.NotificationMention -> { @@ -145,7 +145,7 @@ class NotificationJob( actualStatus.user.displayName ), htmlContent = actualStatus.htmlText, - deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), + deepLink = RootDeepLinks.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, ) } @@ -155,7 +155,7 @@ class NotificationJob( com.twidere.twiderex.MR.strings.common_notification_reblog, actualStatus.user.displayName ), - deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), + deepLink = RootDeepLinks.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, ) } @@ -165,7 +165,7 @@ class NotificationJob( com.twidere.twiderex.MR.strings.common_notification_favourite, actualStatus.user.displayName ), - deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), + deepLink = RootDeepLinks.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, ) } @@ -174,7 +174,7 @@ class NotificationJob( title = resLoader.getString( com.twidere.twiderex.MR.strings.common_notification_poll, ), - deepLink = RootDeepLinksRoute.Status(actualStatus.statusKey), + deepLink = RootDeepLinks.Status(actualStatus.statusKey), profileImage = actualStatus.user.profileImage, ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 6c6f8ac3a..ad9b6e079 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -28,7 +28,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.job.ComposeData import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec @@ -85,7 +85,7 @@ abstract class ComposeJob( if (composeData.isThreadMode) { // open compose scene in thread mode remoteNavigator.openDeepLink( - deeplink = RootDeepLinksRoute.Compose(ComposeType.Thread, status.statusKey), + deeplink = RootDeepLinks.Compose(ComposeType.Thread, status.statusKey), fromBackground = true ) } @@ -96,7 +96,7 @@ abstract class ComposeJob( .setSilent(false) .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_tweet_fail_title)) .setContentText(composeData.content) - .setDeepLink(RootDeepLinksRoute.Draft(composeData.draftId)) + .setDeepLink(RootDeepLinks.Draft(composeData.draftId)) notificationManager.notify(notificationId, builder.build()) throw e } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt index 48fba1167..964b6819c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt @@ -25,7 +25,7 @@ import com.twidere.services.microblog.LookupService import com.twidere.twiderex.kmp.ResLoader import com.twidere.twiderex.model.AccountDetails import com.twidere.twiderex.model.ui.UiDMConversationWithLatestMessage -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec @@ -70,7 +70,7 @@ class DirectMessageFetchJob( message.latestMessage.sender.displayName ) ) - .setDeepLink(RootDeepLinksRoute.Conversation(message.conversation.conversationKey)) + .setDeepLink(RootDeepLinks.Conversation(message.conversation.conversationKey)) notificationManager.notify(message.latestMessage.messageKey.hashCode(), builder.build()) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index d47107026..f939cae1e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -31,7 +31,7 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.job.DirectMessageSendData import com.twidere.twiderex.model.ui.UiDMEvent import com.twidere.twiderex.model.ui.UiMedia -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.AppNotification import com.twidere.twiderex.notification.AppNotificationManager import com.twidere.twiderex.notification.NotificationChannelSpec @@ -84,7 +84,7 @@ abstract class DirectMessageSendJob( ) .setContentTitle(resLoader.getString(com.twidere.twiderex.MR.strings.common_alerts_failed_to_send_message_message)) .setContentText(sendData.text) - .setDeepLink(RootDeepLinksRoute.Conversation(sendData.conversationKey)) + .setDeepLink(RootDeepLinks.Conversation(sendData.conversationKey)) notificationManager.notify(notificationId, builder.build()) throw e } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt index 864614ba5..98f8dac41 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt @@ -26,51 +26,86 @@ import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MediaType @AppRoute -interface Root { +expect object Root { val Home: String val HomeTimeline: String val Notification: String val Mentions: String val Me: String - interface Draft { + object Draft { val List: String - fun Compose(draftId: String): String + + object Compose : IRoute { + operator fun invoke(draftId: String): String + } } - interface SignIn { + object SignIn { val General: String - fun Twitter(consumerKey: String, consumerSecret: String): String + + object Twitter : IRoute { + operator fun invoke(consumerKey: String, consumerSecret: String): String + } + val Mastodon: String - interface Web { - fun Twitter(target: String): String + + object Web { + object Twitter : IRoute { + operator fun invoke(target: String): String + } } } - fun User(userKey: MicroBlogKey): String - interface Media { - fun Status(statusKey: MicroBlogKey, selectedIndex: Int?): String - fun Raw(type: MediaType, url: String): String - fun Pure(belongToKey: MicroBlogKey, selectedIndex: Int?): String + object User : IRoute { + operator fun invoke(userKey: MicroBlogKey): String + } + + object Media { + object Status : IRoute { + operator fun invoke(statusKey: MicroBlogKey, selectedIndex: Int?): String + } + + object Raw : IRoute { + operator fun invoke(type: MediaType, url: String): String + } + + object Pure : IRoute { + operator fun invoke(belongToKey: MicroBlogKey, selectedIndex: Int?): String + } } - interface Search { + object Search { val Home: String - fun Result(keyword: String): String - fun Input(keyword: String?): String + + object Result : IRoute { + operator fun invoke(keyword: String): String + } + + object Input : IRoute { + operator fun invoke(keyword: String?): String + } } - interface Compose { - fun Home(composeType: ComposeType?, statusKey: MicroBlogKey?): String - interface Search { + object Compose { + object Home : IRoute { + operator fun invoke(composeType: ComposeType?, statusKey: MicroBlogKey?): String + } + + object Search { val User: String } } - fun Following(userKey: MicroBlogKey): String - fun Followers(userKey: MicroBlogKey): String + object Following : IRoute { + operator fun invoke(userKey: MicroBlogKey): String + } + + object Followers : IRoute { + operator fun invoke(userKey: MicroBlogKey): String + } - interface Settings { + object Settings { val Home: String val Appearance: String val Display: String @@ -80,40 +115,67 @@ interface Root { val Misc: String val Notification: String val Layout: String - fun AccountNotification(accountKey: MicroBlogKey): String + + object AccountNotification : IRoute { + operator fun invoke(accountKey: MicroBlogKey): String + } + } + + object Status : IRoute { + operator fun invoke(statusKey: MicroBlogKey): String } - fun Status(statusKey: MicroBlogKey): String + object Mastodon { + object Hashtag : IRoute { + operator fun invoke(keyword: String): String + } - interface Mastodon { - fun Hashtag(keyword: String): String val Notification: String val FederatedTimeline: String val LocalTimeline: String - interface Compose { + object Compose { val Hashtag: String } } - interface Lists { + object Lists { val Home: String val MastodonCreateDialog: String val TwitterCreate: String - fun TwitterEdit(listKey: MicroBlogKey): String - fun Timeline(listKey: MicroBlogKey): String - fun Members(listKey: MicroBlogKey, owned: Boolean?): String - fun Subscribers(listKey: MicroBlogKey): String - fun AddMembers(listKey: MicroBlogKey): String + + object TwitterEdit : IRoute { + operator fun invoke(listKey: MicroBlogKey): String + } + + object Timeline : IRoute { + operator fun invoke(listKey: MicroBlogKey): String + } + + object Members : IRoute { + operator fun invoke(listKey: MicroBlogKey, owned: Boolean?): String + } + + object Subscribers : IRoute { + operator fun invoke(listKey: MicroBlogKey): String + } + + object AddMembers : IRoute { + operator fun invoke(listKey: MicroBlogKey): String + } } - interface Messages { + object Messages { val Home: String - fun Conversation(conversationKey: MicroBlogKey): String + + object Conversation : IRoute { + operator fun invoke(conversationKey: MicroBlogKey): String + } + val NewConversation: String } - interface Gif { + object Gif { val Home: String } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt index 40163b265..09b323240 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt @@ -33,27 +33,51 @@ const val twidereXSchema = "twiderex" @AppRoute( schema = twidereXSchema ) -interface RootDeepLinks { - interface Twitter { - fun User(screenName: String): String - fun Status(statusId: String): String +expect object RootDeepLinks { + object Twitter { + object User : IRoute { + operator fun invoke(screenName: String): String + } + + object Status : IRoute { + operator fun invoke(statusId: String): String + } + } + + object Mastodon { + object Hashtag : IRoute { + operator fun invoke(keyword: String): String + } + } + + object User : IRoute { + operator fun invoke(userKey: MicroBlogKey): String } - interface Mastodon { - fun Hashtag(keyword: String): String + object Status : IRoute { + operator fun invoke(statusKey: MicroBlogKey): String + } + + object Search : IRoute { + operator fun invoke(keyword: String): String } - fun User(userKey: MicroBlogKey): String - fun Status(statusKey: MicroBlogKey): String - fun Search(keyword: String): String val SignIn: String - fun Draft(draftId: String): String - fun Compose(composeType: ComposeType?, statusKey: MicroBlogKey?): String - fun Conversation(conversationKey: MicroBlogKey): String + object Draft : IRoute { + operator fun invoke(draftId: String): String + } + + object Compose : IRoute { + operator fun invoke(composeType: ComposeType?, statusKey: MicroBlogKey?): String + } + + object Conversation : IRoute { + operator fun invoke(conversationKey: MicroBlogKey): String + } - interface Callback { - interface SignIn { + object Callback { + object SignIn { val Mastodon: String val Twitter: String } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt index 0bd79c9c4..51e268ff1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -102,7 +102,7 @@ import moe.tlaster.precompose.navigation.transition.fadeScaleCreateTransition import moe.tlaster.precompose.navigation.transition.fadeScaleDestroyTransition import java.net.URLDecoder -const val initialRoute = RootRouteDefinition.Home +val initialRoute get() = Root.Home fun RouteBuilder.authorizedScene( route: String, @@ -211,47 +211,47 @@ fun RequirePlatformAccount( */ fun RouteBuilder.route(constraints: Constraints) { authorizedScene( - RootRouteDefinition.Home, + Root.Home, deepLinks = twitterHosts.map { "$it/*" } ) { HomeScene() } - authorizedScene(RootRouteDefinition.Mastodon.Notification) { + authorizedScene(Root.Mastodon.Notification) { MastodonNotificationScene() } - authorizedScene(RootRouteDefinition.Mastodon.FederatedTimeline) { + authorizedScene(Root.Mastodon.FederatedTimeline) { FederatedTimelineScene() } - authorizedScene(RootRouteDefinition.Mastodon.LocalTimeline) { + authorizedScene(Root.Mastodon.LocalTimeline) { LocalTimelineScene() } - authorizedScene(RootRouteDefinition.Me) { + authorizedScene(Root.Me) { MeScene() } - authorizedScene(RootRouteDefinition.Mentions) { + authorizedScene(Root.Mentions) { MentionScene() } - authorizedScene(RootRouteDefinition.HomeTimeline) { + authorizedScene(Root.HomeTimeline) { HomeTimelineScene() } scene( - RootRouteDefinition.SignIn.General, + Root.SignIn.General, deepLinks = listOf( - RootDeepLinksRouteDefinition.SignIn + RootDeepLinks.SignIn ), ) { SignInScene() } scene( - RootRouteDefinition.SignIn.Twitter, + Root.SignIn.Twitter, ) { backStackEntry -> val consumerKey = backStackEntry.path("consumerKey") val consumerSecret = backStackEntry.path("consumerSecret") @@ -260,7 +260,7 @@ fun RouteBuilder.route(constraints: Constraints) { } } - scene(RootRouteDefinition.SignIn.Mastodon) { + scene(Root.SignIn.Mastodon) { MastodonSignInScene() } @@ -273,7 +273,7 @@ fun RouteBuilder.route(constraints: Constraints) { // } authorizedScene( - RootDeepLinksRouteDefinition.Twitter.User, + RootDeepLinks.Twitter.User, deepLinks = twitterHosts.map { "$it/{screenName}" } @@ -293,9 +293,9 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.User, + Root.User, deepLinks = listOf( - RootDeepLinksRouteDefinition.User + RootDeepLinks.User ) ) { backStackEntry -> backStackEntry.path("userKey")?.let { @@ -310,9 +310,9 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Mastodon.Hashtag, + Root.Mastodon.Hashtag, deepLinks = listOf( - RootDeepLinksRouteDefinition.Mastodon.Hashtag + RootDeepLinks.Mastodon.Hashtag ) ) { backStackEntry -> backStackEntry.path("keyword")?.let { @@ -321,9 +321,9 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Status, + Root.Status, deepLinks = listOf( - RootDeepLinksRouteDefinition.Status, + RootDeepLinks.Status, ) ) { backStackEntry -> backStackEntry.path("statusKey")?.let { @@ -338,7 +338,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootDeepLinksRouteDefinition.Twitter.Status, + RootDeepLinks.Twitter.Status, deepLinks = twitterHosts.map { "$it/{screenName}/status/{statusId:[0-9]+}" } @@ -361,7 +361,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedDialog( - RootRouteDefinition.Media.Status, + Root.Media.Status, ) { backStackEntry -> backStackEntry.path("statusKey")?.let { MicroBlogKey.valueOf(it) @@ -379,7 +379,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedDialog( - RootRouteDefinition.Media.Pure, + Root.Media.Pure, ) { backStackEntry -> backStackEntry.path("belongToKey")?.let { MicroBlogKey.valueOf(it) @@ -393,7 +393,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedDialog( - RootRouteDefinition.Media.Raw, + Root.Media.Raw, ) { backStackEntry -> val url = backStackEntry.path("url")?.let { URLDecoder.decode(it, "UTF-8") @@ -404,12 +404,12 @@ fun RouteBuilder.route(constraints: Constraints) { } } - authorizedScene(RootRouteDefinition.Search.Home) { + authorizedScene(Root.Search.Home) { com.twidere.twiderex.scenes.home.SearchScene() } authorizedScene( - RootRouteDefinition.Search.Input, + Root.Search.Input, navTransition = NavTransition( createTransition = fadeCreateTransition, destroyTransition = fadeDestroyTransition, @@ -423,10 +423,10 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Search.Result, + Root.Search.Result, deepLinks = twitterHosts.map { "$it/search?q={keyword}" - } + RootDeepLinksRouteDefinition.Search, + } + RootDeepLinks.Search, navTransition = NavTransition( createTransition = fadeCreateTransition, destroyTransition = fadeDestroyTransition, @@ -440,7 +440,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Compose.Home, + Root.Compose.Home, navTransition = NavTransition( createTransition = { translationY = constraints.maxHeight * (1 - it) @@ -454,7 +454,7 @@ fun RouteBuilder.route(constraints: Constraints) { resumeTransition = fadeScaleCreateTransition, ), deepLinks = listOf( - RootDeepLinksRouteDefinition.Compose + RootDeepLinks.Compose ) ) { backStackEntry -> val type = backStackEntry.query("composeType")?.let { @@ -475,7 +475,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Followers, + Root.Followers, ) { backStackEntry -> backStackEntry.path("userKey")?.let { MicroBlogKey.valueOf(it) @@ -485,7 +485,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Following, + Root.Following, ) { backStackEntry -> backStackEntry.path("userKey")?.let { MicroBlogKey.valueOf(it) @@ -494,40 +494,40 @@ fun RouteBuilder.route(constraints: Constraints) { } } - scene(RootRouteDefinition.Settings.Home) { + scene(Root.Settings.Home) { SettingsScene() } - scene(RootRouteDefinition.Settings.Appearance) { + scene(Root.Settings.Appearance) { AppearanceScene() } - scene(RootRouteDefinition.Settings.Display) { + scene(Root.Settings.Display) { DisplayScene() } - scene(RootRouteDefinition.Settings.Storage) { + scene(Root.Settings.Storage) { StorageScene() } - scene(RootRouteDefinition.Settings.AccountManagement) { + scene(Root.Settings.AccountManagement) { AccountManagementScene() } - scene(RootRouteDefinition.Settings.Misc) { + scene(Root.Settings.Misc) { MiscScene() } - scene(RootRouteDefinition.Settings.Notification) { + scene(Root.Settings.Notification) { NotificationScene() } - authorizedScene(RootRouteDefinition.Settings.Layout) { + authorizedScene(Root.Settings.Layout) { LayoutScene() } scene( - RootRouteDefinition.Settings.AccountNotification + Root.Settings.AccountNotification ) { it.path("accountKey", null)?.let { MicroBlogKey.valueOf(it) @@ -536,18 +536,18 @@ fun RouteBuilder.route(constraints: Constraints) { } } - scene(RootRouteDefinition.Settings.About) { + scene(Root.Settings.About) { AboutScene() } - scene(RootRouteDefinition.Draft.List) { + scene(Root.Draft.List) { DraftListScene() } scene( - RootRouteDefinition.Draft.Compose, + Root.Draft.Compose, deepLinks = listOf( - RootDeepLinksRouteDefinition.Draft, + RootDeepLinks.Draft, ) ) { backStackEntry -> backStackEntry.path("draftId")?.let { @@ -555,29 +555,29 @@ fun RouteBuilder.route(constraints: Constraints) { } } - authorizedScene(RootRouteDefinition.Compose.Search.User) { + authorizedScene(Root.Compose.Search.User) { ComposeSearchUserScene() } - authorizedScene(RootRouteDefinition.Mastodon.Compose.Hashtag) { + authorizedScene(Root.Mastodon.Compose.Hashtag) { ComposeSearchHashtagScene() } - authorizedScene(RootRouteDefinition.Lists.Home) { + authorizedScene(Root.Lists.Home) { ListsScene() } - authorizedDialog(RootRouteDefinition.Lists.MastodonCreateDialog) { + authorizedDialog(Root.Lists.MastodonCreateDialog) { val navController = LocalNavController.current MastodonListsCreateDialog(onDismissRequest = { navController.goBack() }) } - authorizedScene(RootRouteDefinition.Lists.TwitterCreate) { + authorizedScene(Root.Lists.TwitterCreate) { TwitterListsCreateScene() } authorizedScene( - RootRouteDefinition.Lists.TwitterEdit, + Root.Lists.TwitterEdit, ) { backStackEntry -> backStackEntry.path("listKey")?.let { TwitterListsEditScene(listKey = MicroBlogKey.valueOf(it)) @@ -585,7 +585,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Lists.Timeline, + Root.Lists.Timeline, ) { backStackEntry -> backStackEntry.path("listKey")?.let { ListTimeLineScene(listKey = MicroBlogKey.valueOf(it)) @@ -593,7 +593,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Lists.Members, + Root.Lists.Members, ) { backStackEntry -> backStackEntry.path("listKey")?.let { ListsMembersScene(listKey = MicroBlogKey.valueOf(it), backStackEntry.query("owned") ?: false) @@ -601,7 +601,7 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Lists.Subscribers, + Root.Lists.Subscribers, ) { backStackEntry -> backStackEntry.path("listKey")?.let { ListsSubscribersScene(listKey = MicroBlogKey.valueOf(it)) @@ -609,25 +609,25 @@ fun RouteBuilder.route(constraints: Constraints) { } authorizedScene( - RootRouteDefinition.Lists.AddMembers, + Root.Lists.AddMembers, ) { backStackEntry -> backStackEntry.path("listKey")?.let { ListsAddMembersScene(listKey = MicroBlogKey.valueOf(it)) } } - authorizedScene(RootRouteDefinition.Messages.Home) { + authorizedScene(Root.Messages.Home) { DMConversationListScene() } - authorizedScene(RootRouteDefinition.Messages.NewConversation) { + authorizedScene(Root.Messages.NewConversation) { DMNewConversationScene() } authorizedScene( - RootRouteDefinition.Messages.Conversation, + Root.Messages.Conversation, deepLinks = listOf( - RootDeepLinksRouteDefinition.Conversation + RootDeepLinks.Conversation ) ) { backStackEntry -> backStackEntry.path("conversationKey")?.let { @@ -635,7 +635,7 @@ fun RouteBuilder.route(constraints: Constraints) { } } - authorizedScene(RootRouteDefinition.Gif.Home) { + authorizedScene(Root.Gif.Home) { GifScene() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index ae0504ad7..e42fc37d9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -47,7 +47,7 @@ import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.DraftViewModel @@ -109,7 +109,7 @@ fun DraftListSceneContent( ) { DropdownMenuItem( onClick = { - navController.navigate(RootRoute.Draft.Compose(it.draftId)) + navController.navigate(Root.Draft.Compose(it.draftId)) } ) { Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_drafts_actions_edit_draft)) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index bc7bdd686..9bde74884 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -93,7 +93,7 @@ import com.twidere.twiderex.kmp.Platform import com.twidere.twiderex.kmp.currentPlatform import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.preferences.LocalAppearancePreferences import com.twidere.twiderex.preferences.model.AppearancePreferences import com.twidere.twiderex.scenes.home.item @@ -463,7 +463,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { ListItem( modifier = Modifier.clickable( onClick = { - navController.navigate(RootRoute.SignIn.General) + navController.navigate(Root.SignIn.General) } ), text = { @@ -476,7 +476,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { ListItem( modifier = Modifier.clickable( onClick = { - navController.navigate(RootRoute.Settings.AccountManagement) + navController.navigate(Root.Settings.AccountManagement) } ), text = { @@ -521,7 +521,7 @@ private fun HomeDrawer(scaffoldState: ScaffoldState) { onClick = { scope.launch { scaffoldState.drawerState.close() - navController.navigate(RootRoute.Settings.Home) + navController.navigate(Root.Settings.Home) } } ), diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index a867b2c36..49d6cca61 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -53,7 +53,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.kmp.Platform import com.twidere.twiderex.kmp.currentPlatform -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import kotlinx.coroutines.launch @@ -79,7 +79,7 @@ private fun MastodonSignIn() { SignInButton( onClick = { scope.launch { - navController.navigateForResult(RootRoute.SignIn.Mastodon) + navController.navigateForResult(Root.SignIn.Mastodon) ?.let { it as Boolean }?.let { @@ -141,7 +141,7 @@ private fun TwitterSignIn() { onClick = { scope.launch { navController.navigateForResult( - RootRoute.SignIn.Twitter( + Root.SignIn.Twitter( BuildConfig.CONSUMERKEY, BuildConfig.CONSUMERSECRET, ) @@ -239,7 +239,7 @@ private fun TwitterCustomKeySignIn( onClick = { scope.launch { navController.navigateForResult( - RootRoute.SignIn.Twitter( + Root.SignIn.Twitter( apiKey, apiSecret, ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d491649e1..8e84ccdf6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -133,7 +133,7 @@ import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.model.ui.UiMediaInsert -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.Orange @@ -1196,7 +1196,7 @@ private fun ComposeActions( onClick = { scope.launch { val result = - navController.navigateForResult(RootRoute.Compose.Search.User) + navController.navigateForResult(Root.Compose.Search.User) ?.toString() if (!result.isNullOrEmpty()) { viewModel.insertText("$result ") @@ -1217,7 +1217,7 @@ private fun ComposeActions( onClick = { scope.launch { val result = - navController.navigateForResult(RootRoute.Mastodon.Compose.Hashtag) + navController.navigateForResult(Root.Mastodon.Compose.Hashtag) ?.toString() if (!result.isNullOrEmpty()) { viewModel.insertText("$result ") @@ -1276,7 +1276,7 @@ private fun ComposeActions( if (draftCount.value > 0) { IconButton( onClick = { - navController.navigate(RootRoute.Draft.List) + navController.navigate(Root.Draft.List) } ) { Box { @@ -1331,7 +1331,7 @@ private fun ComposeImage(item: UiMediaInsert, viewModel: ComposeViewModel) { .clickable( onClick = { navController.navigate( - RootRoute.Media.Raw( + Root.Media.Raw( if (type == MediaType.video) MediaType.video else MediaType.photo, item.filePath.toString() ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index 455ac7dd9..6fee24a95 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.component.lazy.ui.LazyUiDMConversationList import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -71,7 +71,7 @@ fun DMConversationListSceneFab() { val navController = LocalNavController.current FloatingActionButton( onClick = { - navController.navigate(RootRoute.Messages.NewConversation) + navController.navigate(Root.Messages.NewConversation) } ) { Icon( @@ -104,7 +104,7 @@ fun DMConversationListSceneContent( items = source, state = listState, onItemClicked = { - navController.navigate(RootRoute.Messages.Conversation(it.conversation.conversationKey)) + navController.navigate(Root.Messages.Conversation(it.conversation.conversationKey)) } ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index e40d342b6..e967c1e22 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -50,7 +50,7 @@ import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.ui.UiUser -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.dm.DMNewConversationViewModel @@ -95,8 +95,8 @@ fun DMNewConversationScene() { onResult = { key -> key?.let { navController.navigate( - RootRoute.Messages.Conversation(it), - NavOptions(popUpTo = PopUpTo(RootRoute.Messages.Home)) + Root.Messages.Conversation(it), + NavOptions(popUpTo = PopUpTo(Root.Messages.Home)) ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt index d3dd0b8a8..d47dfa4fa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.graphics.painter.Painter import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.scenes.dm.DMConversationListSceneContent import com.twidere.twiderex.scenes.dm.DMConversationListSceneFab @@ -36,7 +36,7 @@ class DMConversationListItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Messages.Home + get() = Root.Messages.Home @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt index ab7869e33..0729f4509 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.graphics.painter.Painter import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.scenes.DraftListSceneContent class DraftNavigationItem : HomeNavigationItem() { @@ -35,7 +35,7 @@ class DraftNavigationItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Draft.List + get() = Root.Draft.List @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 054e29c37..37e16b63d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -38,7 +38,7 @@ import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.model.enums.ComposeType -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.HomeTimelineViewModel @@ -47,7 +47,7 @@ class HomeTimelineItem : HomeNavigationItem() { @Composable override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_timeline_title) override val route: String - get() = RootRoute.HomeTimeline + get() = Root.HomeTimeline @Composable override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.ic_home) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt index 29607169a..8985d33f3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.graphics.painter.Painter import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.scenes.lists.ListsSceneContent import com.twidere.twiderex.scenes.lists.ListsSceneFab @@ -37,7 +37,7 @@ class ListsNavigationItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Lists.Home + get() = Root.Lists.Home @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index 8415031fb..78f0b2212 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene @@ -39,7 +39,7 @@ class MeItem : HomeNavigationItem() { @Composable override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_profile_title) override val route: String - get() = RootRoute.Me + get() = Root.Me @Composable override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.ic_user) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index b1d546673..c0adb3a3d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.MentionsTimelineViewModel @@ -40,7 +40,7 @@ class MentionItem : HomeNavigationItem() { @Composable override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_mentions_title) override val route: String - get() = RootRoute.Mentions + get() = Root.Mentions @Composable override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.ic_message_circle) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index 82dda4de7..ba1493ce1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.NotificationTimelineViewModel @@ -42,7 +42,7 @@ class NotificationItem : HomeNavigationItem() { @Composable override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_notification_title) override val route: String - get() = RootRoute.Notification + get() = Root.Notification @Composable override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.ic_message_circle) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index f959f3972..6124de9a4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -58,7 +58,7 @@ import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.HomeNavigationItem import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.search.SearchInputViewModel @@ -69,7 +69,7 @@ class SearchItem : HomeNavigationItem() { @Composable override fun name(): String = stringResource(com.twidere.twiderex.MR.strings.scene_search_title) override val route: String - get() = RootRoute.Search.Home + get() = Root.Search.Home @Composable override fun icon(): Painter = painterResource(res = com.twidere.twiderex.MR.files.ic_search) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index 083e7d199..271bc2125 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.mastodon.FederatedTimelineViewModel @@ -45,7 +45,7 @@ class FederatedTimelineItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Mastodon.FederatedTimeline + get() = Root.Mastodon.FederatedTimeline @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index 3d61ea0a2..33c55c45f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -33,7 +33,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.timeline.mastodon.LocalTimelineViewModel @@ -45,7 +45,7 @@ class LocalTimelineItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Mastodon.LocalTimeline + get() = Root.Mastodon.LocalTimeline @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 42292d636..9e1a464c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -38,7 +38,7 @@ import com.twidere.twiderex.component.lazy.LazyListController import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.HomeNavigationItem -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.scenes.home.AllNotificationItem import com.twidere.twiderex.scenes.home.MentionItem import com.twidere.twiderex.ui.LocalActiveAccount @@ -52,7 +52,7 @@ class MastodonNotificationItem : HomeNavigationItem() { } override val route: String - get() = RootRoute.Mastodon.Notification + get() = Root.Mastodon.Notification @Composable override fun icon(): Painter { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index af9fbaa1c..f42437e1b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -55,7 +55,7 @@ import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.refreshOrRetry import com.twidere.twiderex.model.MicroBlogKey -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsUserViewModel @@ -92,7 +92,7 @@ fun ListsMembersScene( onClick = { scope.launch { val result = - navController.navigateForResult(RootRoute.Lists.AddMembers(listKey = listKey)) as? List<*>? + navController.navigateForResult(Root.Lists.AddMembers(listKey = listKey)) as? List<*>? if (result != null && result.isNotEmpty()) source.refresh() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index d2a71b947..ae71a34a9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -43,7 +43,7 @@ import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -81,10 +81,10 @@ fun ListsSceneFab() { FloatingActionButton( onClick = { when (account.type) { - PlatformType.Twitter -> navController.navigate(RootRoute.Lists.TwitterCreate) + PlatformType.Twitter -> navController.navigate(Root.Lists.TwitterCreate) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() - PlatformType.Mastodon -> navController.navigate(RootRoute.Lists.MastodonCreateDialog) + PlatformType.Mastodon -> navController.navigate(Root.Lists.MastodonCreateDialog) } } ) { @@ -126,7 +126,7 @@ fun ListsSceneContent() { source = sourceItems, ownerItems = ownerItems, subscribedItems = subscribeItems, - onItemClicked = { navController.navigate(RootRoute.Lists.Timeline(it.listKey)) } + onItemClicked = { navController.navigate(Root.Lists.Timeline(it.listKey)) } ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index caf2af0f3..8aa1fd53b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -62,7 +62,7 @@ import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.scenes.lists.platform.MastodonListsEditDialog import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController @@ -158,7 +158,7 @@ fun ListTimeLineScene( onClick = { menuExpand = false navController.navigate( - RootRoute.Lists.Members( + Root.Lists.Members( listKey, uiList.isOwner(account.user.userId) ) @@ -173,7 +173,7 @@ fun ListTimeLineScene( onClick = { menuExpand = false navController.navigate( - RootRoute.Lists.Subscribers( + Root.Lists.Subscribers( listKey ) ) @@ -189,7 +189,7 @@ fun ListTimeLineScene( menuExpand = false when (account.type) { PlatformType.Twitter -> navController.navigate( - RootRoute.Lists.TwitterEdit(listKey = listKey) + Root.Lists.TwitterEdit(listKey = listKey) ) PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index cf9b0e654..baff7a81a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -32,7 +32,7 @@ import com.twidere.twiderex.component.lists.MastodonListsModifyComponent import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel import kotlinx.coroutines.launch @@ -79,7 +79,7 @@ fun MastodonListsCreateDialog(onDismissRequest: () -> Unit) { dismiss() if (result != null) { navController.navigate( - RootRoute.Lists.Timeline(result.listKey), + Root.Lists.Timeline(result.listKey), ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index 742065d1c..bb99013ca 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -45,7 +45,7 @@ import com.twidere.twiderex.component.lists.TwitterListsModifyComponent import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.lists.ListsCreateViewModel @@ -88,9 +88,9 @@ fun TwitterListsCreateScene() { private = isPrivate )?.let { navController.navigate( - RootRoute.Lists.Timeline(it.listKey), + Root.Lists.Timeline(it.listKey), options = NavOptions( - popUpTo = PopUpTo(RootRoute.Lists.Home) + popUpTo = PopUpTo(Root.Lists.Home) ) ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt index cf43ec7c9..7a704532c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt @@ -62,7 +62,7 @@ import com.twidere.twiderex.component.foundation.rememberParallaxLayoutState import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -192,7 +192,7 @@ private fun AboutContent() { Row { IconButton( onClick = { - navController.navigate(RootDeepLinksRoute.Twitter.User("TwidereProject")) + navController.navigate(RootDeepLinks.Twitter.User("TwidereProject")) } ) { Icon( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt index bfad18cc5..99646def3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt @@ -47,7 +47,7 @@ import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -69,7 +69,7 @@ fun AccountManagementScene() { val navController = LocalNavController.current IconButton( onClick = { - navController.navigate(RootRoute.SignIn.General) + navController.navigate(Root.SignIn.General) } ) { Icon( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index c0e5583dd..f04c31cbb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -47,7 +47,7 @@ import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -107,7 +107,7 @@ fun NotificationScene() { ListItem( modifier = Modifier.clickable( onClick = { - navController.navigate(RootRoute.Settings.AccountNotification(it.accountKey)) + navController.navigate(Root.Settings.AccountNotification(it.accountKey)) }, enabled = notificationEnabled, ), diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt index af891f64c..0aa748a84 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.stringResource -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -56,39 +56,39 @@ fun SettingsScene() { SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_appearance_title), painterResource(res = com.twidere.twiderex.MR.files.ic_shirt), - route = RootRoute.Settings.Appearance, + route = Root.Settings.Appearance, ), SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_title), painterResource(res = com.twidere.twiderex.MR.files.ic_template), - route = RootRoute.Settings.Display, + route = Root.Settings.Display, ), SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_layout_title), painterResource(res = com.twidere.twiderex.MR.files.ic_layout_sidebar), - route = RootRoute.Settings.Layout, + route = Root.Settings.Layout, ), SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_notification_title), painterResource(res = com.twidere.twiderex.MR.files.ic_settings_notification), - route = RootRoute.Settings.Notification, + route = Root.Settings.Notification, ), SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_storage_title), painterResource(res = com.twidere.twiderex.MR.files.ic_database), - route = RootRoute.Settings.Storage, + route = Root.Settings.Storage, ), SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_title), painterResource(res = com.twidere.twiderex.MR.files.ic_triangle_square_circle), - route = RootRoute.Settings.Misc, + route = Root.Settings.Misc, ), ), stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_section_header_about) to listOf( SettingItem( stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_about_title), painterResource(res = com.twidere.twiderex.MR.files.ic_info_circle), - route = RootRoute.Settings.About, + route = Root.Settings.About, ), ) ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt index 29e46bfe9..e25a0eafa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt @@ -37,7 +37,7 @@ import com.twidere.twiderex.component.foundation.InAppNotificationScaffold import com.twidere.twiderex.component.navigation.LocalNavigator import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState -import com.twidere.twiderex.navigation.RootDeepLinksRouteDefinition +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.twitter.user.TwitterUserViewModel import moe.tlaster.precompose.navigation.NavOptions @@ -56,7 +56,7 @@ fun TwitterUserScene(screenName: String) { user?.let { navigator.user( user = it, - NavOptions(popUpTo = PopUpTo(RootDeepLinksRouteDefinition.Twitter.User, inclusive = true)) + NavOptions(popUpTo = PopUpTo(RootDeepLinks.Twitter.User.route, inclusive = true)) ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index f05fa681d..800821d13 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -50,7 +50,7 @@ import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootRoute +import com.twidere.twiderex.navigation.Root import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalNavController import com.twidere.twiderex.ui.TwidereScene @@ -92,7 +92,7 @@ fun UserScene( it, onResult = { conversationKey -> conversationKey?.let { - navController.navigate(RootRoute.Messages.Conversation(it)) + navController.navigate(Root.Messages.Conversation(it)) } } ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt index ada23a7b0..6d1619426 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.utils -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel @@ -36,8 +36,8 @@ object CustomTabSignInChannel { } fun canHandle(uri: String): Boolean { - return uri.startsWith(RootDeepLinksRoute.Callback.SignIn.Mastodon) || - uri.startsWith(RootDeepLinksRoute.Callback.SignIn.Twitter) + return uri.startsWith(RootDeepLinks.Callback.SignIn.Mastodon) || + uri.startsWith(RootDeepLinks.Callback.SignIn.Twitter) } @OptIn(ExperimentalCoroutinesApi::class) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index b6ced0b63..7793eaad8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -29,7 +29,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.cred.OAuth2Credentials import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.OAuthLauncher @@ -66,7 +66,7 @@ class MastodonSignInViewModel( host = realHost, client_name = "Twidere X", website = "https://github.com/TwidereProject/TwidereX-Android", - redirect_uri = RootDeepLinksRoute.Callback.SignIn.Mastodon, + redirect_uri = RootDeepLinks.Callback.SignIn.Mastodon, httpClientFactory = TwidereServiceFactory.createHttpClientFactory() ) val application = service.createApplication() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index d2fe903fd..f617153c5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -30,7 +30,7 @@ import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.CredentialsType import com.twidere.twiderex.model.cred.OAuthCredentials import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.navigation.RootDeepLinksRoute +import com.twidere.twiderex.navigation.RootDeepLinks import com.twidere.twiderex.notification.InAppNotification import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.utils.OAuthLauncher @@ -74,7 +74,7 @@ class TwitterSignInViewModel( ) val token = service.getOAuthToken( if (isBuiltInKey()) { - RootDeepLinksRoute.Callback.SignIn.Twitter + RootDeepLinks.Callback.SignIn.Twitter } else { "oob" } From abb7fa1b853e7d6839cb1b71a06fc36599b3fa70 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 16:59:46 +0800 Subject: [PATCH 487/615] update ci to use jdk 17 --- .github/workflows/android.yml | 8 ++++---- .github/workflows/common.yml | 4 ++-- .github/workflows/desktop.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index d310048a5..a77296d19 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -25,7 +25,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses @@ -55,7 +55,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses @@ -97,7 +97,7 @@ jobs: - name: set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses @@ -142,7 +142,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index fd56f6d39..4fbec27a7 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -25,7 +25,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses @@ -70,7 +70,7 @@ jobs: - name: set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 50a18796b..a8ef14d45 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -25,7 +25,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 15 + java-version: 17 - name: Build with Gradle run: ./gradlew :desktop:packageDeb @@ -79,7 +79,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 15 + java-version: 17 - name: Build with Gradle run: ./gradlew :desktop:packageMsi @@ -133,7 +133,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 15 + java-version: 17 - name: Build with Gradle run: ./gradlew :desktop:packageDmg From 7458b174a68da0c7caa5f5880b25690c301ddce9 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 14 Dec 2021 17:08:18 +0800 Subject: [PATCH 488/615] fix too many arguments --- routeProcessor/src/main/kotlin/RouteProcessorProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt b/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt index 59b73a984..4c298284d 100644 --- a/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt +++ b/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt @@ -26,6 +26,6 @@ import com.google.devtools.ksp.processing.SymbolProcessorProvider class RouteProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return RouteProcessor(environment.codeGenerator, environment.logger) + return RouteProcessor(environment.codeGenerator) } } From 355ee3b5cda16454bdb857d6ae6ce37ffcd3c43d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 17:44:52 +0800 Subject: [PATCH 489/615] fix test --- .../twiderex/MainCoroutineScopeRule.kt | 46 ------------------- .../twidere/twiderex/MainThreadTestBase.kt | 19 ++++++-- .../twiderex/mock/paging/MockPagingSource.kt | 4 +- .../lists/ListsModifyViewModelTest.kt | 2 +- 4 files changed, 17 insertions(+), 54 deletions(-) delete mode 100644 common/src/commonTest/kotlin/com/twidere/twiderex/MainCoroutineScopeRule.kt diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainCoroutineScopeRule.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainCoroutineScopeRule.kt deleted file mode 100644 index 2694c7cc3..000000000 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/MainCoroutineScopeRule.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) 2020-2021 Tlaster - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.TestCoroutineScope -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.junit.rules.TestWatcher -import org.junit.runner.Description - -@ExperimentalCoroutinesApi -class MainCoroutineScopeRule(private val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : - TestWatcher(), - TestCoroutineScope by TestCoroutineScope() { - override fun starting(description: Description?) { - super.starting(description) - Dispatchers.setMain(dispatcher) - } - - override fun finished(description: Description?) { - super.finished(description) - cleanupTestCoroutines() - Dispatchers.resetMain() - } -} diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt index b71373c09..eeaa83805 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt @@ -20,19 +20,28 @@ */ package com.twidere.twiderex +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.junit.Rule +import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain import kotlin.test.AfterTest import kotlin.test.BeforeTest @OptIn(ExperimentalCoroutinesApi::class) internal open class MainThreadTestBase { - @get:Rule - var mainCoroutineRule = MainCoroutineScopeRule() + @OptIn(DelicateCoroutinesApi::class) + private val mainThreadSurrogate = newSingleThreadContext("UI thread") @BeforeTest - open fun setUp() {} + open fun setUp() { + Dispatchers.setMain(mainThreadSurrogate) + } @AfterTest - open fun tearDown() {} + open fun tearDown() { + Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher + mainThreadSurrogate.close() + } } diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt index e83602836..6968f5df9 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt @@ -27,7 +27,7 @@ import androidx.paging.PagingDataDiffer import androidx.paging.PagingSource import androidx.paging.PagingState import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.jetbrains.annotations.TestOnly internal class MockPagingSource @TestOnly constructor(val data: List) : PagingSource() { @@ -69,7 +69,7 @@ internal suspend fun PagingData.collectDataForTest(): List { override fun onRemoved(position: Int, count: Int) {} } val items = mutableListOf() - val dif = object : PagingDataDiffer(dcb, TestCoroutineDispatcher()) { + val dif = object : PagingDataDiffer(dcb, UnconfinedTestDispatcher()) { override suspend fun presentNewList( previousList: NullPaddedList, newList: NullPaddedList, diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt index 45f20bfd8..5a42263c5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt @@ -212,7 +212,7 @@ internal class ListsModifyViewModelTest : AccountViewModelTestBase() { success: Boolean ) { verify(exactly = 1) { loadingObserver.onChanged(true) } - verify(exactly = 2) { loadingObserver.onChanged(false) } + verify(exactly = 1) { loadingObserver.onChanged(false) } verify(exactly = if (success) 1 else 2) { successObserver.onChanged(success) } } } From 860a67a842ee8ac3e4cf64644a8ea2a6103fe3a5 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 14 Dec 2021 17:57:39 +0800 Subject: [PATCH 490/615] fixed crashes caused by deeplink --- .../twiderex/component/navigation/Navigator.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt index ba2f3c825..7c62e94ae 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt @@ -32,6 +32,7 @@ import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.navigation.twidereXSchema +import com.twidere.twiderex.twitterHosts import moe.tlaster.precompose.navigation.NavController import moe.tlaster.precompose.navigation.NavOptions @@ -122,19 +123,22 @@ class Navigator( } override fun openLink(it: String, deepLink: Boolean) { - if (( - it.contains(twidereXSchema) || it.contains( - "twitter.com", - ignoreCase = true - ) - ) && deepLink - ) { + if ((it.contains(twidereXSchema) || isTwitterDeeplink(it)) && deepLink) { navController.navigate(it) } else { remoteNavigator.openDeepLink(it) } } + private fun isTwitterDeeplink(url: String): Boolean { + twitterHosts.forEach { + if (url.startsWith(it) && url.length > it.length) { + return true + } + } + return false + } + override suspend fun twitterSignInWeb(target: String): String { clearCookie() return navController.navigateForResult( From f12445e230afb976ee2ca373a15546ca2d47a8c6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 14 Dec 2021 18:17:43 +0800 Subject: [PATCH 491/615] disable koin print logger for now --- .../src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 65ae2cf55..1ffd0adb9 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -50,6 +50,7 @@ import moe.tlaster.precompose.PreComposeWindow import moe.tlaster.precompose.navigation.NavController import org.koin.core.context.startKoin import org.koin.core.context.stopKoin +import org.koin.core.logger.Level import java.awt.Desktop import java.io.File import java.nio.file.Files @@ -141,7 +142,7 @@ private fun ensureWindowsRegistry() { private fun startDesktopApp() { startKoin { - printLogger() + printLogger(Level.NONE) setupModules() } val preferencesHolder = get() From fc283518ae5e46bf0431964303456b0ded974eee Mon Sep 17 00:00:00 2001 From: huixing Date: Tue, 14 Dec 2021 18:31:02 +0800 Subject: [PATCH 492/615] remove workaround --- .../kotlin/com/twidere/twiderex/scenes/HomeScene.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index bc7bdd686..09faa83f7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -89,8 +89,6 @@ import com.twidere.twiderex.component.status.UserScreenName import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.extensions.withElevation -import com.twidere.twiderex.kmp.Platform -import com.twidere.twiderex.kmp.currentPlatform import com.twidere.twiderex.model.HomeMenus import com.twidere.twiderex.model.ui.UiUser import com.twidere.twiderex.navigation.RootRoute @@ -256,13 +254,8 @@ fun HomeAppBar( scope: CoroutineScope, ) { if (tabPosition == AppearancePreferences.TabPosition.Bottom) { - // FIXME: 2021/12/7 workaround for desktop crash 'place was called on a node which was placed already' AnimatedVisibility( - visible = if (currentPlatform == Platform.JVM) { - true - } else { - menus[pagerState.currentPage].item.withAppBar - }, + visible = menus[pagerState.currentPage].item.withAppBar, enter = expandVertically(clip = false), exit = shrinkVertically(clip = false), modifier = modifier From b01653d56ca16986f47880ab36611de164d6ad4a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 15 Dec 2021 14:09:57 +0800 Subject: [PATCH 493/615] fixed EmojiPanel didn't display on desktop --- .../platform/PlatformEmojiPanel.android.kt | 85 +++++++++++++ .../foundation/platform/platformEmojiPanel.kt | 103 +++++++++++++++ .../twiderex/scenes/compose/ComposeScene.kt | 118 ++---------------- .../platform/PlatformEmojiPanel.desktop.kt | 60 +++++++++ 4 files changed, 257 insertions(+), 109 deletions(-) create mode 100644 common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt new file mode 100644 index 000000000..7f2c66643 --- /dev/null +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt @@ -0,0 +1,85 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation.platform + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.coerceAtLeast +import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.ImeBottomInsets +import com.twidere.twiderex.component.ImeHeightWithInsets +import com.twidere.twiderex.model.ui.UiEmoji +import com.twidere.twiderex.model.ui.UiEmojiCategory +import kotlin.math.max + +@OptIn(ExperimentalFoundationApi::class) +@Composable +actual fun PlatformEmojiPanel( + items: List, + showEmoji: Boolean, + onEmojiSelected: (UiEmoji) -> Unit, +) { + var height by remember { mutableStateOf(0) } + ImeHeightWithInsets( + filter = { + it > 0 + }, + collectIme = { + height = max(height, it) + } + + ) + val targetHeight = with(LocalDensity.current) { + height.toDp() + } + val bottom = ImeBottomInsets() + var visibility by remember { mutableStateOf(false) } + LaunchedEffect(showEmoji, bottom) { + if (bottom == targetHeight || showEmoji) { + visibility = showEmoji + } + } + Box( + modifier = Modifier + .height( + height = if (visibility) { + (targetHeight - bottom).coerceAtLeast(0.dp) + } else { + 0.dp + } + ) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + EmojiList(items = items, onEmojiSelected = onEmojiSelected) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt new file mode 100644 index 000000000..08c72fe77 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt @@ -0,0 +1,103 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation.platform + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import com.twidere.twiderex.component.foundation.NetworkImage +import com.twidere.twiderex.component.lazy.itemsGridIndexed +import com.twidere.twiderex.model.ui.UiEmoji +import com.twidere.twiderex.model.ui.UiEmojiCategory + +@Composable +expect fun PlatformEmojiPanel( + items: List, + showEmoji: Boolean, + onEmojiSelected: (UiEmoji) -> Unit, +) + +@ExperimentalFoundationApi +@Composable +internal fun EmojiList( + items: List, + onEmojiSelected: (UiEmoji) -> Unit, +) { + BoxWithConstraints(modifier = Modifier.padding(EmojiListDefaults.ContentPadding)) { + val column = maxOf((maxWidth / EmojiListDefaults.Icon.Size).toInt(), 1) + LazyColumn { + items.forEach { + it.category?.let { category -> + item { + Text( + text = category, + style = MaterialTheme.typography.h6, + modifier = Modifier.padding(EmojiListDefaults.Category.ContentPadding) + ) + } + } + itemsGridIndexed( + data = it.emoji, + rowSize = column, + ) { _, item -> + item.url?.let { it1 -> + NetworkImage( + modifier = Modifier + .size(EmojiListDefaults.Icon.Size) + .padding(EmojiListDefaults.Icon.ContentPadding) + .clickable { + onEmojiSelected.invoke(item) + }, + data = it1, + contentScale = ContentScale.Fit, + ) + } + } + } + } + } +} + +object EmojiListDefaults { + object Icon { + val Size = 48.dp + val ContentPadding = PaddingValues(4.dp) + } + + val ContentPadding = PaddingValues( + horizontal = 8.dp, + vertical = 0.dp + ) + + object Category { + val ContentPadding = PaddingValues(vertical = 16.dp, horizontal = 4.dp) + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d491649e1..ebd4db39f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -42,7 +42,6 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.rememberScrollState @@ -86,17 +85,13 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.dp import com.twidere.twiderex.MR -import com.twidere.twiderex.component.ImeBottomInsets -import com.twidere.twiderex.component.ImeHeightWithInsets import com.twidere.twiderex.component.ImeVisibleWithInsets import com.twidere.twiderex.component.foundation.AlertDialog import com.twidere.twiderex.component.foundation.AppBar @@ -107,7 +102,7 @@ import com.twidere.twiderex.component.foundation.GifTag import com.twidere.twiderex.component.foundation.InAppNotificationBottomSheetScaffold import com.twidere.twiderex.component.foundation.NetworkImage import com.twidere.twiderex.component.foundation.TextInput -import com.twidere.twiderex.component.lazy.itemsGridIndexed +import com.twidere.twiderex.component.foundation.platform.PlatformEmojiPanel import com.twidere.twiderex.component.media.MediaInsertMenu import com.twidere.twiderex.component.painterResource import com.twidere.twiderex.component.status.StatusLineComponent @@ -131,7 +126,6 @@ import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.MastodonVisibility import com.twidere.twiderex.model.enums.MediaType import com.twidere.twiderex.model.enums.PlatformType -import com.twidere.twiderex.model.ui.UiEmojiCategory import com.twidere.twiderex.model.ui.UiMediaInsert import com.twidere.twiderex.navigation.RootRoute import com.twidere.twiderex.ui.LocalActiveAccount @@ -206,6 +200,7 @@ private fun ComposeBody( val enableThreadMode by viewModel.enableThreadMode.observeAsState(initial = false) var showSaveDraftDialog by remember { mutableStateOf(false) } val scaffoldState = rememberBottomSheetScaffoldState() + val emojis by viewModel.emojis.observeAsState(emptyList()) if (showSaveDraftDialog || canSaveDraft) { BackHandler { when { @@ -440,7 +435,13 @@ private fun ComposeBody( }, ) } - EmojiPanel(viewModel = viewModel, showEmoji = showEmoji) + PlatformEmojiPanel( + showEmoji = showEmoji, + items = emojis, + onEmojiSelected = { + viewModel.insertEmoji(it) + }, + ) } } } @@ -475,107 +476,6 @@ private object ComposeImageListDefaults { ) } -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun EmojiPanel( - viewModel: ComposeViewModel, - showEmoji: Boolean, -) { - val items by viewModel.emojis.observeAsState(initial = emptyList()) - - var height by remember { mutableStateOf(0) } - ImeHeightWithInsets( - filter = { - it > 0 - }, - collectIme = { - height = max(height, it) - } - - ) - val targetHeight = with(LocalDensity.current) { - height.toDp() - } - val bottom = ImeBottomInsets() - var visibility by remember { mutableStateOf(false) } - LaunchedEffect(showEmoji, bottom) { - if (bottom == targetHeight || showEmoji) { - visibility = showEmoji - } - } - Box( - modifier = Modifier - .height( - height = if (visibility) { - (targetHeight - bottom).coerceAtLeast(0.dp) - } else { - 0.dp - } - ) - .fillMaxWidth(), - contentAlignment = Alignment.Center, - ) { - EmojiList(items, viewModel) - } -} - -@ExperimentalFoundationApi -@Composable -private fun EmojiList( - items: List, - viewModel: ComposeViewModel -) { - BoxWithConstraints(modifier = Modifier.padding(EmojiListDefaults.ContentPadding)) { - val column = maxOf((maxWidth / EmojiListDefaults.Icon.Size).toInt(), 1) - LazyColumn { - items.forEach { - it.category?.let { category -> - item { - Text( - text = category, - style = MaterialTheme.typography.h6, - modifier = Modifier.padding(EmojiListDefaults.Category.ContentPadding) - ) - } - } - itemsGridIndexed( - data = it.emoji, - rowSize = column, - ) { _, item -> - item.url?.let { it1 -> - NetworkImage( - modifier = Modifier - .size(EmojiListDefaults.Icon.Size) - .padding(EmojiListDefaults.Icon.ContentPadding) - .clickable { - viewModel.insertEmoji(item) - }, - data = it1, - contentScale = ContentScale.Fit, - ) - } - } - } - } - } -} - -object EmojiListDefaults { - object Icon { - val Size = 48.dp - val ContentPadding = PaddingValues(4.dp) - } - - val ContentPadding = PaddingValues( - horizontal = 8.dp, - vertical = 0.dp - ) - - object Category { - val ContentPadding = PaddingValues(vertical = 16.dp, horizontal = 4.dp) - } -} - @Composable private fun MastodonExtraActions( images: List, diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt new file mode 100644 index 000000000..2d31a4987 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt @@ -0,0 +1,60 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component.foundation.platform + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.twidere.twiderex.model.ui.UiEmoji +import com.twidere.twiderex.model.ui.UiEmojiCategory + +@OptIn(ExperimentalFoundationApi::class) +@Composable +actual fun PlatformEmojiPanel( + items: List, + showEmoji: Boolean, + onEmojiSelected: (UiEmoji) -> Unit, +) { + AnimatedVisibility( + visible = showEmoji + ) { + Box( + modifier = Modifier + .height(height = PlatformEmojiPanelDefaults.Height) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + EmojiList(items = items, onEmojiSelected = onEmojiSelected) + } + } +} + +private object PlatformEmojiPanelDefaults { + val Height = 200.dp +} From 568cb6639461ebcb1a9e70b89391bdb68ffb8166 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Dec 2021 14:42:28 +0800 Subject: [PATCH 494/615] update kotlin to 1.6.10 --- buildSrc/src/main/kotlin/Versions.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 97c608639..3be8ef604 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.6.10-RC" + const val lang = "1.6.10" const val coroutines = "1.6.0-RC" const val serialization = "1.3.1" } @@ -12,14 +12,14 @@ object Versions { val java = JavaVersion.VERSION_11 } - const val ksp = "${Kotlin.lang}-1.0.1" + const val ksp = "${Kotlin.lang}-1.0.2" const val agp = "7.0.3" const val spotless = "6.0.4" const val ktlint = "0.42.1" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.0.1-rc1" + const val compose_jb = "1.0.1-rc2" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" From 5d954d04a69bc20588ea37f4fb4b7ea8a66d5e04 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Dec 2021 14:49:35 +0800 Subject: [PATCH 495/615] clean up BlurTransformation --- .../kotlin/com/twidere/twiderex/utils/BlurTransformation.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt index 14e40ead6..46e4b8dc4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt @@ -31,12 +31,10 @@ import com.google.android.renderscript.Toolkit class BlurTransformation @JvmOverloads constructor( private val context: Context, private val radius: Int = DEFAULT_RADIUS, - // private val sampling: Int = DEFAULT_SAMPLING ) : Transformation { init { require(radius in 0..25) { "radius must be in [0, 25]." } - // require(sampling > 0) { "sampling must be > 0." } } override val cacheKey: String From 61f1d7d2f03eb911cb8bc8f0fad44b4922142866 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Dec 2021 14:50:10 +0800 Subject: [PATCH 496/615] add asdf tool versions --- .tool-versions | 1 + 1 file changed, 1 insertion(+) create mode 100644 .tool-versions diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 000000000..c3ea10a70 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +java openjdk-17.0.1 From 1a924098cb4c92f26bd5390f72df1d7dd8eb2746 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Dec 2021 14:51:54 +0800 Subject: [PATCH 497/615] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75f02d350..54125f091 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,8 +19,8 @@ For those able & willing to help fix issues and/or implement features ... ### Development environment Make sure you have - - Android Studio Arctic Fox 2020.3.1 or later - - JDK 11 + - Android Studio Arctic Fox 2020.3.1 or IntelliJ IDEA 2021.3 + - JDK 17 ### Working branch From 2e89faab291c962797508f18df3610c9320022c4 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 15 Dec 2021 15:00:54 +0800 Subject: [PATCH 498/615] fixed ui bug of AlertDialog on Desktop --- .../component/foundation/platform/PlatformAlertDialog.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt index 6a6f1296e..a6117f914 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt @@ -20,6 +20,7 @@ */ package com.twidere.twiderex.component.foundation.platform +import androidx.compose.foundation.layout.width import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme import androidx.compose.material.contentColorFor @@ -27,6 +28,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.dp @OptIn(ExperimentalMaterialApi::class) @Composable @@ -44,7 +46,7 @@ actual fun PlatformAlertDialog( androidx.compose.material.AlertDialog( onDismissRequest = onDismissRequest, confirmButton = confirmButton, - modifier = modifier, + modifier = modifier.width(320.dp), dismissButton = dismissButton, title = title, text = text, From 1de7a8a0c67b1b08a1eef053e9abd8283ee7f2dc Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 15 Dec 2021 19:10:22 +0800 Subject: [PATCH 499/615] update license --- README.md | 2 +- .../kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt | 2 +- .../kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt | 2 +- android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt | 2 +- .../src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/UriExtensions.kt | 2 +- .../com/twidere/twiderex/service/AccountAuthenticatorService.kt | 2 +- .../twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt | 2 +- .../kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt | 2 +- .../twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt | 2 +- .../kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt | 2 +- .../twiderex/paging/LimitOffsetTransformPagingSourceTest.kt | 2 +- .../com/twidere/twiderex/repository/CacheRepositoryTest.kt | 2 +- .../com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt | 2 +- .../kotlin/androidx.paging.compose/PagingPlaceholderKey.kt | 2 +- .../kotlin/com/twidere/twiderex/TwidereApplication.kt | 2 +- .../kotlin/com/twidere/twiderex/action/ComposeAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DirectMessageAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DraftAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/MediaAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/StatusActions.kt | 2 +- .../kotlin/com/twidere/twiderex/component/PlatformInsets.kt | 2 +- .../com/twidere/twiderex/component/foundation/LocalView.kt | 2 +- .../twidere/twiderex/component/foundation/RemainingTimeVideo.kt | 2 +- .../com/twidere/twiderex/component/foundation/WebComponent.kt | 2 +- .../foundation/platform/PlatformAlertDialog.android.kt | 2 +- .../twiderex/component/foundation/platform/PlatformDialog.kt | 2 +- .../foundation/platform/PlatformDropdownMenu.android.kt | 2 +- .../component/foundation/platform/PlatformPlayerView.android.kt | 2 +- .../kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt | 2 +- .../dataprovider/db/dao/DirectMessageConversationDaoImpl.kt | 2 +- .../twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt | 2 +- .../twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt | 2 +- .../twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt | 2 +- .../com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt | 2 +- .../com/twidere/twiderex/db/transform/WorkDataTransform.kt | 2 +- .../com/twidere/twiderex/di/modules/ActionsModule.android.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt | 2 +- .../com/twidere/twiderex/di/modules/PlatformModule.android.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/DataExtensions.kt | 2 +- .../com/twidere/twiderex/extensions/FileProviderExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt | 2 +- .../twidere/twiderex/initializer/DirectMessageInitializer.kt | 2 +- .../twiderex/initializer/NotificationChannelInitializer.kt | 2 +- .../com/twidere/twiderex/initializer/NotificationInitializer.kt | 2 +- .../twidere/twiderex/initializer/TwidereServiceInitializer.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/CookieManager.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt | 2 +- .../androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt | 2 +- .../androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/LocationProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt | 2 +- .../src/androidMain/kotlin/com/twidere/twiderex/kmp/Platform.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- .../androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/StorageProvider.kt | 2 +- .../androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt | 2 +- .../com/twidere/twiderex/model/AccountPreferencesFactory.kt | 2 +- .../kotlin/com/twidere/twiderex/navigation/Route.android.kt | 2 +- .../com/twidere/twiderex/notification/AppNotificationManager.kt | 2 +- .../twiderex/notification/NotificationChannelSpec.android.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/AccountRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt | 2 +- .../twiderex/room/db/dao/RoomDirectMessageConversationDao.kt | 2 +- .../twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt | 2 +- .../twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt | 2 +- .../com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt | 2 +- .../com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt | 2 +- .../com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/Alias.kt | 2 +- .../com/twidere/twiderex/room/db/model/DbDMConversation.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbList.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt | 2 +- .../com/twidere/twiderex/room/db/model/DbNotificationCursor.kt | 2 +- .../com/twidere/twiderex/room/db/model/DbPagingTimeline.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt | 2 +- .../com/twidere/twiderex/room/db/model/DbStatusReaction.kt | 2 +- .../com/twidere/twiderex/room/db/model/DbStatusReference.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt | 2 +- .../kotlin/com/twidere/twiderex/room/db/model/DbUser.kt | 2 +- .../twiderex/room/db/model/converter/ComposeTypeConverter.kt | 2 +- .../twidere/twiderex/room/db/model/converter/ExtraConverter.kt | 2 +- .../room/db/model/converter/MastodonVisibilityConverter.kt | 2 +- .../twiderex/room/db/model/converter/MediaTypeConverter.kt | 2 +- .../twiderex/room/db/model/converter/MicroBlogKeyConverter.kt | 2 +- .../room/db/model/converter/NotificationCursorTypeConverter.kt | 2 +- .../room/db/model/converter/NotificationTypeConverter.kt | 2 +- .../twiderex/room/db/model/converter/PlatformTypeConverter.kt | 2 +- .../twiderex/room/db/model/converter/StringListConverter.kt | 2 +- .../room/db/model/converter/TwitterReplySettingsConverter.kt | 2 +- .../room/db/model/converter/UserTimelineTypeConverter.kt | 2 +- .../twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt | 2 +- .../com/twidere/twiderex/room/db/paging/TablePagingSource.kt | 2 +- .../com/twidere/twiderex/room/db/transform/AccountTransform.kt | 2 +- .../twiderex/room/db/transform/DmConversationTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/DraftTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/EmojiTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/ListTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/MediaTransform.kt | 2 +- .../twiderex/room/db/transform/NotificationCursorTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/SearchTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/StatusTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/TrendTransform.kt | 2 +- .../twidere/twiderex/room/db/transform/UrlEntityTransform.kt | 2 +- .../com/twidere/twiderex/room/db/transform/UserTransform.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt | 2 +- .../scenes/settings/AccountNotificationScene.android.kt | 2 +- .../twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/BlurTransformation.kt | 2 +- .../com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt | 2 +- .../com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/video/VideoCache.kt | 2 +- .../kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/NotificationWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt | 2 +- .../twidere/twiderex/worker/compose/MastodonComposeWorker.kt | 2 +- .../com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt | 2 +- .../twidere/twiderex/worker/database/DeleteDbStatusWorker.kt | 2 +- .../com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt | 2 +- .../com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt | 2 +- .../com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt | 2 +- .../twiderex/worker/dm/TwitterDirectMessageSendWorker.kt | 2 +- .../com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt | 2 +- .../com/twidere/twiderex/worker/status/DeleteStatusWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt | 2 +- .../com/twidere/twiderex/worker/status/MastodonVoteWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt | 2 +- .../kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt | 2 +- .../com/twidere/twiderex/worker/status/UnRetweetWorker.kt | 2 +- .../com/twidere/twiderex/worker/status/UpdateStatusWorker.kt | 2 +- .../moe/tlaster/precompose/lifecycle/PreComposeActivity.kt | 2 +- .../kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt | 2 +- .../kotlin/androidx/paging/compose/LazyPagingItems.kt | 2 +- .../kotlin/androidx/paging/compose/PagingPlaceholderKey.kt | 2 +- common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt | 2 +- .../commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/App.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt | 2 +- .../kotlin/com/twidere/twiderex/action/ComposeAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DirectMessageAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DraftAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/MediaAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/StatusActions.kt | 2 +- .../kotlin/com/twidere/twiderex/component/FormattedTime.kt | 2 +- .../kotlin/com/twidere/twiderex/component/HumanizedTime.kt | 2 +- .../kotlin/com/twidere/twiderex/component/LoginLogo.kt | 2 +- .../kotlin/com/twidere/twiderex/component/PlatformInsets.kt | 2 +- .../kotlin/com/twidere/twiderex/component/Resources.kt | 2 +- .../kotlin/com/twidere/twiderex/component/TimelineComponent.kt | 2 +- .../kotlin/com/twidere/twiderex/component/UserComponent.kt | 2 +- .../kotlin/com/twidere/twiderex/component/UserListComponent.kt | 2 +- .../com/twidere/twiderex/component/foundation/AlertDialog.kt | 2 +- .../kotlin/com/twidere/twiderex/component/foundation/AppBar.kt | 2 +- .../twiderex/component/foundation/AppBarNavigationButton.kt | 2 +- .../com/twidere/twiderex/component/foundation/CheckboxItem.kt | 2 +- .../com/twidere/twiderex/component/foundation/ColoredSwitch.kt | 2 +- .../kotlin/com/twidere/twiderex/component/foundation/Dialog.kt | 2 +- .../com/twidere/twiderex/component/foundation/DropdownMenu.kt | 2 +- .../twidere/twiderex/component/foundation/ErrorPlaceholder.kt | 2 +- .../kotlin/com/twidere/twiderex/component/foundation/GifTag.kt | 2 +- .../com/twidere/twiderex/component/foundation/GridLayout.kt | 2 +- .../twidere/twiderex/component/foundation/HorizontalDivider.kt | 2 +- .../twiderex/component/foundation/InAppNotificationScaffold.kt | 2 +- .../twidere/twiderex/component/foundation/LoadingProgress.kt | 2 +- .../com/twidere/twiderex/component/foundation/LocalView.kt | 2 +- .../component/foundation/MostCenterVideoPlayerLayout.kt | 2 +- .../twiderex/component/foundation/NestedScrollScaffold.kt | 2 +- .../com/twidere/twiderex/component/foundation/NetworkImage.kt | 2 +- .../kotlin/com/twidere/twiderex/component/foundation/Pager.kt | 2 +- .../com/twidere/twiderex/component/foundation/ParallaxLayout.kt | 2 +- .../com/twidere/twiderex/component/foundation/SignInButton.kt | 2 +- .../com/twidere/twiderex/component/foundation/SignInScaffold.kt | 2 +- .../twiderex/component/foundation/SwipeToRefreshLayout.kt | 2 +- .../com/twidere/twiderex/component/foundation/TabsComponent.kt | 2 +- .../com/twidere/twiderex/component/foundation/TextInput.kt | 2 +- .../com/twidere/twiderex/component/foundation/VideoPlayer.kt | 2 +- .../twidere/twiderex/component/foundation/VideoPlayerState.kt | 2 +- .../twiderex/component/foundation/platform/PagerIndicator.kt | 2 +- .../component/foundation/platform/PlatformAlertDialog.kt | 2 +- .../twiderex/component/foundation/platform/PlatformDialog.kt | 2 +- .../component/foundation/platform/PlatformDropdownMenu.kt | 2 +- .../component/foundation/platform/PlatformPlayerView.kt | 2 +- .../twidere/twiderex/component/foundation/reorderableColumn.kt | 2 +- .../kotlin/com/twidere/twiderex/component/image/ImageBlur.kt | 2 +- .../kotlin/com/twidere/twiderex/component/image/ImageEffects.kt | 2 +- .../kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt | 2 +- .../com/twidere/twiderex/component/lazy/LazyListController.kt | 2 +- .../kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt | 2 +- .../kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt | 2 +- .../com/twidere/twiderex/component/lazy/itemsGridIndexed.kt | 2 +- .../kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt | 2 +- .../twiderex/component/lazy/ui/LazyUiDMConversationList.kt | 2 +- .../com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt | 2 +- .../com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt | 2 +- .../kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt | 2 +- .../com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt | 2 +- .../twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt | 2 +- .../com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt | 2 +- .../com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt | 2 +- .../twiderex/component/lists/MastodonListsModifyComponent.kt | 2 +- .../twiderex/component/lists/TwitterListsModifyComponent.kt | 2 +- .../com/twidere/twiderex/component/media/MediaInsertMenu.kt | 2 +- .../com/twidere/twiderex/component/navigation/Navigator.kt | 2 +- .../twiderex/component/placeholder/UiImagePlaceholder.kt | 2 +- .../twiderex/component/placeholder/UiStatusPlaceholder.kt | 2 +- .../twidere/twiderex/component/placeholder/UiUserPlaceholder.kt | 2 +- .../com/twidere/twiderex/component/requireAuthorization.kt | 2 +- .../com/twidere/twiderex/component/settings/SettingRadioItem.kt | 2 +- .../com/twidere/twiderex/component/settings/SwitchItem.kt | 2 +- .../twiderex/component/status/DetailedStatusComponent.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/HtmlText.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/LinkPreview.kt | 2 +- .../com/twidere/twiderex/component/status/MastodonPoll.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt | 2 +- .../com/twidere/twiderex/component/status/SelectionContainer.kt | 2 +- .../com/twidere/twiderex/component/status/StatusActions.kt | 2 +- .../com/twidere/twiderex/component/status/StatusDivider.kt | 2 +- .../twidere/twiderex/component/status/StatusLineComponent.kt | 2 +- .../twidere/twiderex/component/status/StatusMediaComponent.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/StatusText.kt | 2 +- .../com/twidere/twiderex/component/status/StatusThread.kt | 2 +- .../twiderex/component/status/TimelineStatusComponent.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/TweetHeader.kt | 2 +- .../kotlin/com/twidere/twiderex/component/status/UserAvatar.kt | 2 +- .../com/twidere/twiderex/component/status/UserScreenName.kt | 2 +- .../com/twidere/twiderex/component/trend/MastodonTrendItem.kt | 2 +- .../com/twidere/twiderex/component/trend/TwitterTrendItem.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt | 2 +- .../kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt | 2 +- .../com/twidere/twiderex/dataprovider/mapper/DataMapper.kt | 2 +- .../kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt | 2 +- .../kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt | 2 +- .../com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt | 2 +- .../kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt | 2 +- .../kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt | 2 +- .../kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt | 2 +- .../twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt | 2 +- .../twiderex/db/sqldelight/SqlDelightCacheDatabaseImpl.kt | 2 +- .../twiderex/db/sqldelight/adapter/AccountAdapterFactory.kt | 2 +- .../twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt | 2 +- .../db/sqldelight/adapter/DMConversationAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/ListAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt | 2 +- .../db/sqldelight/adapter/NotificationCursorAdapterFactory.kt | 2 +- .../db/sqldelight/adapter/PagingTimelineAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/StatusAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/StatusReactionsFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt | 2 +- .../twiderex/db/sqldelight/adapter/UserAdapterFactory.kt | 2 +- .../dao/SqlDelightDirectMessageConversationDaoImpl.kt | 2 +- .../db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt | 2 +- .../db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt | 2 +- .../db/sqldelight/dao/SqlDelightPagingTimelineDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightStatusDaoImpl.kt | 2 +- .../twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt | 2 +- .../twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt | 2 +- .../kotlin/com/twidere/twiderex/db/sqldelight/database.kt | 2 +- .../twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt | 2 +- .../twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt | 2 +- .../twiderex/db/sqldelight/model/DbPagingTimelineWithStatus.kt | 2 +- .../twidere/twiderex/db/sqldelight/model/DbStatusElements.kt | 2 +- .../twiderex/db/sqldelight/model/DbStatusWithAttachments.kt | 2 +- .../twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt | 2 +- .../twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt | 2 +- .../twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt | 2 +- .../twiderex/db/sqldelight/transform/DMConversationTransform.kt | 2 +- .../twiderex/db/sqldelight/transform/DMEventTransform.kt | 2 +- .../twiderex/db/sqldelight/transform/DbAccountTransform.kt | 2 +- .../db/sqldelight/transform/DbPagingTimelineTransform.kt | 2 +- .../twiderex/db/sqldelight/transform/DbStatusTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/DraftTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/ListTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/MediaTransform.kt | 2 +- .../db/sqldelight/transform/NotificationCursorTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/SearchTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/TrendTransform.kt | 2 +- .../twiderex/db/sqldelight/transform/UrlEntityTransform.kt | 2 +- .../twidere/twiderex/db/sqldelight/transform/UserTransform.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt | 2 +- .../kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/ActionModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/JobsModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/KmpModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt | 2 +- .../com/twidere/twiderex/di/modules/StorageProviderModule.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt | 2 +- .../com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt | 2 +- .../twidere/twiderex/extensions/LazyPagingItemsExtensions.kt | 2 +- .../com/twidere/twiderex/extensions/MastodonExtensions.kt | 2 +- .../kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt | 2 +- .../com/twidere/twiderex/extensions/TextFieldValueExtensions.kt | 2 +- .../com/twidere/twiderex/extensions/TimestampExtension.kt | 2 +- .../com/twidere/twiderex/extensions/VideoPlaybackExtensions.kt | 2 +- .../com/twidere/twiderex/http/TwidereHttpConfigProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt | 2 +- .../com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt | 2 +- .../com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt | 2 +- .../com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt | 2 +- .../com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt | 2 +- .../com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt | 2 +- .../com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt | 2 +- .../com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt | 2 +- .../kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/LocationProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/kmp/Platform.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/StorageProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/StorageProviderExt.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt | 2 +- .../kotlin/com/twidere/twiderex/model/AccountDetails.kt | 2 +- .../kotlin/com/twidere/twiderex/model/AccountPreferences.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt | 2 +- .../kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt | 2 +- .../kotlin/com/twidere/twiderex/model/MicroBlogKey.kt | 2 +- .../kotlin/com/twidere/twiderex/model/TwidereAccount.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/Credentials.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt | 2 +- .../kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/ComposeType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/ListType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/Mastodon.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/MediaType.kt | 2 +- .../com/twidere/twiderex/model/enums/NotificationCursorType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/PlatformType.kt | 2 +- .../kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt | 2 +- .../com/twidere/twiderex/model/enums/TwitterReplySettings.kt | 2 +- .../kotlin/com/twidere/twiderex/model/job/ComposeData.kt | 2 +- .../com/twidere/twiderex/model/job/DirectMessageDeleteData.kt | 2 +- .../com/twidere/twiderex/model/job/DirectMessageSendData.kt | 2 +- .../kotlin/com/twidere/twiderex/model/job/StatusResult.kt | 2 +- .../kotlin/com/twidere/twiderex/model/kmp/Location.kt | 2 +- .../com/twidere/twiderex/model/paging/NotificationCursor.kt | 2 +- .../kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt | 2 +- .../kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt | 2 +- .../kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt | 2 +- .../kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiGif.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt | 2 +- .../kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt | 2 +- .../kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt | 2 +- .../twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt | 2 +- .../com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt | 2 +- .../com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt | 2 +- .../com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt | 2 +- .../kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt | 2 +- .../com/twidere/twiderex/notification/AppNotificationManager.kt | 2 +- .../com/twidere/twiderex/notification/InAppNotification.kt | 2 +- .../twidere/twiderex/notification/NotificationChannelSpec.kt | 2 +- .../kotlin/com/twidere/twiderex/paging/IPagingList.kt | 2 +- .../twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt | 2 +- .../com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt | 2 +- .../com/twidere/twiderex/paging/crud/PagingMemoryCache.kt | 2 +- .../twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt | 2 +- .../twiderex/paging/mediator/dm/DMConversationMediator.kt | 2 +- .../com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt | 2 +- .../com/twidere/twiderex/paging/mediator/list/ListsMediator.kt | 2 +- .../twiderex/paging/mediator/list/ListsMembersMediator.kt | 2 +- .../twiderex/paging/mediator/list/ListsTimelineMediator.kt | 2 +- .../twiderex/paging/mediator/list/ListsUserPagingMediator.kt | 2 +- .../twiderex/paging/mediator/paging/CursorPagingMediator.kt | 2 +- .../mediator/paging/CursorWithCustomOrderPagingMediator.kt | 2 +- .../twiderex/paging/mediator/paging/MaxIdPagingMediator.kt | 2 +- .../twidere/twiderex/paging/mediator/paging/PagingMediator.kt | 2 +- .../paging/mediator/paging/PagingTimelineMediatorBase.kt | 2 +- .../twiderex/paging/mediator/paging/PagingWithGapMediator.kt | 2 +- .../twiderex/paging/mediator/search/SearchMediaMediator.kt | 2 +- .../twiderex/paging/mediator/search/SearchStatusMediator.kt | 2 +- .../paging/mediator/status/MastodonStatusContextMediator.kt | 2 +- .../paging/mediator/status/TwitterConversationMediator.kt | 2 +- .../twiderex/paging/mediator/timeline/HomeTimelineMediator.kt | 2 +- .../paging/mediator/timeline/MastodonHashtagTimelineMediator.kt | 2 +- .../paging/mediator/timeline/MentionTimelineMediator.kt | 2 +- .../paging/mediator/timeline/NotificationTimelineMediator.kt | 2 +- .../mediator/timeline/mastodon/FederatedTimelineMediator.kt | 2 +- .../paging/mediator/timeline/mastodon/LocalTimelineMediator.kt | 2 +- .../com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt | 2 +- .../twiderex/paging/mediator/user/UserFavouriteMediator.kt | 2 +- .../twidere/twiderex/paging/mediator/user/UserMediaMediator.kt | 2 +- .../twidere/twiderex/paging/mediator/user/UserStatusMediator.kt | 2 +- .../com/twidere/twiderex/paging/source/FollowersPagingSource.kt | 2 +- .../com/twidere/twiderex/paging/source/FollowingPagingSource.kt | 2 +- .../twiderex/paging/source/ListsSubscribersPagingSource.kt | 2 +- .../twiderex/paging/source/MastodonSearchHashtagPagingSource.kt | 2 +- .../twidere/twiderex/paging/source/SearchUserPagingSource.kt | 2 +- .../com/twidere/twiderex/paging/source/UserPagingSource.kt | 2 +- .../com/twidere/twiderex/paging/source/gif/GifPagingSource.kt | 2 +- .../twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt | 2 +- .../twiderex/paging/source/gif/GifTrendingPagingSource.kt | 2 +- .../com/twidere/twiderex/preferences/PreferencesHolder.kt | 2 +- .../com/twidere/twiderex/preferences/ProvidePreferences.kt | 2 +- .../twidere/twiderex/preferences/model/AppearancePreferences.kt | 2 +- .../twidere/twiderex/preferences/model/DisplayPreferences.kt | 2 +- .../com/twidere/twiderex/preferences/model/MiscPreferences.kt | 2 +- .../twiderex/preferences/model/NotificationPreferences.kt | 2 +- .../preferences/serializer/AppearancePreferencesSerializer.kt | 2 +- .../preferences/serializer/DisplayPreferencesSerializer.kt | 2 +- .../preferences/serializer/MiscPreferencesSerializer.kt | 2 +- .../preferences/serializer/NotificationPreferencesSerializer.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/AccountRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/CacheRepository.kt | 2 +- .../com/twidere/twiderex/repository/DirectMessageRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/DraftRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/GifRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/ListsRepository.kt | 2 +- .../com/twidere/twiderex/repository/ListsUsersRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/MediaRepository.kt | 2 +- .../com/twidere/twiderex/repository/NotificationRepository.kt | 2 +- .../com/twidere/twiderex/repository/ReactionRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/SearchRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/StatusRepository.kt | 2 +- .../com/twidere/twiderex/repository/TimelineRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/TrendRepository.kt | 2 +- .../com/twidere/twiderex/repository/UserListRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/UserRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/DraftListScene.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt | 2 +- .../commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/SignInScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/StatusScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt | 2 +- .../twiderex/scenes/compose/ComposeSearchHashtagScene.kt | 2 +- .../twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt | 2 +- .../com/twidere/twiderex/scenes/dm/DMConversationListScene.kt | 2 +- .../com/twidere/twiderex/scenes/dm/DMConversationScene.kt | 2 +- .../com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt | 2 +- .../com/twidere/twiderex/scenes/home/AllNotificationItem.kt | 2 +- .../com/twidere/twiderex/scenes/home/DMConversationListItem.kt | 2 +- .../com/twidere/twiderex/scenes/home/DraftNavigationItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt | 2 +- .../com/twidere/twiderex/scenes/home/ListsNavigationItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/MeItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt | 2 +- .../twiderex/scenes/home/mastodon/FederatedTimelineItem.kt | 2 +- .../twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt | 2 +- .../twiderex/scenes/home/mastodon/MastodonNotificationItem.kt | 2 +- .../com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt | 2 +- .../com/twidere/twiderex/scenes/lists/ListsMembersScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt | 2 +- .../com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt | 2 +- .../com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt | 2 +- .../twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt | 2 +- .../twiderex/scenes/lists/platform/MastodonListsEditDialog.kt | 2 +- .../twiderex/scenes/lists/platform/TwitterListsCreateScene.kt | 2 +- .../twiderex/scenes/lists/platform/TwitterListsEditScene.kt | 2 +- .../twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt | 2 +- .../com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt | 2 +- .../com/twidere/twiderex/scenes/search/SearchInputScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt | 2 +- .../twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt | 2 +- .../com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt | 2 +- .../com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt | 2 +- .../com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt | 2 +- .../twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt | 2 +- .../twidere/twiderex/scenes/settings/AccountManagementScene.kt | 2 +- .../twiderex/scenes/settings/AccountNotificationScene.kt | 2 +- .../com/twidere/twiderex/scenes/settings/AppearanceScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt | 2 +- .../com/twidere/twiderex/scenes/settings/NotificationScene.kt | 2 +- .../com/twidere/twiderex/scenes/settings/SettingsScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt | 2 +- .../com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt | 2 +- .../twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/user/UserScene.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt | 2 +- .../src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt | 2 +- common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/OAuthLauncher.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/PlatformResolver.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/TwidereFilePicker.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt | 2 +- .../com/twidere/twiderex/utils/video/CustomVideoControl.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/video/VideoPool.kt | 2 +- .../com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt | 2 +- .../twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt | 2 +- .../viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt | 2 +- .../twiderex/viewmodel/lists/ListsSearchUserViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt | 2 +- .../twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt | 2 +- .../viewmodel/mastodon/MastodonSearchHashtagViewModel.kt | 2 +- .../twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/search/SearchInputViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/search/SearchUserViewModel.kt | 2 +- .../twiderex/viewmodel/settings/AccountNotificationViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt | 2 +- .../twiderex/viewmodel/settings/NotificationViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt | 2 +- .../twiderex/viewmodel/timeline/HomeTimelineViewModel.kt | 2 +- .../twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt | 2 +- .../viewmodel/timeline/NotificationTimelineViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt | 2 +- .../viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt | 2 +- .../viewmodel/timeline/mastodon/LocalTimelineViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt | 2 +- .../twiderex/viewmodel/twitter/TwitterSignInViewModel.kt | 2 +- .../viewmodel/twitter/search/TwitterSearchMediaViewModel.kt | 2 +- .../twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt | 2 +- .../twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt | 2 +- .../com/twidere/twiderex/viewmodel/user/UserListViewModel.kt | 2 +- .../twiderex/viewmodel/user/UserMediaTimelineViewModel.kt | 2 +- .../twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt | 2 +- .../kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt | 2 +- .../moe/tlaster/nestedscrollview/NestedScrollViewState.kt | 2 +- .../commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt | 2 +- .../kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt | 2 +- .../moe/tlaster/precompose/lifecycle/LifecycleObserver.kt | 2 +- .../kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt | 2 +- .../moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt | 2 +- .../moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/BackHandler.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt | 2 +- .../moe/tlaster/precompose/navigation/NavControllerViewModel.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/NavHost.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/NavOptions.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/Navigator.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/QueryString.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt | 2 +- .../moe/tlaster/precompose/navigation/RouteMatchResult.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteParser.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteStack.kt | 2 +- .../moe/tlaster/precompose/navigation/RouteStackManager.kt | 2 +- .../moe/tlaster/precompose/navigation/route/ComposeRoute.kt | 2 +- .../moe/tlaster/precompose/navigation/route/DialogRoute.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/route/Route.kt | 2 +- .../moe/tlaster/precompose/navigation/route/SceneRoute.kt | 2 +- .../precompose/navigation/transition/AnimatedDialogRoute.kt | 2 +- .../tlaster/precompose/navigation/transition/AnimatedRoute.kt | 2 +- .../precompose/navigation/transition/DialogTransition.kt | 2 +- .../tlaster/precompose/navigation/transition/NavTransition.kt | 2 +- .../kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt | 2 +- .../kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt | 2 +- .../kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt | 2 +- .../moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt | 2 +- .../kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt | 2 +- .../moe/tlaster/precompose/viewmodel/ViewModelProvider.kt | 2 +- .../kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt | 2 +- .../moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt | 2 +- .../moe/tlaster/precompose/viewmodel/compose/ViewModel.kt | 2 +- common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt | 2 +- common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt | 2 +- .../kotlin/com/twidere/twiderex/MainThreadTestBase.kt | 2 +- .../kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt | 2 +- .../kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt | 2 +- .../kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt | 2 +- .../com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt | 2 +- .../db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt | 2 +- .../twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt | 2 +- .../twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt | 2 +- .../twiderex/db/dao/SqlDelightPagingTimelineDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightStatusDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt | 2 +- .../com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt | 2 +- .../twidere/twiderex/db/sqldelight/AccountQueriesImplTest.kt | 2 +- .../twiderex/db/sqldelight/DMConversationQueriesImplTest.kt | 2 +- .../twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt | 2 +- .../twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt | 2 +- .../twiderex/db/sqldelight/PagingTimelineQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/StatusQueriesImplTest.kt | 2 +- .../twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt | 2 +- .../twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt | 2 +- .../com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt | 2 +- .../twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt | 2 +- .../twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt | 2 +- .../db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt | 2 +- .../db/sqldelight/adapter/StringListColumnAdapterTest.kt | 2 +- .../twiderex/db/sqldelight/transform/DMEventTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/DraftTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/ListTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/MediaTransformTest.kt | 2 +- .../db/sqldelight/transform/NotificationCursorTransformTest.kt | 2 +- .../db/sqldelight/transform/PagingTimelineTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/SearchTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/StatusTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/TrendTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt | 2 +- .../twiderex/db/sqldelight/transform/UserTransformTest.kt | 2 +- .../src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt | 2 +- .../twiderex/mock/db/dao/MockDirectMessageConversationDao.kt | 2 +- .../twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt | 2 +- .../twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt | 2 +- .../com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/model/MockModels.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt | 2 +- .../kotlin/com/twidere/twiderex/mock/service/ErrorService.kt | 2 +- .../twidere/twiderex/mock/service/MockDirectMessageService.kt | 2 +- .../com/twidere/twiderex/mock/service/MockListsService.kt | 2 +- .../com/twidere/twiderex/mock/service/MockLookUpService.kt | 2 +- .../twidere/twiderex/mock/service/MockNotificationService.kt | 2 +- .../twidere/twiderex/mock/service/MockRelationshipService.kt | 2 +- .../com/twidere/twiderex/mock/service/MockSearchService.kt | 2 +- .../com/twidere/twiderex/mock/service/MockTimelineService.kt | 2 +- .../com/twidere/twiderex/mock/service/MockTrendService.kt | 2 +- .../twiderex/paging/crud/MemoryCachePagingMediatorTest.kt | 2 +- .../twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt | 2 +- .../com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt | 2 +- .../twidere/twiderex/paging/dm/DMConversationMediatorTest.kt | 2 +- .../com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt | 2 +- .../twidere/twiderex/paging/lists/ListMembersMediatorTest.kt | 2 +- .../com/twidere/twiderex/paging/lists/ListsMediatorTest.kt | 2 +- .../twiderex/paging/lists/ListsUserPagingMediatorTest.kt | 2 +- .../twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt | 2 +- .../twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt | 2 +- .../twidere/twiderex/paging/search/SearchMediaMediatorTest.kt | 2 +- .../twidere/twiderex/paging/search/SearchStatusMediatorTest.kt | 2 +- .../twidere/twiderex/paging/source/FollowersPagingSourceTest.kt | 2 +- .../twidere/twiderex/paging/source/FollowingPagingSourceTest.kt | 2 +- .../twiderex/paging/source/ListsSubscribersPagingSourceTest.kt | 2 +- .../twiderex/paging/source/SearchUserPagingSourceTest.kt | 2 +- .../com/twidere/twiderex/paging/source/UserPagingSourceTest.kt | 2 +- .../twiderex/paging/timeline/HomeTimelineMediatorTest.kt | 2 +- .../twiderex/paging/timeline/MentionTimelineMediatorTest.kt | 2 +- .../paging/timeline/NotificationTimelineMediatorTest.kt | 2 +- .../twiderex/paging/timeline/UserFavoriteMediatorTest.kt | 2 +- .../twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt | 2 +- .../twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt | 2 +- .../com/twidere/twiderex/paging/trend/TrendMediatorTest.kt | 2 +- .../twidere/twiderex/repository/DirectMessageRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/DraftRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/ListsRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/MediaRepositoryTest.kt | 2 +- .../twidere/twiderex/repository/NotificationRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/ReactionRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/SearchRepositoryTest.kt | 2 +- .../com/twidere/twiderex/repository/StatusRepositoryTest.kt | 2 +- .../com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt | 2 +- .../twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt | 2 +- .../com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt | 2 +- .../com/twidere/twiderex/viewmodel/StatusViewModelTest.kt | 2 +- .../kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt | 2 +- .../twiderex/viewmodel/lists/ListsCreateViewModelTest.kt | 2 +- .../twiderex/viewmodel/lists/ListsModifyViewModelTest.kt | 2 +- .../com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt | 2 +- .../twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt | 2 +- .../twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt | 2 +- .../viewmodel/user/UserFavouriteTimelineViewModelTest.kt | 2 +- .../twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt | 2 +- .../twiderex/viewmodel/user/UserTimelineViewModelTest.kt | 2 +- .../com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt | 2 +- .../kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt | 2 +- .../moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt | 2 +- .../moe/tlaster/precompose/navigation/RouteBuilderTest.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt | 2 +- .../moe/tlaster/precompose/navigation/RouteParserTest2.kt | 2 +- .../kotlin/moe/tlaster/precompose/navigation/TestRoute.kt | 2 +- .../moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt | 2 +- .../kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt | 2 +- .../kotlin/androidx.paging.compose/PagingPlaceholderKey.kt | 2 +- .../src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt | 2 +- .../kotlin/com/twidere/twiderex/action/ComposeAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DirectMessageAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/DraftAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/MediaAction.kt | 2 +- .../kotlin/com/twidere/twiderex/action/StatusActions.kt | 2 +- .../kotlin/com/twidere/twiderex/component/PlatformInsets.kt | 2 +- .../twidere/twiderex/component/foundation/DesktopMediaPlayer.kt | 2 +- .../com/twidere/twiderex/component/foundation/LocalView.kt | 2 +- .../twidere/twiderex/component/foundation/VLCJMediaPlayer.kt | 2 +- .../component/foundation/platform/PlatformAlertDialog.kt | 2 +- .../twiderex/component/foundation/platform/PlatformDialog.kt | 2 +- .../foundation/platform/PlatformDropdownMenu.desktop.kt | 2 +- .../component/foundation/platform/PlatformPlayerView.desktop.kt | 2 +- .../kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt | 2 +- .../com/twidere/twiderex/di/modules/ActionsModule.desktop.kt | 2 +- .../kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt | 2 +- .../com/twidere/twiderex/di/modules/PlatformModule.desktop.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/image/ImageCache.kt | 2 +- .../kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt | 2 +- .../kotlin/com/twidere/twiderex/image/ImagePainter.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/init/Initializer.kt | 2 +- .../twidere/twiderex/init/TwidereServiceFactoryInitialTask.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/CookieManager.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/LocationProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt | 2 +- .../src/desktopMain/kotlin/com/twidere/twiderex/kmp/Platform.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt | 2 +- .../kotlin/com/twidere/twiderex/kmp/StorageProvider.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt | 2 +- .../com/twidere/twiderex/model/AccountPreferencesFactory.kt | 2 +- .../kotlin/com/twidere/twiderex/navigation/Route.desktop.kt | 2 +- .../com/twidere/twiderex/notification/AppNotificationManager.kt | 2 +- .../kotlin/com/twidere/twiderex/repository/AccountRepository.kt | 2 +- .../kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt | 2 +- .../scenes/settings/AccountNotificationScene.desktop.kt | 2 +- .../desktopMain/kotlin/com/twidere/twiderex/utils/SystemInfo.kt | 2 +- .../com/twidere/twiderex/utils/WindowsDatastoreModifier.kt | 2 +- .../kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt | 2 +- .../kotlin/moe/tlaster/precompose/PreComposeWindow.kt | 2 +- .../kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt | 2 +- .../com/twidere/twiderex/repository/CacheRepositoryTest.kt | 2 +- desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt | 2 +- .../com/twidere/twiderex/media/DesktopMediaPlayerFactoryImpl.kt | 2 +- .../jvmMain/kotlin/com/twidere/twiderex/media/JFXMediaPlayer.kt | 2 +- routeProcessor/src/main/kotlin/AppRoute.kt | 2 +- routeProcessor/src/main/kotlin/RouteDefinition.kt | 2 +- routeProcessor/src/main/kotlin/RouteProcessor.kt | 2 +- routeProcessor/src/main/kotlin/RouteProcessorProvider.kt | 2 +- services/src/main/java/com/twidere/services/gif/GifService.kt | 2 +- .../java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt | 2 +- .../main/java/com/twidere/services/gif/giphy/GiphyResource.kt | 2 +- .../main/java/com/twidere/services/gif/giphy/GiphyService.kt | 2 +- services/src/main/java/com/twidere/services/gif/model/IGif.kt | 2 +- .../java/com/twidere/services/http/AuthorizationInterceptor.kt | 2 +- services/src/main/java/com/twidere/services/http/Errors.kt | 2 +- .../main/java/com/twidere/services/http/HttpClientFactory.kt | 2 +- .../main/java/com/twidere/services/http/HttpConfigProvider.kt | 2 +- .../main/java/com/twidere/services/http/MicroBlogException.kt | 2 +- .../java/com/twidere/services/http/MicroBlogHttpException.kt | 2 +- .../com/twidere/services/http/authorization/Authorization.kt | 2 +- .../twidere/services/http/authorization/BearerAuthorization.kt | 2 +- .../twidere/services/http/authorization/EmptyAuthorization.kt | 2 +- .../twidere/services/http/authorization/OAuth1Authorization.kt | 2 +- .../main/java/com/twidere/services/http/config/HttpConfig.kt | 2 +- .../com/twidere/services/http/config/HttpConfigClientFactory.kt | 2 +- .../java/com/twidere/services/mastodon/MastodonOAuthService.kt | 2 +- .../main/java/com/twidere/services/mastodon/MastodonService.kt | 2 +- .../java/com/twidere/services/mastodon/api/AccountResources.kt | 2 +- .../com/twidere/services/mastodon/api/FriendshipResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/ListsResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/LookupResources.kt | 2 +- .../com/twidere/services/mastodon/api/MastodonOAuthResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/MastodonResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/SearchResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/StatusResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/TimelineResources.kt | 2 +- .../java/com/twidere/services/mastodon/api/TrendsResources.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Account.kt | 2 +- .../java/com/twidere/services/mastodon/model/Application.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Attachment.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Card.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Context.kt | 2 +- .../services/mastodon/model/CreateApplicationResponse.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Emoji.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Field.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Hashtag.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/History.kt | 2 +- .../com/twidere/services/mastodon/model/MastodonAuthScope.kt | 2 +- .../java/com/twidere/services/mastodon/model/MastodonList.kt | 2 +- .../java/com/twidere/services/mastodon/model/MastodonPaging.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/MediaType.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Mention.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Meta.kt | 2 +- .../java/com/twidere/services/mastodon/model/Notification.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Option.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Original.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Poll.kt | 2 +- .../java/com/twidere/services/mastodon/model/PostAccounts.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/PostList.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/PostPoll.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/PostStatus.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/PostVote.kt | 2 +- .../com/twidere/services/mastodon/model/RelationshipResponse.kt | 2 +- .../com/twidere/services/mastodon/model/RequestTokenResponse.kt | 2 +- .../java/com/twidere/services/mastodon/model/SearchResult.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/SearchType.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Small.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Source.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Status.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Tag.kt | 2 +- .../src/main/java/com/twidere/services/mastodon/model/Trend.kt | 2 +- .../java/com/twidere/services/mastodon/model/UploadResponse.kt | 2 +- .../services/mastodon/model/VerifyCredentialsResponse.kt | 2 +- .../main/java/com/twidere/services/mastodon/model/Visibility.kt | 2 +- .../services/mastodon/model/exceptions/MastodonException.kt | 2 +- .../java/com/twidere/services/microblog/DirectMessageService.kt | 2 +- .../java/com/twidere/services/microblog/DownloadMediaService.kt | 2 +- .../main/java/com/twidere/services/microblog/ListsService.kt | 2 +- .../main/java/com/twidere/services/microblog/LookupService.kt | 2 +- .../java/com/twidere/services/microblog/MicroBlogService.kt | 2 +- .../java/com/twidere/services/microblog/NotificationService.kt | 2 +- .../java/com/twidere/services/microblog/RelationshipService.kt | 2 +- .../main/java/com/twidere/services/microblog/SearchService.kt | 2 +- .../main/java/com/twidere/services/microblog/StatusService.kt | 2 +- .../main/java/com/twidere/services/microblog/TimelineService.kt | 2 +- .../main/java/com/twidere/services/microblog/TrendService.kt | 2 +- .../java/com/twidere/services/microblog/model/IDirectMessage.kt | 2 +- .../java/com/twidere/services/microblog/model/IListModel.kt | 2 +- .../java/com/twidere/services/microblog/model/INotification.kt | 2 +- .../main/java/com/twidere/services/microblog/model/IPaging.kt | 2 +- .../java/com/twidere/services/microblog/model/IRelationship.kt | 2 +- .../com/twidere/services/microblog/model/ISearchResponse.kt | 2 +- .../main/java/com/twidere/services/microblog/model/IStatus.kt | 2 +- .../main/java/com/twidere/services/microblog/model/ITrend.kt | 2 +- .../src/main/java/com/twidere/services/microblog/model/IUser.kt | 2 +- .../src/main/java/com/twidere/services/nitter/NitterService.kt | 2 +- .../java/com/twidere/services/nitter/TweetNotFoundException.kt | 2 +- .../main/java/com/twidere/services/nitter/model/Attachments.kt | 2 +- .../com/twidere/services/nitter/model/ConversationThreadItem.kt | 2 +- .../com/twidere/services/nitter/model/ConversationTimeline.kt | 2 +- .../src/main/java/com/twidere/services/nitter/model/Status.kt | 2 +- .../main/java/com/twidere/services/nitter/model/StatusCard.kt | 2 +- .../main/java/com/twidere/services/nitter/model/StatusStats.kt | 2 +- .../java/com/twidere/services/nitter/model/TweetNotFound.kt | 2 +- .../src/main/java/com/twidere/services/nitter/model/User.kt | 2 +- .../main/java/com/twidere/services/nitter/model/UserTimeline.kt | 2 +- .../twidere/services/nitter/model/serializer/DateSerializer.kt | 2 +- .../twidere/services/nitter/model/serializer/StatsSerializer.kt | 2 +- .../services/nitter/model/serializer/StatusBodySerializer.kt | 2 +- .../services/nitter/model/serializer/StatusIdSerializer.kt | 2 +- .../services/nitter/model/serializer/UrlDecodeSerializer.kt | 2 +- .../src/main/java/com/twidere/services/proxy/ProxyConfig.kt | 2 +- .../java/com/twidere/services/proxy/ReverseProxyInterceptor.kt | 2 +- .../twidere/services/serializer/DateQueryConverterFactory.kt | 2 +- .../main/java/com/twidere/services/serializer/DateSerializer.kt | 2 +- .../java/com/twidere/services/serializer/DateSerializerV2.kt | 2 +- .../twidere/services/serializer/DateSerializerV2WithOffset.kt | 2 +- .../main/java/com/twidere/services/twitter/TwitterErrorCodes.kt | 2 +- .../java/com/twidere/services/twitter/TwitterOAuthService.kt | 2 +- .../main/java/com/twidere/services/twitter/TwitterService.kt | 2 +- .../com/twidere/services/twitter/api/DirectMessagesResources.kt | 2 +- .../java/com/twidere/services/twitter/api/FollowsResources.kt | 2 +- .../com/twidere/services/twitter/api/FriendshipResources.kt | 2 +- .../java/com/twidere/services/twitter/api/ListsResources.kt | 2 +- .../java/com/twidere/services/twitter/api/LookupResources.kt | 2 +- .../java/com/twidere/services/twitter/api/SearchResources.kt | 2 +- .../java/com/twidere/services/twitter/api/StatusResources.kt | 2 +- .../java/com/twidere/services/twitter/api/TimelineResources.kt | 2 +- .../java/com/twidere/services/twitter/api/TrendsResources.kt | 2 +- .../com/twidere/services/twitter/api/TwitterOAuthResources.kt | 2 +- .../java/com/twidere/services/twitter/api/TwitterResources.kt | 2 +- .../java/com/twidere/services/twitter/api/UploadResources.kt | 2 +- .../java/com/twidere/services/twitter/api/UsersResources.kt | 2 +- .../main/java/com/twidere/services/twitter/model/AccessToken.kt | 2 +- .../java/com/twidere/services/twitter/model/AnnotationV2.kt | 2 +- .../java/com/twidere/services/twitter/model/AttachmentsV2.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/BlockV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/Contributor.kt | 2 +- .../main/java/com/twidere/services/twitter/model/Coordinates.kt | 2 +- .../main/java/com/twidere/services/twitter/model/Description.kt | 2 +- .../java/com/twidere/services/twitter/model/DescriptionURL.kt | 2 +- .../com/twidere/services/twitter/model/DirectMessageResponse.kt | 2 +- .../main/java/com/twidere/services/twitter/model/Entities.kt | 2 +- .../main/java/com/twidere/services/twitter/model/EntitiesURL.kt | 2 +- .../java/com/twidere/services/twitter/model/EntitiesURLV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/EntitiesV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/FluffyURL.kt | 2 +- .../main/java/com/twidere/services/twitter/model/GeoPoint.kt | 2 +- .../main/java/com/twidere/services/twitter/model/HashtagV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/Hashtags.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/ImageV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/IncludesV2.kt | 2 +- .../java/com/twidere/services/twitter/model/ListUserResponse.kt | 2 +- .../com/twidere/services/twitter/model/MediaPublicMetrics.kt | 2 +- .../main/java/com/twidere/services/twitter/model/MediaSize.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/MediaV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/MentionV2.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Meta.kt | 2 +- .../main/java/com/twidere/services/twitter/model/OAuthToken.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Place.kt | 2 +- .../main/java/com/twidere/services/twitter/model/PlaceGeo.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/PlaceV2.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/PollV2.kt | 2 +- .../java/com/twidere/services/twitter/model/PollV2Option.kt | 2 +- .../java/com/twidere/services/twitter/model/ProfileBanner.kt | 2 +- .../com/twidere/services/twitter/model/ProfileBannerSize.kt | 2 +- .../java/com/twidere/services/twitter/model/PublicMetricsV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/PurpleMedia.kt | 2 +- .../main/java/com/twidere/services/twitter/model/PurpleURLV2.kt | 2 +- .../com/twidere/services/twitter/model/QuotedStatusPermalink.kt | 2 +- .../com/twidere/services/twitter/model/ReferencedTweetType.kt | 2 +- .../com/twidere/services/twitter/model/ReferencedTweetV2.kt | 2 +- .../java/com/twidere/services/twitter/model/Relationship.kt | 2 +- .../com/twidere/services/twitter/model/RelationshipResponse.kt | 2 +- .../java/com/twidere/services/twitter/model/SearchMetadataV1.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Sizes.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Source.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Status.kt | 2 +- .../java/com/twidere/services/twitter/model/StatusEntities.kt | 2 +- .../twidere/services/twitter/model/StatusExtendedEntities.kt | 2 +- .../com/twidere/services/twitter/model/StatusReactionsV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/StatusV2.kt | 2 +- .../java/com/twidere/services/twitter/model/StatusV2Entities.kt | 2 +- .../main/java/com/twidere/services/twitter/model/StatusV2Geo.kt | 2 +- .../com/twidere/services/twitter/model/StatusV2PublicMetrics.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Symbol.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Target.kt | 2 +- .../java/com/twidere/services/twitter/model/TwitterErrorV2.kt | 2 +- .../main/java/com/twidere/services/twitter/model/TwitterList.kt | 2 +- .../java/com/twidere/services/twitter/model/TwitterPaging.kt | 2 +- .../java/com/twidere/services/twitter/model/TwitterQueryList.kt | 2 +- .../com/twidere/services/twitter/model/TwitterResponseV2.kt | 2 +- .../twidere/services/twitter/model/TwitterSearchResponseV1.kt | 2 +- .../twidere/services/twitter/model/TwitterSearchResponseV2.kt | 2 +- .../twidere/services/twitter/model/TwitterTrendsResponseV1.kt | 2 +- .../com/twidere/services/twitter/model/TwitterUploadResponse.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/URL.kt | 2 +- .../java/com/twidere/services/twitter/model/URLElementV2.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/User.kt | 2 +- .../java/com/twidere/services/twitter/model/UserEntities.kt | 2 +- .../main/java/com/twidere/services/twitter/model/UserMention.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/UserV2.kt | 2 +- .../src/main/java/com/twidere/services/twitter/model/Variant.kt | 2 +- .../main/java/com/twidere/services/twitter/model/VideoInfo.kt | 2 +- .../services/twitter/model/exceptions/TwitterApiException.kt | 2 +- .../services/twitter/model/exceptions/TwitterApiExceptionV2.kt | 2 +- .../com/twidere/services/twitter/model/fields/Expansions.kt | 2 +- .../com/twidere/services/twitter/model/fields/MediaFields.kt | 2 +- .../com/twidere/services/twitter/model/fields/PlaceFields.kt | 2 +- .../com/twidere/services/twitter/model/fields/PollFields.kt | 2 +- .../com/twidere/services/twitter/model/fields/TweetFields.kt | 2 +- .../com/twidere/services/twitter/model/fields/UserFields.kt | 2 +- .../twitter/model/request/TwitterReactionRequestBody.kt | 2 +- services/src/main/java/com/twidere/services/utils/Base64.java | 2 +- services/src/main/java/com/twidere/services/utils/Flags.kt | 2 +- services/src/main/java/com/twidere/services/utils/Json.kt | 2 +- services/src/main/java/com/twidere/services/utils/OkHttp.kt | 2 +- .../src/main/java/com/twidere/services/utils/QueryString.kt | 2 +- services/src/main/java/com/twidere/services/utils/Stream.kt | 2 +- .../java/com/twidere/services/HttpConfigClientFactoryTest.kt | 2 +- services/src/test/java/com/twidere/services/JsonTest.kt | 2 +- services/src/test/java/com/twidere/services/api/MockApiTest.kt | 2 +- .../test/java/com/twidere/services/api/common/MockApiAsset.kt | 2 +- .../test/java/com/twidere/services/api/common/MockRetrofit.kt | 2 +- .../test/java/com/twidere/services/api/common/MockServices.kt | 2 +- .../twidere/services/api/common/Request2AssetPathConvertor.kt | 2 +- .../com/twidere/services/api/mastodon/MastodonListsApiTest.kt | 2 +- .../services/api/mastodon/MastodonRequest2AssetPathConvertor.kt | 2 +- .../com/twidere/services/api/mastodon/MastodonTrendsApiTest.kt | 2 +- .../src/test/java/com/twidere/services/api/nitter/NitterTest.kt | 2 +- .../twidere/services/api/proxy/ReverseProxyInterceptorTest.kt | 2 +- .../twidere/services/api/twitter/TwitterDirectMessageApiTest.kt | 2 +- .../com/twidere/services/api/twitter/TwitterListsApiTest.kt | 2 +- .../services/api/twitter/TwitterRequest2AssetPathConvertor.kt | 2 +- .../com/twidere/services/api/twitter/TwitterTrendsApiTest.kt | 2 +- .../test/java/com/twidere/services/service/ListServiceTest.kt | 2 +- .../services/service/mastodon/MastodonListServiceTest.kt | 2 +- .../services/service/mastodon/MastodonTrendsServiceTest.kt | 2 +- .../twidere/services/service/twitter/TwitterListServiceTest.kt | 2 +- .../services/service/twitter/TwitterTrendsServiceTest.kt | 2 +- spotless/license | 2 +- 1109 files changed, 1109 insertions(+), 1109 deletions(-) diff --git a/README.md b/README.md index 5eec16699..aedadba47 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Twidere X is still in an early stage and will be periodically re-structuring/ref ``` Twidere X - Copyright (C) 2020-2021 Tlaster + Copyright (C) TwidereProject and Contributors Twidere X is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/android/src/fdroid/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt b/android/src/fdroid/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt index e3d5cb336..c181f8687 100644 --- a/android/src/fdroid/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt +++ b/android/src/fdroid/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/google/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt b/android/src/google/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt index 72dc225a6..cd651bad2 100644 --- a/android/src/google/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt +++ b/android/src/google/kotlin/com.twidere.twiderex/MissingSplitsCheckerImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt index f34e96a2d..9a251c480 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereApp.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index b820bcaa4..78979d989 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt b/android/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt index a6e93e814..ca6cc3db5 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/extensions/UriExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt b/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt index 1de3d9f3b..4d33def07 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/service/AccountAuthenticatorService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt index 88b0e7fec..a748ab20c 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index 08ca1bc96..8a2e7e443 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt index aafc07694..b12e39d15 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageConversationDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt index 964624c22..908e7618e 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DirectMessageEventDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt index b8f685b8a..ec5d126d2 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/DraftDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt index f804f5b8c..bcd242e31 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/ListsDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt index 2aba33d7c..96a33a7f8 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/MediaDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt index d2a68013a..9d616616e 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/NotificationCursorDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt index 8d963f986..bcea72bef 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/PagingTimelineDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt index 0a0b5b6f7..7c1a4afce 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/SearchDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt index efd2a5c2a..510437c61 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/StatusDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt index 70ed18ac6..6c142ee31 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/TrendDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt index 745d7d575..aae3bfb65 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/UserDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt index 00f1dc489..a66b6bde3 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/AppDatabaseDaoTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt index 02777a5d5..62b38145e 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/BaseDaoTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt index 7089ca84d..c28d29c09 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/db/base/CacheDatabaseDaoTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt index caf37bc68..4c8be6dfd 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt index d7c3f7efd..1c7e08746 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt index e181e6f65..5f3560b81 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/room/RoomAppDatabaseMigrationTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt b/common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt index 3f2a4b9e4..92fb8735d 100644 --- a/common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt +++ b/common/src/androidMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt index 3120b4504..7ec580907 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/TwidereApplication.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index 74ab6a4d7..5b5e27838 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt index 3f1163a43..35af55101 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt index 8757a7228..10a1ec091 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index 72ec213d7..8286fe231 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt index ee9e13280..985e0ebcd 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 83b8c9b00..2acd33eee 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt index f874057d4..917dc2f83 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt index a17f6195c..ecbefe7ff 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/RemainingTimeVideo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt index ecbbbf42b..56b8fb38b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/WebComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.android.kt index 028f45682..3758af139 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt index b353cb9fb..a442dfd58 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.android.kt index 2357e2b08..b473af672 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt index deff3c016..87e808d04 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index fe0eae307..a5e9b343f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt index d1e7ed560..1dd368ebc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/AppDatabaseImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt index 7f5f9560f..ee319d46f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/CacheDatabaseImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt index f6339ed2f..87e884921 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageConversationDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt index 2a392ffc8..5c54d38c9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DirectMessageEventDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt index 44e21e3d9..5137710c4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/DraftDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt index f1d15a285..467e180d6 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/ListsDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt index 5a79199a8..7411ed5e3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/MediaDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt index be0a6342e..ffd68af20 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/NotificationCursorDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt index e4c672960..ce46e1597 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/PagingTimelineDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt index f5b6761c7..bc350b9f0 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/SearchDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt index 894a0371e..22fd969fe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/StatusDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt index 6ceae8028..3f4760e0c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/TrendDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt index fe064ef35..9c79885b9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/dataprovider/db/dao/UserDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt index 89a5d929f..fe424908d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/db/transform/WorkDataTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt index f233ac7a9..7eb9c139d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt index 296c4873c..4c4f818b6 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt index cacc07b9b..621dc4427 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt index c84c11699..a9eb3d12f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt index e4458cb3b..efa8fab7f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/DataExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/FileProviderExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/FileProviderExtensions.kt index 0b573c9a8..098c54a61 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/FileProviderExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/FileProviderExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt index 5138d2c33..bfd6da273 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ViewExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt index 09670e9d0..987716ce1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/WindowExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt index 5a7298b50..6eaa43a21 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/DirectMessageInitializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt index 7d0fc4093..1e24aa5f8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationChannelInitializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt index 47c132bec..d07131ea4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/NotificationInitializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt index c24134719..4132fbaad 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/initializer/TwidereServiceInitializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt index c9c72aae9..beb8f85bc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 4ffc0c228..b05d6abd6 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 0a105b385..92b16382e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 20d43d3de..4a7baf260 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index bbdf296d5..92c760fd4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt index 24caf6774..3477c51b4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt index c77d25328..903484ea6 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt index e87a86438..103e1ee33 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/Platform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/Platform.kt index fd9228e27..ff44df36a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/Platform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/Platform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt index ed0e41f6e..3bbd433ee 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt index 37666f80e..14c3fc0ac 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index 0267fd974..fe8c11cb5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index a6d9ffcfd..7907383f3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt index cb96a7a5d..99e7d20e8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt index 4f43ecece..a9e33dc60 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt index 6d495bd8a..f69c42aff 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt index fe73870ba..a39f60fcc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/navigation/Route.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 73058045f..d107e8ced 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt index 7efd83fd5..b09bac3a9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 68ffb27cb..3bad4c357 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt index a1ed467b1..994fa4e9f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomAppDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt index aa54595d5..afce320e1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/RoomCacheDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt index 7bd80b572..8ee72b68e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageConversationDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt index 3fc012da9..adef85568 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDirectMessageEventDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt index 62545f422..eecc4e5b5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomDraftDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt index 13572b628..5654d971c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomListsDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt index 19158c6fc..1e8b79e61 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomMediaDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt index 7228df37c..e2f999756 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomNotificationCursorDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt index c6f28bed8..9d25d4013 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomPagingTimelineDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt index 9e6007740..3676d0c1d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomReactionDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt index 4e0f1d3d2..785816239 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomSearchDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt index 97d057c9c..413e5d8b8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt index 53925d686..9fbe6e3e7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomStatusReferenceDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt index c05c4e45d..e8032326e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt index 194aead83..49258b961 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomTrendHistoryDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt index 4033a10ce..94e702797 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUrlEntityDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt index aae3ee94d..a13c66663 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/dao/RoomUserDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt index f2a0a4dc7..071162e49 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/Alias.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt index 596fe99b8..83ca5b65f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMConversation.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt index fcb04d217..2d92420f5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDMEvent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt index f89fab264..63ce85ed5 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbDraft.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt index 844fc5930..03f63459c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt index 1434e8f32..f8727e7aa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbMedia.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt index f3fc2f17f..4297cb117 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbNotificationCursor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt index 74158451c..e2151c32f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbPagingTimeline.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt index 73c473667..ffbadd4ee 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbSearch.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt index b7dda0df7..dce62d03a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt index 54658ca8c..b71c73cbb 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReaction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt index 5f4bd93f2..7128fab77 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbStatusReference.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt index 73575d350..0fbec22b8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrend.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt index 718bb59c0..d6653d68c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbTrendHistory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt index 47c802ab2..d2e260162 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUrlEntity.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt index 36879c343..dcd186e12 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/DbUser.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt index 9fe8e41bb..026639069 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ComposeTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt index dd413b40c..356d68ab4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/ExtraConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt index 7f8e1edf0..52d0bddb7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MastodonVisibilityConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt index ac7cc12dc..128396c40 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MediaTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt index 5c9f14231..89a6191ee 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/MicroBlogKeyConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt index 73a63f878..10d51d52e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationCursorTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt index e531112d9..1f6558caa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/NotificationTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt index a5fdee325..8df21b8e1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/PlatformTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt index d71a37a48..0dcbf5356 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/StringListConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt index c803d6013..67bfef86b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/TwitterReplySettingsConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt index abfebb485..e5db49197 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/model/converter/UserTimelineTypeConverter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt index c89b4b839..517e4a1d4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt index a5c288bae..53f8e1bfe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/TablePagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt index 4aad0a455..e345f9699 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/AccountTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt index bcc3c1836..72aaf3a94 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DmConversationTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt index 875ffd8e4..fb6065dfe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/DraftTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt index 426607a1c..997462733 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/EmojiTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt index d03045a64..4c5483ffc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/ListTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt index 93ce4ea45..409fb741b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/MediaTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt index 385e1fa55..6bc313b62 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/NotificationCursorTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt index 6d15fa632..e6fd8f70e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/SearchTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt index e51495f3a..f336df36f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt index 39b3c3ada..575edea05 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/TrendTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt index 9c0ec2c68..d7ae12cf8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UrlEntityTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt index 19911f92c..0a5b79782 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/UserTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt index 3388643fd..42ad3e923 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt index a579f19ab..a70d12f56 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt index d22e87d2f..8afe91557 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterWebSignInScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt index 46e4b8dc4..b8cac811e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/BlurTransformation.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt index 40cbab289..34bd4c551 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/TwitterWebJavascriptInterface.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt index c1102a5f0..0993c7572 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/CacheDataSourceFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt index 671be7cb8..9fd8d8c49 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/utils/video/VideoCache.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt index f4103d17d..8f97fe671 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/view/LollipopFixWebView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt index 17e1819b4..b869600e9 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/DownloadMediaWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt index ab6e02ed3..222d5f5a4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/NotificationWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt index 1a80e4645..1f871649a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt index 7f450b3e8..a6ee1562e 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/ComposeWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt index dbd70c140..c1f993316 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/MastodonComposeWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt index ab0a66355..8cfa3c812 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/compose/TwitterComposeWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt index 8867f663c..5f12b852b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/database/DeleteDbStatusWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt index 2e0f71301..84ab9e547 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageDeleteWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt index d1b2fdb90..15f7bc55a 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageFetchWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt index 598d1fc6a..e56ccbb61 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/DirectMessageSendWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt index a1b6bdad1..2e956e4e7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/dm/TwitterDirectMessageSendWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt index 1c79e5a27..52c84ee61 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/RemoveDraftWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt index 3c9ae823d..d4667e27b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/draft/SaveDraftWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt index f0595063c..9c4a23d2c 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/DeleteStatusWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt index 69e9be899..1cbd485f1 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/LikeWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt index 14b9477ff..c1025d05f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/MastodonVoteWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt index 9bae59166..137f613b2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/RetweetWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt index 987e43370..d1099e3d3 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/StatusWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt index 3d2b54726..7c6f14ad2 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnLikeWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt index 92cfa5dfe..0f99a1d86 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UnRetweetWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt index 89cbbc4b2..7d8efec90 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/status/UpdateStatusWorker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt index 4012fe437..27955481b 100644 --- a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt +++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index 885ed98fd..d129cf5a4 100644 --- a/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/androidTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt index 575255aa8..770214dfe 100644 --- a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt +++ b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt b/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt index a7327b3cb..5f3ce37fd 100644 --- a/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt +++ b/common/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt index e22824838..c62114c7c 100644 --- a/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt +++ b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt b/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt index d3d331e48..bc223a58e 100644 --- a/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt +++ b/common/src/commonMain/kotlin/com/mxalbert/zoomable/ZoomableState.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 7dab86e47..76120d32c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt index 7de2ce975..67a4c0a87 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/Consts.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index b1123a9cb..1883f2e5a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt index a9b8997b9..3856f414f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt index a3eb3114c..6ef401c51 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index 385472d54..3b199c556 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt index c68028205..59c377dfb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt index 0cd1755b9..9f64c252c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/FormattedTime.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt index b24e66488..549cf26aa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/HumanizedTime.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt index 45d9f98ba..ed58565f4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/LoginLogo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 4acb69014..89fdc1132 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt index 2e8f9a346..3daf8f90e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/Resources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index d85809188..ecde09812 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt index 65debd946..117c0ce1e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt index 0bd30201f..756a966c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/UserListComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AlertDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AlertDialog.kt index 7242e976d..987895dec 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AlertDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AlertDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt index d2a60a0c9..267a0fe74 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBar.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt index 70bdeb985..caea2cfb2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/AppBarNavigationButton.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt index 7751e87f4..61c15f4bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/CheckboxItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt index 36ca57db0..f4a60c163 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ColoredSwitch.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Dialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Dialog.kt index bd854e071..cfed90655 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Dialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Dialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/DropdownMenu.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/DropdownMenu.kt index 699c58650..48201f84c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/DropdownMenu.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/DropdownMenu.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt index a1a762f35..b9a26099c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ErrorPlaceholder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GifTag.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GifTag.kt index bb24dee70..d54fcc9c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GifTag.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GifTag.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt index b9194a5aa..6e4dfd306 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/GridLayout.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt index 350bb29ea..b0e951cef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/HorizontalDivider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt index 66395da5a..9b61bc8d0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/InAppNotificationScaffold.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt index b87e714c6..715928219 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LoadingProgress.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt index 322f1bdac..664515c72 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/MostCenterVideoPlayerLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/MostCenterVideoPlayerLayout.kt index bde602aee..eea8bfbd7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/MostCenterVideoPlayerLayout.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/MostCenterVideoPlayerLayout.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt index 5cd2d79f1..8be4b93f7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NestedScrollScaffold.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt index 1af7ffbed..aa8cec020 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/NetworkImage.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt index 963655475..67d628770 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt index 4e0cfa3bb..936747728 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/ParallaxLayout.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt index e9cb233e9..9968ea10c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInButton.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt index 65e141474..2f02f1529 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SignInScaffold.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt index 9be413a5e..b51f82ed9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt index fbb96829e..370f41fc1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TabsComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt index b54b9e850..d41d40b33 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/TextInput.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt index 4d3ef2351..53dc145f5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerState.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerState.kt index 8f538170a..724ed8814 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerState.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/VideoPlayerState.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PagerIndicator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PagerIndicator.kt index a605ed8b1..6d95a24ce 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PagerIndicator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PagerIndicator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt index 0098abe8b..ca7055aaf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt index 53b5ed097..b0a6e4b1a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.kt index e75b9b7c0..61e69c219 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.kt index dab515cda..3d41840fd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/reorderableColumn.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/reorderableColumn.kt index 02a2f7f6c..e7136dccc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/reorderableColumn.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/reorderableColumn.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt index bb2e2c701..a49aa7736 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageBlur.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt index 2f7ad76b2..4582450dd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/image/ImageEffects.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt index cc2bd3d6c..7f39697b6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyGrid.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt index b6e66131e..1de1e8be3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/LazyListController.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt index 80610fe0f..cee5ea347 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemDivider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt index 4c1d7de5e..dfa2af908 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemHeader.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt index 4eaf082b9..2b5f2174a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsGridIndexed.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt index 31d88f6c2..67a00cc48 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/itemsPaging.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt index 75a064364..48e795388 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMConversationList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 8e8127bfe..77b4717eb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt index 57dbdd748..d2b1fa892 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiGifList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt index 617e81daf..5290064cd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt index 5f4391536..034c0f3a0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiListsList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt index b8d45a41c..78c8863d8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusImageList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt index 0a0edcbe8..1ce83749d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt index 616dab65f..c5ecaebac 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiUserList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt index 8ac0e5fa6..fe8982618 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/MastodonListsModifyComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt index b8168d3e4..aaadbb44a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lists/TwitterListsModifyComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt index 2c1a45d32..bb518942d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/media/MediaInsertMenu.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt index ba2f3c825..ecf914e48 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/navigation/Navigator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt index 19cec1a01..ed97ef461 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiImagePlaceholder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt index 2e03d73a1..6c69d6c00 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiStatusPlaceholder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt index 0f24f940d..bb27cc32b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/placeholder/UiUserPlaceholder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt index 9f6025cc4..e8a40a74b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/requireAuthorization.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt index c8c6f36f6..57ba22234 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SettingRadioItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt index d78ab5add..9e80ce6f1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/settings/SwitchItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt index b9525441a..b0ef5d3e2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/DetailedStatusComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt index 2007263c7..32ed9dca1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt index 419ca2961..fa938abe1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/LinkPreview.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt index cf0e8a64d..971dd754a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/MastodonPoll.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt index 0addb21cf..b41659518 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/RoundAvatar.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt index f49ac93ee..dd12ed893 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 0a826bb10..bcafd7332 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt index 4b22d0cc9..1f56fee24 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusDivider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt index 34f2b1196..a6a642aea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusLineComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 32682d23b..5fcffc68f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index e164c9c45..810ae8ab5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt index c98cc6db2..0b24db9d1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusThread.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 5c2a241c3..6512f160a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt index 18bf2bac7..aea98037a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TweetHeader.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt index 51265f0f8..9cfae0747 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserAvatar.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt index 58ddfc8b7..a210eef66 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/UserScreenName.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt index caf86cd18..05fc6f4b4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/MastodonTrendItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt index 8d668a22a..6d5647973 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/trend/TwitterTrendItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt index 2a1003535..ca073fc91 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/compose/ResLocal.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index 878567572..b46cc17d6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt index 07bead2d5..9eec2027b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/DataMapper.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index 4d4b21e1d..5b7215ffa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index bcb5bf8d1..5ce2be742 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt index bab79a3d2..f59b4ae10 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/AppDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt index 4e633219c..aef7fafa2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/CacheDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt index cbd37ad62..7ee9c72ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/Database.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt index bb78ccf9f..e86d5b863 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageConversationDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt index 57eef6ca1..30cf44ab2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DirectMessageEventDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt index c9c151ade..64ef00a79 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/DraftDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt index ef45e0371..58df85629 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/ListsDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt index 5897937f7..76e013f32 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/MediaDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt index 188f920cd..263ee872d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/NotificationCursorDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt index 105b826c0..1a2b8f662 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/PagingTimelineDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt index 568839239..982c5816a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/SearchDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt index 5187217c3..9513f8c20 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/StatusDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt index cc34817d9..f1009681e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/TrendDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt index 0608eacf4..2de14fb68 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/dao/UserDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt index 8bd8fbee6..d12a121ba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightAppDatabaseImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightCacheDatabaseImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightCacheDatabaseImpl.kt index c0b461881..72c57c670 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightCacheDatabaseImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/SqlDelightCacheDatabaseImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/AccountAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/AccountAdapterFactory.kt index c0b89146e..585f68b23 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/AccountAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/AccountAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt index d61f1160f..8535f318b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ColumnAdapters.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt index 980ad4a2a..682626ab2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMConversationAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt index 6a422769e..0f5efb58e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DMEventAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt index 4dbc042b4..8f8450077 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/DraftAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt index c471d6e88..91730a41c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/ListAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt index 7983adf37..351fdbb2e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MediaAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt index 5dcbcc963..8a0254a67 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/NotificationCursorAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/PagingTimelineAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/PagingTimelineAdapterFactory.kt index 3d52310d7..3bf9c95d0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/PagingTimelineAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/PagingTimelineAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt index 6e77ea1fe..470ef83ae 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/SearchAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusAdapterFactory.kt index 56a5302bc..ce60de594 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusReactionsFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusReactionsFactory.kt index 17234605f..d4cc91363 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusReactionsFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StatusReactionsFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt index 4aac1b040..d420af92f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/TrendAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt index 4bc789db1..d638ec1d1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UrlEntityAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt index 8c8ffe466..61a989576 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/adapter/UserAdapterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt index 8afd2fae4..51345269a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageConversationDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt index b309c03fd..cc8470f22 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDirectMessageEventDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt index bd32d792b..bf2b5e6c4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightDraftDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt index aabd294eb..1261a164e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightListsDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt index cc5e92bd7..cf9cd517c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightMediaDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt index 88427fc0f..51cb808bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightNotificationCursorDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightPagingTimelineDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightPagingTimelineDaoImpl.kt index 919cefbe2..cb12f688f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightPagingTimelineDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightPagingTimelineDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt index 72b31592b..31ee3579a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightSearchDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightStatusDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightStatusDaoImpl.kt index 361cc0c1b..f441fdbeb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightStatusDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightStatusDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt index 03526b87f..00e49a1d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightTrendDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt index b56dd6f17..a9ea11571 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/dao/SqlDelightUserDaoImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt index 0330e2fc9..163272ab1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/database.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt index c1b199a3c..bd5e3f0e1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMConversationWithEvent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt index 7871c5c2e..453baab1d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbDMEventWithAttachments.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbPagingTimelineWithStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbPagingTimelineWithStatus.kt index a8e8690fa..8bc8d26d3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbPagingTimelineWithStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbPagingTimelineWithStatus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt index edb0f8e8c..7475bd37c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusElements.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusWithAttachments.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusWithAttachments.kt index 2ec314d2e..278be9011 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusWithAttachments.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbStatusWithAttachments.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt index 6d07c8a39..f63aebc55 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/model/DbTrendWithHistory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt index 511e669ac..c3a2c9cb4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/OffsetQueryPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt index 9383dd85b..857b503d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/paging/QueryPagingSourceKt.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt index 231069e6b..1e880ada6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/query/QueryExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt index 90a5089a1..0c3b28abe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMConversationTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt index b82afae45..733dd652d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbAccountTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbAccountTransform.kt index adf4194fe..e018e83ba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbAccountTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbAccountTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbPagingTimelineTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbPagingTimelineTransform.kt index e51380f12..820afaf80 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbPagingTimelineTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbPagingTimelineTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbStatusTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbStatusTransform.kt index e99de12cb..835c661f2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbStatusTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DbStatusTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt index 172b42fa3..d0fc48e47 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt index f865e0c0d..108c727db 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt index 8c2c468bc..d90a79d94 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt index ae103a9c3..a422d3c44 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt index 93b8f072a..ceb5d0eb1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt index dc0a1fed6..050cd0d53 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt index d95c1cbaa..99355f7a5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt index e1bfe909e..0588668c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt index ef4480359..1776eac1c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/Setup.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt index a2f982eb9..2fa1e23a5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ComposeExt.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt index 0b2b49a69..a08455853 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt index e89f3343b..2d7fe6c01 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ActionModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt index 60bcfb2b6..451a66183 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/DataBaseModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt index 5e64c2317..37ec84cf2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/JobsModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt index 1cbdaeb24..5463b3243 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt index bba6d1f70..e7988ac02 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt index c084df059..72087aae8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/PreferencesModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt index 6decfe047..2a088e744 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/StorageProviderModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/StorageProviderModule.kt index f312067cc..4d051ec50 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/StorageProviderModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/StorageProviderModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt index 9dac52438..ff79c7a36 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt index ffb61df81..a00cceb89 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/ColorExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt index df461988d..c85cb5c4a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/CoroutineScopeExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt index abe1d5cdd..c69242ac4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/FlowExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt index b362934ba..291539ead 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/KoinExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt index d588893d8..152b63cc7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/LazyPagingItemsExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt index a33aa8141..df8c039c1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/MastodonExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt index 20017c614..b10e07c44 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/NumberExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt index 4acad6808..b48ab2cef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TextFieldValueExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt index a56ac71fa..afbcfbbad 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/TimestampExtension.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/VideoPlaybackExtensions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/VideoPlaybackExtensions.kt index ab18bfd37..327d087cb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/VideoPlaybackExtensions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/extensions/VideoPlaybackExtensions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt index 368034859..ab11825c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt index 0c3b8e68d..5b54a2a04 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt index 4eebacbcc..4dfb74d7d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/DownloadMediaJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt index 8eb720941..3a02e0a0c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/NotificationJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt index 96af576a9..7da420779 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 6c6f8ac3a..e003c88db 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index 92b6dc7e2..87f39df8e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 4f30f5057..744072bb9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt index 66a1cce13..de8e85623 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/database/DeleteDbStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt index b477f3590..a7f1ae2c1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageDeleteJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt index 48fba1167..7d589c01b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageFetchJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt index d47107026..e0bd57479 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/DirectMessageSendJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt index df0042576..9d1675848 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/dm/TwitterDirectMessageSendJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt index b2f675cd6..a8c6dc402 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/RemoveDraftJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt index 6af5fd044..5efc22021 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/draft/SaveDraftJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt index a0563ffb0..2fa99861f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/DeleteStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt index 8168aea28..a85775229 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/LikeStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt index 9834440ac..3fd366eea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/MastodonVoteJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt index 0b0f8cdb9..35213c794 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/RetweetStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt index 2af297e33..07f992f7b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/StatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt index a51c00a2d..a7021cb6b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnRetweetStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt index f4f142e7c..5e4107f72 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UnlikeStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt index db9dad7e9..0086fea38 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/status/UpdateStatusJob.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt index 2228a2338..256925ed9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 9d3817bb8..6aed04741 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 63c0df31b..662199456 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 4b2f88602..de4421f30 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index dfb59e6fc..9d5b18b85 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt index 2fbff096a..45d253403 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt index cdad67865..6c5d8bdf4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt index 344153c42..0ed8b8d58 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/Platform.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/Platform.kt index 60e360959..61a755c01 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/Platform.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/Platform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt index f40607a57..8fb02058b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt index 59fa1de92..385e4a442 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index e0ccd48a2..a89aa5740 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f2cea261c..0825c8c9f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt index 1ac157117..2b2705376 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProviderExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProviderExt.kt index c2575a4fc..8bf60485e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProviderExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/StorageProviderExt.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt index b50ffbc87..7aa078dd1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt index d1877aa89..8465126f3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountDetails.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt index 4a28e41f6..f516fe159 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AccountPreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt index 2849f6718..1f4f908fc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/AmUser.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt index ca4d185c8..d6bd0af9b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeMenus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt index 312cb65fc..ff8c1d2e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/HomeNavigationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt index 3640aa0d2..ff2452b15 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/MicroBlogKey.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt index ba635a28c..f73faa45c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/TwidereAccount.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt index 0e24c8162..83311da7d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/BasicCredentials.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt index dbc0fabc3..7f9b48348 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/Credentials.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt index 513db7d22..2b9f46224 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/CredentialsType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt index ef8cdc90f..88dad5d1a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/EmptyCredentials.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt index 8ffc64ed1..1d1903a92 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuth2Credentials.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt index 5427c3d06..531394133 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/cred/OAuthCredentials.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt index b48b6e543..c7762fc30 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ComposeType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt index 5199dfb82..90c009fd1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ListType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt index 64f934d73..c10df2715 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/Mastodon.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt index 55783ac9f..26d334509 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaInsertType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt index b22e97fd3..56660d6ee 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/MediaType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt index f18cc8a1c..8be5b9b1e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/NotificationCursorType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt index fedc795ba..e5cd31b11 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/PlatformType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt index 8e10b05d3..92f820355 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/ReferenceType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt index 08dc3bc03..57e6b46cb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/enums/TwitterReplySettings.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt index 020ce8c4a..521db9933 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/ComposeData.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt index 1a7c22a6e..0be0071d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageDeleteData.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt index a2f36d5bf..b9150c0c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/DirectMessageSendData.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt index f27c2f7e5..179210019 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/job/StatusResult.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt index 5ff3be18f..29f2fb79b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/kmp/Location.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt index 2771c77f9..8c2fc12a5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/NotificationCursor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt index ecc8b92b6..d8af840bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/paging/PagingTimeLine.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt index 271444244..5a4a26362 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiCard.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt index 14574680a..610c93a6b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMConversation.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt index 640abbc4a..270f176ca 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDMEvent.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt index 07273e605..7f9c973a2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiDraft.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt index 7c7461d34..71a430696 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiEmojiCategory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt index d336f2a8e..e36e90420 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGeo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGif.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGif.kt index 831841b98..580e48364 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGif.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiGif.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt index 9d523c4a6..db72fc4e8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt index 8e8ebb40f..84f02bfea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMedia.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt index 45c160396..1489cad0b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiMediaInsert.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt index 883245053..dc699ab77 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiPoll.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt index 8af0db8b9..c03c9f09c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiSearch.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt index 371697c6f..aee06a373 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt index d15bf3b4e..c667a7f90 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiTrend.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt index 46d0f7abc..80e7ec093 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUrlEntity.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt index 13749df5c..bd4e2b939 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiUser.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt index 20abdd44a..a43eed3c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonStatusExtra.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt index 550bab953..0f6af088f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/mastodon/MastodonUserExtra.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt index e4dc2114a..945e8d368 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterStatusExtra.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt index 3b61231eb..21e0e9996 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/twitter/TwitterUserExtra.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt index 864614ba5..2f28b6594 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Root.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt index 40163b265..d9a778feb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/RootDeepLinks.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt index 0bd79c9c4..4780a96d6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Route.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt index 237bfa5a5..ac2cd9472 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 928e4fc69..4f2cfca97 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt index 10de6480c..e94b3bd78 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/InAppNotification.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt index 8387efeeb..1807ce64e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/notification/NotificationChannelSpec.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt index 65174673d..2eac5b7d2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/IPagingList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt index e7331f07c..2161aec66 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt index 713e15e62..49125b64b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt index a0c3daf1a..6df8ac7f4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt index bcb9acd46..191828cd2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/BaseDirectMessageMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt index ada0c6a9d..f08d88167 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMConversationMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt index 6e8cb7180..bdb0f15b8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/dm/DMEventMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt index e81dd4b4d..7edec42a6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt index 2887d5528..5c5fd90fa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsMembersMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt index 76b4bc4ba..b5f8b6545 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt index d2f886418..b9466fbe9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/list/ListsUserPagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt index 2d529b1d5..654f4e43a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorPagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt index 65db08574..4eeef9276 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/CursorWithCustomOrderPagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt index aaf5837fa..e62a8469f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/MaxIdPagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt index d5ea6244d..079a6683b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt index e5ca1a52f..098e14f10 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingTimelineMediatorBase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index f245f6200..0f9e9bb04 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt index 369bb7448..158fa4ae3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchMediaMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt index 31b5716c4..ec94b809b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/search/SearchStatusMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt index 76ca079e8..737470d7f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/MastodonStatusContextMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt index 1fdde753c..9423806ea 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/status/TwitterConversationMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt index 9d65da848..1de69af7e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/HomeTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt index 525e93564..e77ead51e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MastodonHashtagTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt index 396a228d5..1612a1528 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/MentionTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt index 348b045ea..855776c57 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/NotificationTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt index 0580cd3d8..eba612852 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/FederatedTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt index 6fc1144f3..8c6aff7ef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/timeline/mastodon/LocalTimelineMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt index d3d7a569a..b5f7b9b43 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/trend/TrendMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt index f07ede8d1..05947f5f3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserFavouriteMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt index c95b9bc8e..f990ae2d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserMediaMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt index f4c19448a..86e34a9c8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/user/UserStatusMediator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt index 4f106d8f6..87d3ed25d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt index 6e2b5fac2..7072a5c37 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt index c20901a49..9e59310d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt index 56477d3df..8b1caafcf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/MastodonSearchHashtagPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt index 6b01b48fd..f91dd04d3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt index 18121672e..8c7923790 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/UserPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt index 57c7ac637..608b9c791 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt index dc6e28d9b..e07a53b26 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifSearchPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt index 6569b2bfd..2b0b51f3d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/source/gif/GifTrendingPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt index 8d4b6fc78..2801b1ade 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/PreferencesHolder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt index 5325e31ca..86eabf3a6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt index b83a7352c..9d20e5b26 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/AppearancePreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt index 766513a47..70120d2a2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt index f1c0881d9..af6d52246 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt index bdcc7ded2..9c4608f0f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/NotificationPreferences.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt index 8d1224768..1d773205a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/AppearancePreferencesSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt index 9b6056198..eafa6c12f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/DisplayPreferencesSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt index b388e4aec..88bdd8688 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/MiscPreferencesSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt index cc94b6654..25dfb10a6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/serializer/NotificationPreferencesSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index ea2ac404f..bd5091efa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt index 9286ff9f6..003405194 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/CacheRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt index f78e46dc4..33f7fb1d0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt index 450466257..a219c9d28 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DraftRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/GifRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/GifRepository.kt index 1115e2dc3..5f7611b1b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/GifRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/GifRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt index 6dd02f1e8..28d390956 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt index f6e5f593a..60ef5e569 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ListsUsersRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt index 637a01740..3c61678c7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/MediaRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt index 9f6fc15f2..a21043e7b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NotificationRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt index 1d4462fdf..e3874dcc1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/ReactionRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt index 79a647a39..1c6455d17 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/SearchRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt index e7751e406..2c4833932 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/StatusRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt index 91d879c5a..61815e6ee 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TimelineRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt index 83edd8a5e..c4c62cab4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/TrendRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt index d71bab108..7981e0e9e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserListRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt index 75b1e19bd..b8a0b89b1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/UserRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt index ae0504ad7..1324392cb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/DraftListScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt index 09faa83f7..900ce0449 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/HomeScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 2bdd2229c..fabe89739 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt index 7a3411711..854417cac 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt index 4bc3a36dc..b37a76088 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/PureMediaScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt index a867b2c36..138c71a8e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/SignInScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt index b4e874ee8..75c451a8c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/StatusScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt index d491649e1..aecc3874c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt index f6a3b4d0f..383a02b18 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchHashtagScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt index fefc8e5f6..5399cc5df 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/compose/ComposeSearchUserScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt index 455ac7dd9..5f466f060 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationListScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt index d8b94c0de..7fff910ff 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMConversationScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt index e40d342b6..d08d297aa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/dm/DMNewConversationScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt index d84b79b0b..4395e7580 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/gif/GifScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt index 3e407251b..ad71d6bc9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/AllNotificationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt index d3dd0b8a8..4fe50075d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DMConversationListItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt index ab7869e33..0ed43b01e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/DraftNavigationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt index f9f1c30e0..8d2825aa8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeMenu.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt index 054e29c37..ef141efd9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/HomeTimelineItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt index 29607169a..d4103ad63 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/ListsNavigationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt index 8415031fb..90b553dab 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MeItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt index b1d546673..88a942698 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/MentionItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt index 82dda4de7..5bee22f54 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/NotificationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index f959f3972..0ba5d0722 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt index 083e7d199..28e51b5e0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/FederatedTimelineItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt index 3d61ea0a2..1b5ce55f7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/LocalTimelineItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt index 42292d636..32179929c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/mastodon/MastodonNotificationItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt index af7b5901f..a3dae228f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsAddMembersScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt index af9fbaa1c..1659b93ac 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsMembersScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt index d2a71b947..b20de8631 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt index 29eb7f725..973988516 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsSubscribersScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt index caf2af0f3..c96620b5d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/ListsTimelineScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt index cf9b0e654..6efc921ee 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsCreateDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt index 6afe5dba5..a4fdaf1c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/MastodonListsEditDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt index 742065d1c..7e178a0e9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsCreateScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt index 7d346cd70..53b098055 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/lists/platform/TwitterListsEditScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt index ed4a47168..c54988042 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonHashtagScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt index 0fe2bf385..6bdb7b211 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/mastodon/MastodonSignInScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index ea1acf4e9..6538396f9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt index 26eab7508..ee938afa7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt index c8eaecaef..101a41af3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/MastodonSearchHashtagItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt index c6509f9ee..952cad63e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchSceneItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt index 63ab9d759..61e938942 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchTweetsItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt index f25ce7938..91ee6520b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/SearchUserItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt index 9e7029ae6..9ae919238 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/tabs/TwitterSearchMediaItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt index cf43ec7c9..18c6811c0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AboutScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt index bfad18cc5..d9ef85ad8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountManagementScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt index 4ff6c12ae..a2049d297 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt index 37e6baa6d..7112ae1b8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/AppearanceScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index 416031b58..a064c9070 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt index 8430a4f3a..cbbffc515 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/LayoutScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 665204fef..be9c8a273 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt index c0e5583dd..9245a42f3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/NotificationScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt index af891f64c..d8581389f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/SettingsScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 94058f516..98a11e6ae 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt index f26ecdf30..3cf627a2f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/TwitterSigninScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt index 29e46bfe9..1b47dec4e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/twitter/user/TwitterUserScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt index 7ddad4ea0..96e12c523 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowersScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt index b26183c9d..a69230a3e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/FollowingScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt index f05fa681d..da41b2c6e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/user/UserScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt index bf234f182..c00915774 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Ambient.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt index e267f1507..7374808d3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Color.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt index 8de6d9493..941ba4e28 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Shape.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt index d8b403ccc..758cd5eef 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt index ada23a7b0..8758ae003 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt index ade39e12a..106b4e5cf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Event.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt index 6fe7095d9..2bb412b7c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/Json.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt index 576ffac76..393f93bf8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/MastodonEmojiCache.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/OAuthLauncher.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/OAuthLauncher.kt index a9ea7208c..fd70c9f7a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/OAuthLauncher.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/OAuthLauncher.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt index 67b99472e..35e957b64 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/PlatformResolver.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwidereFilePicker.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwidereFilePicker.kt index c5d3a627b..e36e4e545 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwidereFilePicker.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwidereFilePicker.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt index cd284f616..c5f29fbfe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/TwitterErrorHandling.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt index 652848f98..3da8e4a93 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/CustomVideoControl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt index 3d970c2d1..59657e132 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/video/VideoPool.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt index a13576cc5..ba8cd8ba8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt index fdd163ef1..9592b3479 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/DraftViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 433e6c42a..8944e950d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt index 6b56d8f05..30a36d896 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt index ee5351113..e6c15a5b7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/StatusViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt index 54e540465..61c6ef303 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeSearchUserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 891600f8e..62e683b69 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt index 77ef547a3..03cacc7f3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/MastodonComposeSearchHashtagViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt index 1f4eb6261..e48d9b17b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMConversationViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt index 6c4152db9..5b3a1babf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMEventViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt index b2056102b..f8a99cf8d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/dm/DMNewConversationViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt index f46d9a8ac..db3c8da7f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/gif/GifViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt index 09b03c274..1cf00d5fb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsAddMemberViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt index e42093cf5..6199fbe5c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsSearchUserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt index 947368693..840db864c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt index c0aee439e..6a29e135b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsUserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt index d408275db..a5687684d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt index 4aaf9d9ee..39d93ac69 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonHashtagViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt index 48c820497..e40c4c1d5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSearchHashtagViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt index b6ced0b63..f249cfece 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/mastodon/MastodonSignInViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 3c0406fe0..bbe72c1d6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt index a4c5fe253..74fe538bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchSaveViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt index f4c967992..c02b6dfca 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchTweetsViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt index 58b6246e0..bee4ab224 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchUserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt index 195f6d468..6c8dfcc98 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AccountNotificationViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt index fea1261f9..63207c3e2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/AppearanceViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index a96174724..8112c7913 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt index 2920dc053..773dffc4f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/LayoutViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index e7874c984..05782e566 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt index 6a9a65092..ca83a689e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/NotificationViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt index 4a5fb7aee..1b7579712 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/StorageViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt index 2a7b53ff8..b9ed1df15 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/HomeTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt index 28e53384f..149f6f5fc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/MentionsTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt index 98b910570..4a30a2c3e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/NotificationTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 80219d950..f8a0ee0da 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index a0fbc5738..e0eb57c86 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt index 92e1d6113..4fcf6e986 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/LocalTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt index af8ca8ef0..f84cc1e06 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/trend/TrendViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index d2fe903fd..5f1dc94b1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt index 9ee8d9c87..233d37c65 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/search/TwitterSearchMediaViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt index 69af0c801..139b6ba95 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/twitter/user/TwitterUserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt index ccf7cb6e7..fbbacc8c2 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt index 7bb5918c3..03d7c4166 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt index 905036117..7810c9a95 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt index e407d2d1c..1be3e4ffe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserListViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt index 9d4652e95..4ed7bac35 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt index 4641b72af..eaf3feb0a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt index 5b061a859..1d50d915a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt index 843702170..a48317352 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt index b504d5a22..fd429e57a 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/nestedscrollview/NestedScrollViewState.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt b/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt index ef8b4a669..3193fc275 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/placeholder/PlaceHolder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt index 2c42f9698..5aec001c2 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/Lifecycle.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt index e2d6194a0..6ca741f23 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleObserver.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt index 022739896..dc672875c 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleOwner.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt index 1bc4fe527..45b0d1461 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/LifecycleRegistry.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt index 85b8b1a52..782d6fe3b 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/lifecycle/RepeatOnLifecycle.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackHandler.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackHandler.kt index f6c0dac3b..db625ee57 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackHandler.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackHandler.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt index 02e53687c..4c64b9061 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackEntry.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt index e038444de..a281047e9 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavControllerViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt index 1ffcc3136..a2b0c7496 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt index 744e4cff7..f8206da50 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavOptions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt index e659a7970..6562e8270 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt index c1670c400..a71c3f39d 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/PopUpTo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt index 4c4e26aa8..8d8a734d0 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/QueryString.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt index 80bb3cc4b..94e620f72 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt index 75e9091ea..334900b7c 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteGraph.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt index 6fd4722dd..bc3346f1e 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatch.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt index 34f34b520..a73ee76db 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteMatchResult.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt index 9cbaa5038..41927d94f 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteParser.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt index 27d79a163..ea7d49248 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt index ca8128630..33749835b 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStackManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt index 85e6362c7..c12c142be 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt index 5179da4b8..3544e4441 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/DialogRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt index 5b4fc1f59..08497c545 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt index fcab24908..41e6abb3c 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt index 46b6b3771..a8ea0f621 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedDialogRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt index e28e8b211..e499d700a 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/AnimatedRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt index ec5a425ee..d4fbbd9a8 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/DialogTransition.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt index 9610fb28b..f51b43286 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/transition/NavTransition.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt index c84bd7e2c..3de6c55ce 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/BackPressAdapter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt index a1288262c..751085807 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ComposeCompositionLocal.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt index ab4ecfe1c..4ce1253bc 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/ui/ViewModelAdapter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt index ad9f8d396..85f2fc7fd 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/CloseableCoroutineScope.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt index 8b974dd70..873934ca6 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt index 4ed54cc21..5fdb8a995 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt index c1b2df8b9..a8dbab48a 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStore.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt index 4ad9843b6..b4426439a 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreOwner.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt index 7276e3126..add924ebd 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/viewmodel/compose/ViewModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt b/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt index 95f617ae2..a3d49dc47 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/swiper/Swiper.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt b/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt index a775b4f88..642c80fcd 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/swiper/SwiperState.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt index eeaa83805..7796643f3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/MainThreadTestBase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt index 6c89038a8..4175a38f1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseAppDatabaseTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt index bd960def5..2731a61c6 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/BaseCacheDatabaseTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index 590ffd2d8..e55ba795a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt index 7c2a82273..637ea35cf 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/SqlDelightAppDatabaseImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt index aabfe205d..60c2515a0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageConversationDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt index 314049d66..992d55201 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDirectMessageEventDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt index 9e57ac442..e0b0b739d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightDraftDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt index de1fb1340..71af89395 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightListsDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt index 77456ddf7..915ea8c83 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightMediaDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt index 78216259c..bf39f6e6b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightNotificationCursorImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightPagingTimelineDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightPagingTimelineDaoImplTest.kt index 9ac5d53ef..cd76f121f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightPagingTimelineDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightPagingTimelineDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt index b615353ef..9ae0315b1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightSearchDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightStatusDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightStatusDaoImplTest.kt index 76d9de7cd..5719de5e8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightStatusDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightStatusDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt index 49aefe894..c6bc48908 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightTrendDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt index 00ea3ac9a..1697fa303 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/dao/SqlDelightUserDaoImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/AccountQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/AccountQueriesImplTest.kt index f5d6ad55d..9246b1c79 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/AccountQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/AccountQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt index 385978c94..3e0f86f7b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMConversationQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt index caa9bb270..fb3de163e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DMEventQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt index 49332a22e..2b17d86e2 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/DraftQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt index cb8138c76..81ce01fc0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/ListQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt index b5c78ed28..27dbda9b9 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/MediaQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt index dc7a54f28..eb571f954 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/NotificationCursorQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/PagingTimelineQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/PagingTimelineQueriesImplTest.kt index 9e413ae34..04bab6825 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/PagingTimelineQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/PagingTimelineQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt index e348ffc37..37b095803 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/SearchQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/StatusQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/StatusQueriesImplTest.kt index 85ada2f77..732a000b9 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/StatusQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/StatusQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt index 2889e1ff2..6dc3a28cc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendHistoryQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt index ca512faed..6c2e8f694 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/TrendQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt index eb0784286..d1d4ed86c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UrlEntityQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt index d1dd3eb1f..06ef7cf99 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/UserQueriesImplTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt index 4a65a4ff7..d6bd0560f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/EnumColumnAdapterTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt index 6e8d02a57..d0e686de6 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/JsonColumnAdapterTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt index f02476653..8b0ea5e90 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/MicroBlogKeyColumnAdapterTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt index d6a513f4e..df63e7e53 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/adapter/StringListColumnAdapterTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt index d48fed279..818b6b4aa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DMEventTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt index bdd67e66e..56674210f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/DraftTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt index 792792a7a..544746765 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/ListTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt index 9f1de7f05..d576f5759 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/MediaTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt index 72a40c81e..ed6033c94 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/NotificationCursorTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/PagingTimelineTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/PagingTimelineTransformTest.kt index 9089ee915..13490d204 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/PagingTimelineTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/PagingTimelineTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt index 7b1a6ac5b..9ee09745d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/SearchTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/StatusTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/StatusTransformTest.kt index f0cbe4f34..ffffa735a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/StatusTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/StatusTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt index 9b55fa7c1..50a6b087f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/TrendTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt index 1734636f1..d16d05630 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UrlEntityTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt index 1eff53106..b346dcd4e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/db/sqldelight/transform/UserTransformTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt index 91198c36d..415c5a37f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/Observer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt index 208853539..2e635800c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockAppDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt index 847eb1e99..a4a327686 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/MockCacheDatabase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt index 2b4d7a731..925c98efa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageConversationDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt index 4f656cede..a38b362dd 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDirectMessageEventDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt index ad0df1492..5e7baea44 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockDraftDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt index de4110af5..d4a0c2655 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockListsDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt index 9da1fa707..bd9bcd1bc 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockMediaDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt index 6cd4d90c1..496544899 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockNotificationCursorDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt index 7a3554408..33e21eee3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockPagingTimelineDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt index 7fd359939..7b68de905 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockSearchDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt index ea3ad97e0..21b932042 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockStatusDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt index 9cb2e93f2..48b0eaab1 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockTrendDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt index 34481153d..9043d414c 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/db/dao/MockUserDao.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index fb0d2f6d2..39978c88e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt index 6968f5df9..b6cca1011 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/paging/MockPagingSource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt index 394093584..65a6c6d1f 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/ErrorService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt index 046d411a8..c916ab492 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockDirectMessageService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt index bb8ec405a..ab19c724a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockListsService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt index 21d9e451a..3c15a85a7 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockLookUpService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt index 24969d018..77b85a01d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockNotificationService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt index 8cba2bc2d..6cf02d488 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockRelationshipService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt index b1784ba8e..6169a0bce 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockSearchService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt index 4a5b0f7c6..c787828b3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTimelineService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt index 4c46b66e2..5c01faf21 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/service/MockTrendService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt index 8f937721d..48519fb0b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt index 5ca075c28..e6fff9f19 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index 2794a4fff..630a19c8d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt index d3531a8aa..d852fa640 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMConversationMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt index deb07df50..30c0bacaf 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/dm/DMEventMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt index 70d19c604..d36872b41 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListMembersMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt index f1d39fe08..a7587a7f5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt index 0f0511f3c..9ee06448b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/lists/ListsUserPagingMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt index 93cd2ac11..721c2baaa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingTimelineMediatorBaseTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt index 5e4b8ffac..ca4bb0bd3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/paging/PagingWithGapMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt index 0d3509f7e..788f74f24 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchMediaMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt index 048b35877..b7fa9b362 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/search/SearchStatusMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt index 64d55466c..c4413bc44 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowersPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt index b256fe894..587f53bf0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/FollowingPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt index 15bd798d1..eb522e2fa 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/ListsSubscribersPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt index e74a9c41c..dfd70e661 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/SearchUserPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt index b52f33007..17d9b471e 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/source/UserPagingSourceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt index 616664a32..b411db0f8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/HomeTimelineMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt index fbb2fe97a..3fa768cee 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/MentionTimelineMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt index 41d50d31b..bcb4369d6 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/NotificationTimelineMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt index 811ec5420..8b76385ee 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserFavoriteMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt index 3b6bf97eb..5cb5a304b 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserMediaMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt index d00e21634..40fc12008 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/timeline/UserStatusMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt index 4ef37b990..41b57445a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/trend/TrendMediatorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt index 742dc2ee3..72ba12784 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DirectMessageRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt index 715281be1..6d34032c0 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/DraftRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt index 7d2a50376..e35dd76ff 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt index 44a0bf7a9..faee99599 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ListsUsersRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt index d5eaf4a65..fbe93c57a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/MediaRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt index 7969c1c9f..5ed0f87bf 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/NotificationRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt index f9b417f30..2d168bf98 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/ReactionRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt index c52d262b1..f3b100357 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/SearchRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt index 3c75f0639..17d852d87 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/repository/StatusRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt index 1791e638d..a41e793d5 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/AccountViewModelTestBase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt index 1068d9f55..8093284d3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ActiveAccountViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt index aa48d7855..ac4052f3d 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/DraftViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt index 4e35b8c52..ef60b8a19 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/MediaViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt index f3fcdea17..4dd3b692a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/PureMediaViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt index 60f76f0c3..cc8388ff3 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/StatusViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt index 382c39ab0..95618bd73 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/ViewModelTestBase.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt index a90986955..cec78a159 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsCreateViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt index 5a42263c5..09e05ae99 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsModifyViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt index b1ef1836c..58be11f65 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/lists/ListsViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt index b19c6acf2..2305a4ab4 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowersViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt index 3681909f6..5d4c307f4 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/FollowingViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt index ad95ca981..6c752bba8 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserFavouriteTimelineViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt index aa0d8628f..938cc4741 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserMediaTimelineViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt index 4ee4bcfa8..2426f4068 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserTimelineViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt index a28202035..227e1f57a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/viewmodel/user/UserViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt index 0b228c35c..8cd25a35c 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/LifecycleTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt index 5f51bb9aa..2fa8acddf 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/lifecycle/TestLifecycleOwner.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt index a4b608e3a..c6e7924b4 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/QueryStringTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt index 8b192987c..994dbb8e0 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteBuilderTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt index 1874f90aa..97c19dcca 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt index dd0972650..8d359327e 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/RouteParserTest2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt index 9a3c1d172..b2abd4989 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt index e08683b38..e3fb7e9f9 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelStoreTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt index d643b5732..5523cebf0 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/viewmodel/ViewModelTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt b/common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt index 895fa67e6..665b7d8c3 100644 --- a/common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt +++ b/common/src/desktopMain/kotlin/androidx.paging.compose/PagingPlaceholderKey.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 1ffd0adb9..27cdbca85 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt index 9f3c319b7..9579e7998 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/ComposeAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt index 012214283..1da526da6 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DirectMessageAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt index 56d8e001b..c43c6e7d0 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/DraftAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index fe998b44b..ea9a317dd 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt index 987d2cf5c..d64ccc12e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/StatusActions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 932793c64..fb9bbd5ad 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopMediaPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopMediaPlayer.kt index 3e5d67467..781568380 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopMediaPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/DesktopMediaPlayer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt index 127b040e6..55c061d28 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/LocalView.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/VLCJMediaPlayer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/VLCJMediaPlayer.kt index 0dffa4d88..2c025ac17 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/VLCJMediaPlayer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/VLCJMediaPlayer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt index 6a6f1296e..20880a827 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformAlertDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt index 9d192ef1e..1341c7255 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDialog.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.desktop.kt index 46a6ef7fd..2ee98dc84 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformDropdownMenu.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.desktop.kt index 995b7704e..394d96a1d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformPlayerView.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt index 4528bb071..37f7b8f1f 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/dataprovider/DataProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt index a4ef1741d..2c3ba04da 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/ActionsModule.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt index 4c33b7431..c4e5878d4 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/KmpModule.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt index d36d84306..9cf1b8391 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/di/modules/PlatformModule.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt index 41f3071c9..53602c0c0 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/GifPainter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageCache.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageCache.kt index 6a4092299..183bdd749 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageCache.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageCache.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt index d763dc08f..21045fb8c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImageEffectsFilter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index e125713fe..d00c51083 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/init/Initializer.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/init/Initializer.kt index d2f370cac..add2d2807 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/init/Initializer.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/init/Initializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/init/TwidereServiceFactoryInitialTask.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/init/TwidereServiceFactoryInitialTask.kt index 60d44ddbe..f71a96563 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/init/TwidereServiceFactoryInitialTask.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/init/TwidereServiceFactoryInitialTask.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt index 390f41f7b..9a2ec5dfb 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/CookieManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 38bfe2e94..3b4d79a2c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index ea976544d..506e8c7ab 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 45154d578..2834b7834 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt index 8090bbc38..a28212418 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/LocationProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt index e88316118..31da5eff3 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/MediaInsertProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt index f903d370e..f47ff49df 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OrientationSensorManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt index 6976cf953..3ea3a8d23 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/Platform.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/Platform.kt index 6f2b111ae..b4bd02e7e 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/Platform.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/Platform.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt index 18b0877f9..399a9704f 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformMediaWrapper.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt index 35ea7ba21..3c907e53a 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/PlatformWindow.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index ac55f91ab..c8ad8cf0d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index f24a62a39..7a121a191 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt index dd5a25f7e..00cccf04c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/StorageProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt index c2e7a02c8..fe00a513b 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/TimeUtils.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt index dda19052f..f5c646b36 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/model/AccountPreferencesFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/navigation/Route.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/navigation/Route.desktop.kt index b2ceebabf..846ba7956 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/navigation/Route.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/navigation/Route.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt index 6310502d6..00cb10881 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/notification/AppNotificationManager.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt index 38ddb1bb4..aac2a5e01 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/repository/AccountRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt index a6cf5fa38..c170e2d80 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/PlatformMediaScene.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.desktop.kt index afcb1bfb2..86b07b573 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/SystemInfo.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/SystemInfo.kt index efeb2c1ad..91b59e613 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/SystemInfo.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/SystemInfo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsDatastoreModifier.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsDatastoreModifier.kt index e4b519339..47c4d05df 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsDatastoreModifier.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsDatastoreModifier.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt index 323e674fc..f9c546b87 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/utils/WindowsRegistry.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt index 5ef5ec80a..6952c3c35 100644 --- a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt +++ b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt index 885ed98fd..d129cf5a4 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/base/SqlDriverFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/desktopTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt b/common/src/desktopTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt index 3987afe97..9e63f266b 100644 --- a/common/src/desktopTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt +++ b/common/src/desktopTest/kotlin/com/twidere/twiderex/repository/CacheRepositoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index 9fa486d19..c615ddd20 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/DesktopMediaPlayerFactoryImpl.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/DesktopMediaPlayerFactoryImpl.kt index edba7924c..03bb54829 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/DesktopMediaPlayerFactoryImpl.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/DesktopMediaPlayerFactoryImpl.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/JFXMediaPlayer.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/JFXMediaPlayer.kt index 6b63f3f5d..18a84cae6 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/JFXMediaPlayer.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/media/JFXMediaPlayer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/routeProcessor/src/main/kotlin/AppRoute.kt b/routeProcessor/src/main/kotlin/AppRoute.kt index 6f3adf195..6d48f2e32 100644 --- a/routeProcessor/src/main/kotlin/AppRoute.kt +++ b/routeProcessor/src/main/kotlin/AppRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/routeProcessor/src/main/kotlin/RouteDefinition.kt b/routeProcessor/src/main/kotlin/RouteDefinition.kt index 421ad2c6f..a1483ef41 100644 --- a/routeProcessor/src/main/kotlin/RouteDefinition.kt +++ b/routeProcessor/src/main/kotlin/RouteDefinition.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/routeProcessor/src/main/kotlin/RouteProcessor.kt b/routeProcessor/src/main/kotlin/RouteProcessor.kt index e22afe441..79a2b1ee9 100644 --- a/routeProcessor/src/main/kotlin/RouteProcessor.kt +++ b/routeProcessor/src/main/kotlin/RouteProcessor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt b/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt index 59b73a984..fc6cf3da5 100644 --- a/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt +++ b/routeProcessor/src/main/kotlin/RouteProcessorProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/gif/GifService.kt b/services/src/main/java/com/twidere/services/gif/GifService.kt index e828f6621..5518b7154 100644 --- a/services/src/main/java/com/twidere/services/gif/GifService.kt +++ b/services/src/main/java/com/twidere/services/gif/GifService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt index 19676accd..72423c6f2 100644 --- a/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyPagingResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt index 641e45a58..7b033449b 100644 --- a/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyResource.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt index 0713c371a..c78a8dc72 100644 --- a/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt +++ b/services/src/main/java/com/twidere/services/gif/giphy/GiphyService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/gif/model/IGif.kt b/services/src/main/java/com/twidere/services/gif/model/IGif.kt index 689f12705..7a9e932af 100644 --- a/services/src/main/java/com/twidere/services/gif/model/IGif.kt +++ b/services/src/main/java/com/twidere/services/gif/model/IGif.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/AuthorizationInterceptor.kt b/services/src/main/java/com/twidere/services/http/AuthorizationInterceptor.kt index 2dda25998..5684dee25 100644 --- a/services/src/main/java/com/twidere/services/http/AuthorizationInterceptor.kt +++ b/services/src/main/java/com/twidere/services/http/AuthorizationInterceptor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/Errors.kt b/services/src/main/java/com/twidere/services/http/Errors.kt index 34cbb2dfa..4aa6744b7 100644 --- a/services/src/main/java/com/twidere/services/http/Errors.kt +++ b/services/src/main/java/com/twidere/services/http/Errors.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/HttpClientFactory.kt b/services/src/main/java/com/twidere/services/http/HttpClientFactory.kt index f8e29b592..6bf248a34 100644 --- a/services/src/main/java/com/twidere/services/http/HttpClientFactory.kt +++ b/services/src/main/java/com/twidere/services/http/HttpClientFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/HttpConfigProvider.kt b/services/src/main/java/com/twidere/services/http/HttpConfigProvider.kt index 5708c2d61..ddf97e8fb 100644 --- a/services/src/main/java/com/twidere/services/http/HttpConfigProvider.kt +++ b/services/src/main/java/com/twidere/services/http/HttpConfigProvider.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/MicroBlogException.kt b/services/src/main/java/com/twidere/services/http/MicroBlogException.kt index d86cf4ead..63228819d 100644 --- a/services/src/main/java/com/twidere/services/http/MicroBlogException.kt +++ b/services/src/main/java/com/twidere/services/http/MicroBlogException.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/MicroBlogHttpException.kt b/services/src/main/java/com/twidere/services/http/MicroBlogHttpException.kt index 0b0903ee9..49489c807 100644 --- a/services/src/main/java/com/twidere/services/http/MicroBlogHttpException.kt +++ b/services/src/main/java/com/twidere/services/http/MicroBlogHttpException.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/authorization/Authorization.kt b/services/src/main/java/com/twidere/services/http/authorization/Authorization.kt index 25baadc33..2bd08119e 100644 --- a/services/src/main/java/com/twidere/services/http/authorization/Authorization.kt +++ b/services/src/main/java/com/twidere/services/http/authorization/Authorization.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/authorization/BearerAuthorization.kt b/services/src/main/java/com/twidere/services/http/authorization/BearerAuthorization.kt index e79f3e560..39f05fe9b 100644 --- a/services/src/main/java/com/twidere/services/http/authorization/BearerAuthorization.kt +++ b/services/src/main/java/com/twidere/services/http/authorization/BearerAuthorization.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/authorization/EmptyAuthorization.kt b/services/src/main/java/com/twidere/services/http/authorization/EmptyAuthorization.kt index 47c0b552b..1e6208ce3 100644 --- a/services/src/main/java/com/twidere/services/http/authorization/EmptyAuthorization.kt +++ b/services/src/main/java/com/twidere/services/http/authorization/EmptyAuthorization.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/authorization/OAuth1Authorization.kt b/services/src/main/java/com/twidere/services/http/authorization/OAuth1Authorization.kt index c487f59c7..8d1ecde64 100644 --- a/services/src/main/java/com/twidere/services/http/authorization/OAuth1Authorization.kt +++ b/services/src/main/java/com/twidere/services/http/authorization/OAuth1Authorization.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/config/HttpConfig.kt b/services/src/main/java/com/twidere/services/http/config/HttpConfig.kt index 52105dc47..525323d5c 100644 --- a/services/src/main/java/com/twidere/services/http/config/HttpConfig.kt +++ b/services/src/main/java/com/twidere/services/http/config/HttpConfig.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt index 55a5e16ad..c0a75369d 100644 --- a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt +++ b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt index 7313c7c47..f05842b31 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonOAuthService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt index 7dc56deb3..9fe2294ea 100644 --- a/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt +++ b/services/src/main/java/com/twidere/services/mastodon/MastodonService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/AccountResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/AccountResources.kt index 5b52e84d2..083b28566 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/AccountResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/AccountResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt index 59a7c09d6..3e3eb15d9 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/FriendshipResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/ListsResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/ListsResources.kt index 9d0ed2dda..2c7c685e1 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/ListsResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/ListsResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/LookupResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/LookupResources.kt index 69562f921..9e75b04d8 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/LookupResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/LookupResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/MastodonOAuthResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/MastodonOAuthResources.kt index 771c7c64a..0b0a1213b 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/MastodonOAuthResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/MastodonOAuthResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/MastodonResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/MastodonResources.kt index 58246c89a..6b9f4e9ee 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/MastodonResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/MastodonResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/SearchResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/SearchResources.kt index 93a2fbb97..863466a56 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/SearchResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/SearchResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/StatusResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/StatusResources.kt index 6d67c600f..67b0c4652 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/StatusResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/StatusResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/TimelineResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/TimelineResources.kt index a18b95149..a0f5c79fa 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/TimelineResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/TimelineResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/api/TrendsResources.kt b/services/src/main/java/com/twidere/services/mastodon/api/TrendsResources.kt index f13e2e33d..d53f1ccfa 100644 --- a/services/src/main/java/com/twidere/services/mastodon/api/TrendsResources.kt +++ b/services/src/main/java/com/twidere/services/mastodon/api/TrendsResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Account.kt b/services/src/main/java/com/twidere/services/mastodon/model/Account.kt index 6ea44beb9..859870555 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Account.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Account.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Application.kt b/services/src/main/java/com/twidere/services/mastodon/model/Application.kt index 2aeb21cae..336ca4ebf 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Application.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Application.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Attachment.kt b/services/src/main/java/com/twidere/services/mastodon/model/Attachment.kt index 352d8ebc5..fb83602b5 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Attachment.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Attachment.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Card.kt b/services/src/main/java/com/twidere/services/mastodon/model/Card.kt index 28cd9beb6..133c798e1 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Card.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Card.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Context.kt b/services/src/main/java/com/twidere/services/mastodon/model/Context.kt index eb8681dd6..753089b01 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Context.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Context.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/CreateApplicationResponse.kt b/services/src/main/java/com/twidere/services/mastodon/model/CreateApplicationResponse.kt index 496cecf09..e1cf22591 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/CreateApplicationResponse.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/CreateApplicationResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Emoji.kt b/services/src/main/java/com/twidere/services/mastodon/model/Emoji.kt index 8a9c35e54..468f8e657 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Emoji.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Emoji.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Field.kt b/services/src/main/java/com/twidere/services/mastodon/model/Field.kt index ad822f82e..aacafc993 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Field.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Field.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Hashtag.kt b/services/src/main/java/com/twidere/services/mastodon/model/Hashtag.kt index 3b6282506..e247543da 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Hashtag.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Hashtag.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/History.kt b/services/src/main/java/com/twidere/services/mastodon/model/History.kt index 23278ebef..d3f299bcb 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/History.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/History.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/MastodonAuthScope.kt b/services/src/main/java/com/twidere/services/mastodon/model/MastodonAuthScope.kt index 47d3da876..8f22011de 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/MastodonAuthScope.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/MastodonAuthScope.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/MastodonList.kt b/services/src/main/java/com/twidere/services/mastodon/model/MastodonList.kt index ee08008e5..7f5d22e13 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/MastodonList.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/MastodonList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/MastodonPaging.kt b/services/src/main/java/com/twidere/services/mastodon/model/MastodonPaging.kt index 9091988f5..82a2ebf77 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/MastodonPaging.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/MastodonPaging.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/MediaType.kt b/services/src/main/java/com/twidere/services/mastodon/model/MediaType.kt index 707d2b9b4..16b0171f4 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/MediaType.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/MediaType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Mention.kt b/services/src/main/java/com/twidere/services/mastodon/model/Mention.kt index 2414b47c5..b0bad54bc 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Mention.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Mention.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Meta.kt b/services/src/main/java/com/twidere/services/mastodon/model/Meta.kt index c00910176..85356c7b8 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Meta.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Meta.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt b/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt index 60f86bdd2..400dee31e 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Option.kt b/services/src/main/java/com/twidere/services/mastodon/model/Option.kt index e1762766d..01c6001d3 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Option.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Option.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Original.kt b/services/src/main/java/com/twidere/services/mastodon/model/Original.kt index 587bb897d..c9866a603 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Original.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Original.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt b/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt index f9c1df4c3..569bb1a2d 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/PostAccounts.kt b/services/src/main/java/com/twidere/services/mastodon/model/PostAccounts.kt index f917334c7..ac88b8010 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/PostAccounts.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/PostAccounts.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/PostList.kt b/services/src/main/java/com/twidere/services/mastodon/model/PostList.kt index b6223387b..d72fa533d 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/PostList.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/PostList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/PostPoll.kt b/services/src/main/java/com/twidere/services/mastodon/model/PostPoll.kt index d6356cafe..e187ba2be 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/PostPoll.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/PostPoll.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/PostStatus.kt b/services/src/main/java/com/twidere/services/mastodon/model/PostStatus.kt index 35a756783..42d670973 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/PostStatus.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/PostStatus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/PostVote.kt b/services/src/main/java/com/twidere/services/mastodon/model/PostVote.kt index 620a24329..131e0bc19 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/PostVote.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/PostVote.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/RelationshipResponse.kt b/services/src/main/java/com/twidere/services/mastodon/model/RelationshipResponse.kt index 73c00851f..e17d7d8af 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/RelationshipResponse.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/RelationshipResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/RequestTokenResponse.kt b/services/src/main/java/com/twidere/services/mastodon/model/RequestTokenResponse.kt index 4b9b031ba..674bef13b 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/RequestTokenResponse.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/RequestTokenResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/SearchResult.kt b/services/src/main/java/com/twidere/services/mastodon/model/SearchResult.kt index 6140ccb99..3d051ff19 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/SearchResult.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/SearchResult.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/SearchType.kt b/services/src/main/java/com/twidere/services/mastodon/model/SearchType.kt index c4e9e3cee..2f88703aa 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/SearchType.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/SearchType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Small.kt b/services/src/main/java/com/twidere/services/mastodon/model/Small.kt index 85c29165f..a75998562 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Small.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Small.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Source.kt b/services/src/main/java/com/twidere/services/mastodon/model/Source.kt index c2f292cf6..020e3ba6d 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Source.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Source.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Status.kt b/services/src/main/java/com/twidere/services/mastodon/model/Status.kt index 6d7c58629..0edc135d9 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Status.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Status.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Tag.kt b/services/src/main/java/com/twidere/services/mastodon/model/Tag.kt index b293f8f91..d84215a09 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Tag.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Tag.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Trend.kt b/services/src/main/java/com/twidere/services/mastodon/model/Trend.kt index 66cc7bd17..654327672 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Trend.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Trend.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/UploadResponse.kt b/services/src/main/java/com/twidere/services/mastodon/model/UploadResponse.kt index 16fd7e45d..095fea8f0 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/UploadResponse.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/UploadResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/VerifyCredentialsResponse.kt b/services/src/main/java/com/twidere/services/mastodon/model/VerifyCredentialsResponse.kt index dfa06f63e..538d8e9a3 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/VerifyCredentialsResponse.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/VerifyCredentialsResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Visibility.kt b/services/src/main/java/com/twidere/services/mastodon/model/Visibility.kt index b2e46fead..1b6754389 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Visibility.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Visibility.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/mastodon/model/exceptions/MastodonException.kt b/services/src/main/java/com/twidere/services/mastodon/model/exceptions/MastodonException.kt index b6d1f6631..83c56f59b 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/exceptions/MastodonException.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/exceptions/MastodonException.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/DirectMessageService.kt b/services/src/main/java/com/twidere/services/microblog/DirectMessageService.kt index aad8c3652..30c37859e 100644 --- a/services/src/main/java/com/twidere/services/microblog/DirectMessageService.kt +++ b/services/src/main/java/com/twidere/services/microblog/DirectMessageService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/DownloadMediaService.kt b/services/src/main/java/com/twidere/services/microblog/DownloadMediaService.kt index 47a22c064..6e2fbf77b 100644 --- a/services/src/main/java/com/twidere/services/microblog/DownloadMediaService.kt +++ b/services/src/main/java/com/twidere/services/microblog/DownloadMediaService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/ListsService.kt b/services/src/main/java/com/twidere/services/microblog/ListsService.kt index 3713d4be1..58cb84611 100644 --- a/services/src/main/java/com/twidere/services/microblog/ListsService.kt +++ b/services/src/main/java/com/twidere/services/microblog/ListsService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/LookupService.kt b/services/src/main/java/com/twidere/services/microblog/LookupService.kt index df1f9f06e..85388e43a 100644 --- a/services/src/main/java/com/twidere/services/microblog/LookupService.kt +++ b/services/src/main/java/com/twidere/services/microblog/LookupService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/MicroBlogService.kt b/services/src/main/java/com/twidere/services/microblog/MicroBlogService.kt index 76fabc8ff..7848e7afd 100644 --- a/services/src/main/java/com/twidere/services/microblog/MicroBlogService.kt +++ b/services/src/main/java/com/twidere/services/microblog/MicroBlogService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/NotificationService.kt b/services/src/main/java/com/twidere/services/microblog/NotificationService.kt index ecad8001c..84ddb3724 100644 --- a/services/src/main/java/com/twidere/services/microblog/NotificationService.kt +++ b/services/src/main/java/com/twidere/services/microblog/NotificationService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt index b3a2e5500..2921504c4 100644 --- a/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt +++ b/services/src/main/java/com/twidere/services/microblog/RelationshipService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/SearchService.kt b/services/src/main/java/com/twidere/services/microblog/SearchService.kt index a63c68ef7..3b9081225 100644 --- a/services/src/main/java/com/twidere/services/microblog/SearchService.kt +++ b/services/src/main/java/com/twidere/services/microblog/SearchService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/StatusService.kt b/services/src/main/java/com/twidere/services/microblog/StatusService.kt index ee755fe33..8abd2441f 100644 --- a/services/src/main/java/com/twidere/services/microblog/StatusService.kt +++ b/services/src/main/java/com/twidere/services/microblog/StatusService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/TimelineService.kt b/services/src/main/java/com/twidere/services/microblog/TimelineService.kt index 14f175df8..d2cace738 100644 --- a/services/src/main/java/com/twidere/services/microblog/TimelineService.kt +++ b/services/src/main/java/com/twidere/services/microblog/TimelineService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/TrendService.kt b/services/src/main/java/com/twidere/services/microblog/TrendService.kt index 595135a14..77bb00965 100644 --- a/services/src/main/java/com/twidere/services/microblog/TrendService.kt +++ b/services/src/main/java/com/twidere/services/microblog/TrendService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IDirectMessage.kt b/services/src/main/java/com/twidere/services/microblog/model/IDirectMessage.kt index 235b72011..07bdad235 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IDirectMessage.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IDirectMessage.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IListModel.kt b/services/src/main/java/com/twidere/services/microblog/model/IListModel.kt index b684bf255..f38cd288f 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IListModel.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IListModel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/INotification.kt b/services/src/main/java/com/twidere/services/microblog/model/INotification.kt index a844166e1..93068e3eb 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/INotification.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/INotification.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IPaging.kt b/services/src/main/java/com/twidere/services/microblog/model/IPaging.kt index e764a2871..855552f82 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IPaging.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IPaging.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt b/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt index fc3bb1e70..dd1df4ce2 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IRelationship.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/ISearchResponse.kt b/services/src/main/java/com/twidere/services/microblog/model/ISearchResponse.kt index 27dec5ac7..0f938efc9 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/ISearchResponse.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/ISearchResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IStatus.kt b/services/src/main/java/com/twidere/services/microblog/model/IStatus.kt index 67c316687..e4abfef13 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IStatus.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IStatus.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/ITrend.kt b/services/src/main/java/com/twidere/services/microblog/model/ITrend.kt index 3b1290293..6119e367e 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/ITrend.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/ITrend.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/microblog/model/IUser.kt b/services/src/main/java/com/twidere/services/microblog/model/IUser.kt index 222d33b87..f5a3cd04e 100644 --- a/services/src/main/java/com/twidere/services/microblog/model/IUser.kt +++ b/services/src/main/java/com/twidere/services/microblog/model/IUser.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/NitterService.kt b/services/src/main/java/com/twidere/services/nitter/NitterService.kt index ccb9e9419..c320d2112 100644 --- a/services/src/main/java/com/twidere/services/nitter/NitterService.kt +++ b/services/src/main/java/com/twidere/services/nitter/NitterService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/TweetNotFoundException.kt b/services/src/main/java/com/twidere/services/nitter/TweetNotFoundException.kt index f39ece9be..3abe1e52e 100644 --- a/services/src/main/java/com/twidere/services/nitter/TweetNotFoundException.kt +++ b/services/src/main/java/com/twidere/services/nitter/TweetNotFoundException.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/Attachments.kt b/services/src/main/java/com/twidere/services/nitter/model/Attachments.kt index 9ba2efbb5..c390dd088 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Attachments.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Attachments.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/ConversationThreadItem.kt b/services/src/main/java/com/twidere/services/nitter/model/ConversationThreadItem.kt index ea1beb4d2..8f4fc8dcf 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/ConversationThreadItem.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/ConversationThreadItem.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/ConversationTimeline.kt b/services/src/main/java/com/twidere/services/nitter/model/ConversationTimeline.kt index 3bbbca7f2..4d6abff1e 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/ConversationTimeline.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/ConversationTimeline.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/Status.kt b/services/src/main/java/com/twidere/services/nitter/model/Status.kt index 1655aa1a0..83ba19fbe 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Status.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Status.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/StatusCard.kt b/services/src/main/java/com/twidere/services/nitter/model/StatusCard.kt index f76be6f1d..d645fa9e6 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/StatusCard.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/StatusCard.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/StatusStats.kt b/services/src/main/java/com/twidere/services/nitter/model/StatusStats.kt index 2d491133c..f734d205b 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/StatusStats.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/StatusStats.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/TweetNotFound.kt b/services/src/main/java/com/twidere/services/nitter/model/TweetNotFound.kt index 99c0fd1d1..f21b2076b 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/TweetNotFound.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/TweetNotFound.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/User.kt b/services/src/main/java/com/twidere/services/nitter/model/User.kt index f6afee259..387d8c27e 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/User.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/User.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/UserTimeline.kt b/services/src/main/java/com/twidere/services/nitter/model/UserTimeline.kt index 533a6268a..8bce7ea43 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/UserTimeline.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/UserTimeline.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/serializer/DateSerializer.kt b/services/src/main/java/com/twidere/services/nitter/model/serializer/DateSerializer.kt index 975022fe4..6d7a08a1c 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/serializer/DateSerializer.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/serializer/DateSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatsSerializer.kt b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatsSerializer.kt index 8e81b0118..ffc0b66bf 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatsSerializer.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatsSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusBodySerializer.kt b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusBodySerializer.kt index d484fd0aa..e79aebb1c 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusBodySerializer.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusBodySerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusIdSerializer.kt b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusIdSerializer.kt index c2c6bba43..d3bcf9279 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusIdSerializer.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/serializer/StatusIdSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/serializer/UrlDecodeSerializer.kt b/services/src/main/java/com/twidere/services/nitter/model/serializer/UrlDecodeSerializer.kt index 3dda144fd..ea723af54 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/serializer/UrlDecodeSerializer.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/serializer/UrlDecodeSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt b/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt index 4b22b1db6..f4730f40f 100644 --- a/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt +++ b/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/proxy/ReverseProxyInterceptor.kt b/services/src/main/java/com/twidere/services/proxy/ReverseProxyInterceptor.kt index bb74e9f6a..709b2bdba 100644 --- a/services/src/main/java/com/twidere/services/proxy/ReverseProxyInterceptor.kt +++ b/services/src/main/java/com/twidere/services/proxy/ReverseProxyInterceptor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt b/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt index ee9d82ae0..3cb6ac254 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt index 9ebc7614f..986b3594c 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt index 58672a989..3bbdb2fc0 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt index 177ab461f..50a118c5a 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt b/services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt index 9664f8c34..67d01ad1e 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterErrorCodes.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterOAuthService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterOAuthService.kt index 39221e357..396c4a898 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterOAuthService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterOAuthService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 6d2e7d868..fd27132c9 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/DirectMessagesResources.kt b/services/src/main/java/com/twidere/services/twitter/api/DirectMessagesResources.kt index 21c6eafad..9841ffa40 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/DirectMessagesResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/DirectMessagesResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/FollowsResources.kt b/services/src/main/java/com/twidere/services/twitter/api/FollowsResources.kt index c7cc76669..99b9f268e 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/FollowsResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/FollowsResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/FriendshipResources.kt b/services/src/main/java/com/twidere/services/twitter/api/FriendshipResources.kt index 42739ddbf..2180506de 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/FriendshipResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/FriendshipResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/ListsResources.kt b/services/src/main/java/com/twidere/services/twitter/api/ListsResources.kt index a2d9f3560..bb7b5401d 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/ListsResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/ListsResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/LookupResources.kt b/services/src/main/java/com/twidere/services/twitter/api/LookupResources.kt index fd33eb284..6764ea7cf 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/LookupResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/LookupResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/SearchResources.kt b/services/src/main/java/com/twidere/services/twitter/api/SearchResources.kt index c0953b68e..c39d250d2 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/SearchResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/SearchResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt b/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt index 0a1629d02..c144b0bd2 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt b/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt index 08508bea4..326ffc6af 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/TrendsResources.kt b/services/src/main/java/com/twidere/services/twitter/api/TrendsResources.kt index e071eb8d7..b83196906 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/TrendsResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/TrendsResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/TwitterOAuthResources.kt b/services/src/main/java/com/twidere/services/twitter/api/TwitterOAuthResources.kt index 7c6c4f4a2..db0a20f45 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/TwitterOAuthResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/TwitterOAuthResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/TwitterResources.kt b/services/src/main/java/com/twidere/services/twitter/api/TwitterResources.kt index 36c1b1eb1..cca068e20 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/TwitterResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/TwitterResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/UploadResources.kt b/services/src/main/java/com/twidere/services/twitter/api/UploadResources.kt index 22c5cb9d4..fbd116480 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/UploadResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/UploadResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt index eb69ac197..b9ff1aae3 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/UsersResources.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/AccessToken.kt b/services/src/main/java/com/twidere/services/twitter/model/AccessToken.kt index 112b946b7..a4d591e23 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/AccessToken.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/AccessToken.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/AnnotationV2.kt b/services/src/main/java/com/twidere/services/twitter/model/AnnotationV2.kt index edfe89deb..54f7ac352 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/AnnotationV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/AnnotationV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/AttachmentsV2.kt b/services/src/main/java/com/twidere/services/twitter/model/AttachmentsV2.kt index 4398f51e4..f600d9b95 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/AttachmentsV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/AttachmentsV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt b/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt index c49681889..b1a639a03 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/BlockV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Contributor.kt b/services/src/main/java/com/twidere/services/twitter/model/Contributor.kt index 0d3184882..f141483e8 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Contributor.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Contributor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Coordinates.kt b/services/src/main/java/com/twidere/services/twitter/model/Coordinates.kt index 8a662d089..e977f3f11 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Coordinates.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Coordinates.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Description.kt b/services/src/main/java/com/twidere/services/twitter/model/Description.kt index 908b2c8ed..dadbc5ffa 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Description.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Description.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/DescriptionURL.kt b/services/src/main/java/com/twidere/services/twitter/model/DescriptionURL.kt index 6891ce42f..60ffd046d 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/DescriptionURL.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/DescriptionURL.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/DirectMessageResponse.kt b/services/src/main/java/com/twidere/services/twitter/model/DirectMessageResponse.kt index b37947e66..2feeb1cae 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/DirectMessageResponse.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/DirectMessageResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Entities.kt b/services/src/main/java/com/twidere/services/twitter/model/Entities.kt index 09a4a94dc..6812ec5dc 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Entities.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Entities.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/EntitiesURL.kt b/services/src/main/java/com/twidere/services/twitter/model/EntitiesURL.kt index b290eead4..a67bccdad 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/EntitiesURL.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/EntitiesURL.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/EntitiesURLV2.kt b/services/src/main/java/com/twidere/services/twitter/model/EntitiesURLV2.kt index 0a4a46973..0289aa384 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/EntitiesURLV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/EntitiesURLV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/EntitiesV2.kt b/services/src/main/java/com/twidere/services/twitter/model/EntitiesV2.kt index 5207b8654..f6adb9764 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/EntitiesV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/EntitiesV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/FluffyURL.kt b/services/src/main/java/com/twidere/services/twitter/model/FluffyURL.kt index e64174ab8..90c1832a8 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/FluffyURL.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/FluffyURL.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/GeoPoint.kt b/services/src/main/java/com/twidere/services/twitter/model/GeoPoint.kt index b93cbfa80..d65e2aa63 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/GeoPoint.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/GeoPoint.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/HashtagV2.kt b/services/src/main/java/com/twidere/services/twitter/model/HashtagV2.kt index 10207a552..6301d15a8 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/HashtagV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/HashtagV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Hashtags.kt b/services/src/main/java/com/twidere/services/twitter/model/Hashtags.kt index 2475b10f0..786dfe082 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Hashtags.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Hashtags.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ImageV2.kt b/services/src/main/java/com/twidere/services/twitter/model/ImageV2.kt index 07dc93e17..75c9a5b82 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ImageV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ImageV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/IncludesV2.kt b/services/src/main/java/com/twidere/services/twitter/model/IncludesV2.kt index db79555bc..84888b091 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/IncludesV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/IncludesV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ListUserResponse.kt b/services/src/main/java/com/twidere/services/twitter/model/ListUserResponse.kt index 1aa51bd18..d42935ede 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ListUserResponse.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ListUserResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/MediaPublicMetrics.kt b/services/src/main/java/com/twidere/services/twitter/model/MediaPublicMetrics.kt index c83bdf063..aee1e4976 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/MediaPublicMetrics.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/MediaPublicMetrics.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/MediaSize.kt b/services/src/main/java/com/twidere/services/twitter/model/MediaSize.kt index 310d33055..1a37ddf6c 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/MediaSize.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/MediaSize.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/MediaV2.kt b/services/src/main/java/com/twidere/services/twitter/model/MediaV2.kt index 5c35be866..2f510d4b0 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/MediaV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/MediaV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/MentionV2.kt b/services/src/main/java/com/twidere/services/twitter/model/MentionV2.kt index 06111c799..fedf0b7d8 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/MentionV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/MentionV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Meta.kt b/services/src/main/java/com/twidere/services/twitter/model/Meta.kt index 27b40bd60..320b69112 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Meta.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Meta.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/OAuthToken.kt b/services/src/main/java/com/twidere/services/twitter/model/OAuthToken.kt index 0e52ae455..78871bac8 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/OAuthToken.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/OAuthToken.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Place.kt b/services/src/main/java/com/twidere/services/twitter/model/Place.kt index a387909f0..4c244981c 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Place.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Place.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PlaceGeo.kt b/services/src/main/java/com/twidere/services/twitter/model/PlaceGeo.kt index 85a29173d..fa87231d5 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PlaceGeo.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PlaceGeo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PlaceV2.kt b/services/src/main/java/com/twidere/services/twitter/model/PlaceV2.kt index 79bfed707..861be511b 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PlaceV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PlaceV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt b/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt index 702151b68..846b9d2ef 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PollV2Option.kt b/services/src/main/java/com/twidere/services/twitter/model/PollV2Option.kt index 1bc785933..adda9ebfb 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PollV2Option.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PollV2Option.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ProfileBanner.kt b/services/src/main/java/com/twidere/services/twitter/model/ProfileBanner.kt index 13714499d..3321302ab 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ProfileBanner.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ProfileBanner.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ProfileBannerSize.kt b/services/src/main/java/com/twidere/services/twitter/model/ProfileBannerSize.kt index 8e2f82ad3..c5a20647f 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ProfileBannerSize.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ProfileBannerSize.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PublicMetricsV2.kt b/services/src/main/java/com/twidere/services/twitter/model/PublicMetricsV2.kt index e4329b42e..2cf7ebfd5 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PublicMetricsV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PublicMetricsV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PurpleMedia.kt b/services/src/main/java/com/twidere/services/twitter/model/PurpleMedia.kt index d55b16b61..2bb68f213 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PurpleMedia.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PurpleMedia.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/PurpleURLV2.kt b/services/src/main/java/com/twidere/services/twitter/model/PurpleURLV2.kt index d71889e96..ce2e0f782 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PurpleURLV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PurpleURLV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/QuotedStatusPermalink.kt b/services/src/main/java/com/twidere/services/twitter/model/QuotedStatusPermalink.kt index c3f9c5ffe..e87b3837e 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/QuotedStatusPermalink.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/QuotedStatusPermalink.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetType.kt b/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetType.kt index dfd565af7..856d2fc72 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetType.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetType.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetV2.kt b/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetV2.kt index fd036d9b0..e9220fdaa 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/ReferencedTweetV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Relationship.kt b/services/src/main/java/com/twidere/services/twitter/model/Relationship.kt index 00fea6519..0801ff4d9 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Relationship.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Relationship.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/RelationshipResponse.kt b/services/src/main/java/com/twidere/services/twitter/model/RelationshipResponse.kt index 8ae5d5ddd..13cb7aef4 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/RelationshipResponse.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/RelationshipResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/SearchMetadataV1.kt b/services/src/main/java/com/twidere/services/twitter/model/SearchMetadataV1.kt index c9b75fd28..01a5efa89 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/SearchMetadataV1.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/SearchMetadataV1.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Sizes.kt b/services/src/main/java/com/twidere/services/twitter/model/Sizes.kt index e2b60cf55..dffe90bd2 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Sizes.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Sizes.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Source.kt b/services/src/main/java/com/twidere/services/twitter/model/Source.kt index 5df9712d5..923017e8a 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Source.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Source.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Status.kt b/services/src/main/java/com/twidere/services/twitter/model/Status.kt index c2c4a19dc..e421f6871 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Status.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Status.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusEntities.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusEntities.kt index b2f4e9b92..ef5b8f84a 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusEntities.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusEntities.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusExtendedEntities.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusExtendedEntities.kt index 576125b98..752d152d5 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusExtendedEntities.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusExtendedEntities.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt index aa586a9f8..cc72e531a 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt index 58ab552f0..ce244b4bc 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusV2Entities.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusV2Entities.kt index 46e1e733b..1e3a0a89d 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusV2Entities.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusV2Entities.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusV2Geo.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusV2Geo.kt index b1648bd31..05f46cdf1 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusV2Geo.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusV2Geo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusV2PublicMetrics.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusV2PublicMetrics.kt index 21730b6b9..807573631 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusV2PublicMetrics.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusV2PublicMetrics.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Symbol.kt b/services/src/main/java/com/twidere/services/twitter/model/Symbol.kt index c52c4233b..4815307c9 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Symbol.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Symbol.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Target.kt b/services/src/main/java/com/twidere/services/twitter/model/Target.kt index a02d4b0d7..63cd74ebd 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Target.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Target.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterErrorV2.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterErrorV2.kt index f7e90074a..627c7eba5 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterErrorV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterErrorV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt index 2643233b8..bfc31ad30 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterPaging.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterPaging.kt index 345e30ca7..9e6294ceb 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterPaging.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterPaging.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterQueryList.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterQueryList.kt index 1d669327e..2dea76dc5 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterQueryList.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterQueryList.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterResponseV2.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterResponseV2.kt index b90b520b8..a5e64c9b7 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterResponseV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterResponseV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV1.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV1.kt index ed239051c..208e694f7 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV1.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV1.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV2.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV2.kt index d357b50ea..0d0f9da44 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterSearchResponseV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterTrendsResponseV1.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterTrendsResponseV1.kt index 0894c7e59..8678604de 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterTrendsResponseV1.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterTrendsResponseV1.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterUploadResponse.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterUploadResponse.kt index 638baa1fc..b2a9312d0 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterUploadResponse.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterUploadResponse.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/URL.kt b/services/src/main/java/com/twidere/services/twitter/model/URL.kt index c9c66393c..d2726f7a9 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/URL.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/URL.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/URLElementV2.kt b/services/src/main/java/com/twidere/services/twitter/model/URLElementV2.kt index 3eae78a85..d96bc6084 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/URLElementV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/URLElementV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/User.kt b/services/src/main/java/com/twidere/services/twitter/model/User.kt index 482debbed..55aef4e77 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/User.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/User.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/UserEntities.kt b/services/src/main/java/com/twidere/services/twitter/model/UserEntities.kt index 18f9aeba2..55fcc482c 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/UserEntities.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/UserEntities.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/UserMention.kt b/services/src/main/java/com/twidere/services/twitter/model/UserMention.kt index eac2a70b7..bd947ab2a 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/UserMention.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/UserMention.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/UserV2.kt b/services/src/main/java/com/twidere/services/twitter/model/UserV2.kt index 448e096be..5dbb2f728 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/UserV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/UserV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/Variant.kt b/services/src/main/java/com/twidere/services/twitter/model/Variant.kt index 130220a80..57d14252d 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Variant.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Variant.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/VideoInfo.kt b/services/src/main/java/com/twidere/services/twitter/model/VideoInfo.kt index bcecb5560..51e54a8a9 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/VideoInfo.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/VideoInfo.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiException.kt b/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiException.kt index cb1221b4a..8d1c6e493 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiException.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiException.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiExceptionV2.kt b/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiExceptionV2.kt index 563f5dcf5..0d56ced05 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiExceptionV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/exceptions/TwitterApiExceptionV2.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/Expansions.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/Expansions.kt index aebaa23b5..6c71b60cb 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/Expansions.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/Expansions.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/MediaFields.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/MediaFields.kt index 3bd726611..83eb08342 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/MediaFields.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/MediaFields.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/PlaceFields.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/PlaceFields.kt index d119fe428..1734cca00 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/PlaceFields.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/PlaceFields.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/PollFields.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/PollFields.kt index 7cb4113c9..c7c99527d 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/PollFields.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/PollFields.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/TweetFields.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/TweetFields.kt index 614124956..4ba746fd6 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/TweetFields.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/TweetFields.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/fields/UserFields.kt b/services/src/main/java/com/twidere/services/twitter/model/fields/UserFields.kt index 037905c6a..a6e2b684c 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/fields/UserFields.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/fields/UserFields.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt b/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt index cd078163e..dbde89386 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/Base64.java b/services/src/main/java/com/twidere/services/utils/Base64.java index 0e898b1b1..4d70f5b44 100644 --- a/services/src/main/java/com/twidere/services/utils/Base64.java +++ b/services/src/main/java/com/twidere/services/utils/Base64.java @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/Flags.kt b/services/src/main/java/com/twidere/services/utils/Flags.kt index 14a66d52f..49df37228 100644 --- a/services/src/main/java/com/twidere/services/utils/Flags.kt +++ b/services/src/main/java/com/twidere/services/utils/Flags.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/Json.kt b/services/src/main/java/com/twidere/services/utils/Json.kt index a6e49fd59..7a026a020 100644 --- a/services/src/main/java/com/twidere/services/utils/Json.kt +++ b/services/src/main/java/com/twidere/services/utils/Json.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/OkHttp.kt b/services/src/main/java/com/twidere/services/utils/OkHttp.kt index 3c5407a2f..26e877ceb 100644 --- a/services/src/main/java/com/twidere/services/utils/OkHttp.kt +++ b/services/src/main/java/com/twidere/services/utils/OkHttp.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/QueryString.kt b/services/src/main/java/com/twidere/services/utils/QueryString.kt index 7f93369c7..13d51cc2e 100644 --- a/services/src/main/java/com/twidere/services/utils/QueryString.kt +++ b/services/src/main/java/com/twidere/services/utils/QueryString.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/utils/Stream.kt b/services/src/main/java/com/twidere/services/utils/Stream.kt index 91fccb630..46fa5a619 100644 --- a/services/src/main/java/com/twidere/services/utils/Stream.kt +++ b/services/src/main/java/com/twidere/services/utils/Stream.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/HttpConfigClientFactoryTest.kt b/services/src/test/java/com/twidere/services/HttpConfigClientFactoryTest.kt index 91b07973d..7bedf39cf 100644 --- a/services/src/test/java/com/twidere/services/HttpConfigClientFactoryTest.kt +++ b/services/src/test/java/com/twidere/services/HttpConfigClientFactoryTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/JsonTest.kt b/services/src/test/java/com/twidere/services/JsonTest.kt index 23dffc3e6..876fb60ab 100644 --- a/services/src/test/java/com/twidere/services/JsonTest.kt +++ b/services/src/test/java/com/twidere/services/JsonTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/MockApiTest.kt b/services/src/test/java/com/twidere/services/api/MockApiTest.kt index 258d694ed..8df6c44a5 100644 --- a/services/src/test/java/com/twidere/services/api/MockApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/MockApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/common/MockApiAsset.kt b/services/src/test/java/com/twidere/services/api/common/MockApiAsset.kt index 383b65519..2e58d1d21 100644 --- a/services/src/test/java/com/twidere/services/api/common/MockApiAsset.kt +++ b/services/src/test/java/com/twidere/services/api/common/MockApiAsset.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/common/MockRetrofit.kt b/services/src/test/java/com/twidere/services/api/common/MockRetrofit.kt index 0bc670006..c5d085d5c 100644 --- a/services/src/test/java/com/twidere/services/api/common/MockRetrofit.kt +++ b/services/src/test/java/com/twidere/services/api/common/MockRetrofit.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/common/MockServices.kt b/services/src/test/java/com/twidere/services/api/common/MockServices.kt index 8d05a6e97..ae20b5597 100644 --- a/services/src/test/java/com/twidere/services/api/common/MockServices.kt +++ b/services/src/test/java/com/twidere/services/api/common/MockServices.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/common/Request2AssetPathConvertor.kt b/services/src/test/java/com/twidere/services/api/common/Request2AssetPathConvertor.kt index 5d062f1b3..a9f499f9e 100644 --- a/services/src/test/java/com/twidere/services/api/common/Request2AssetPathConvertor.kt +++ b/services/src/test/java/com/twidere/services/api/common/Request2AssetPathConvertor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/mastodon/MastodonListsApiTest.kt b/services/src/test/java/com/twidere/services/api/mastodon/MastodonListsApiTest.kt index 7847e2e5b..23aa9372a 100644 --- a/services/src/test/java/com/twidere/services/api/mastodon/MastodonListsApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/mastodon/MastodonListsApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/mastodon/MastodonRequest2AssetPathConvertor.kt b/services/src/test/java/com/twidere/services/api/mastodon/MastodonRequest2AssetPathConvertor.kt index 78fd14679..1cb5c1c47 100644 --- a/services/src/test/java/com/twidere/services/api/mastodon/MastodonRequest2AssetPathConvertor.kt +++ b/services/src/test/java/com/twidere/services/api/mastodon/MastodonRequest2AssetPathConvertor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/mastodon/MastodonTrendsApiTest.kt b/services/src/test/java/com/twidere/services/api/mastodon/MastodonTrendsApiTest.kt index 5972a7032..185aaecfb 100644 --- a/services/src/test/java/com/twidere/services/api/mastodon/MastodonTrendsApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/mastodon/MastodonTrendsApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/nitter/NitterTest.kt b/services/src/test/java/com/twidere/services/api/nitter/NitterTest.kt index 321c07256..269c21584 100644 --- a/services/src/test/java/com/twidere/services/api/nitter/NitterTest.kt +++ b/services/src/test/java/com/twidere/services/api/nitter/NitterTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/proxy/ReverseProxyInterceptorTest.kt b/services/src/test/java/com/twidere/services/api/proxy/ReverseProxyInterceptorTest.kt index 918ad78bd..121981a63 100644 --- a/services/src/test/java/com/twidere/services/api/proxy/ReverseProxyInterceptorTest.kt +++ b/services/src/test/java/com/twidere/services/api/proxy/ReverseProxyInterceptorTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/twitter/TwitterDirectMessageApiTest.kt b/services/src/test/java/com/twidere/services/api/twitter/TwitterDirectMessageApiTest.kt index 381585420..8dc06e9ee 100644 --- a/services/src/test/java/com/twidere/services/api/twitter/TwitterDirectMessageApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/twitter/TwitterDirectMessageApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/twitter/TwitterListsApiTest.kt b/services/src/test/java/com/twidere/services/api/twitter/TwitterListsApiTest.kt index 1389952ce..9665310de 100644 --- a/services/src/test/java/com/twidere/services/api/twitter/TwitterListsApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/twitter/TwitterListsApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/twitter/TwitterRequest2AssetPathConvertor.kt b/services/src/test/java/com/twidere/services/api/twitter/TwitterRequest2AssetPathConvertor.kt index ee06b77b1..1c18c3238 100644 --- a/services/src/test/java/com/twidere/services/api/twitter/TwitterRequest2AssetPathConvertor.kt +++ b/services/src/test/java/com/twidere/services/api/twitter/TwitterRequest2AssetPathConvertor.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/api/twitter/TwitterTrendsApiTest.kt b/services/src/test/java/com/twidere/services/api/twitter/TwitterTrendsApiTest.kt index 0e3aa4cb4..451936ff1 100644 --- a/services/src/test/java/com/twidere/services/api/twitter/TwitterTrendsApiTest.kt +++ b/services/src/test/java/com/twidere/services/api/twitter/TwitterTrendsApiTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/service/ListServiceTest.kt b/services/src/test/java/com/twidere/services/service/ListServiceTest.kt index 1f1bfd90d..e0da5bc7f 100644 --- a/services/src/test/java/com/twidere/services/service/ListServiceTest.kt +++ b/services/src/test/java/com/twidere/services/service/ListServiceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/service/mastodon/MastodonListServiceTest.kt b/services/src/test/java/com/twidere/services/service/mastodon/MastodonListServiceTest.kt index e3dae38b3..824bd353f 100644 --- a/services/src/test/java/com/twidere/services/service/mastodon/MastodonListServiceTest.kt +++ b/services/src/test/java/com/twidere/services/service/mastodon/MastodonListServiceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/service/mastodon/MastodonTrendsServiceTest.kt b/services/src/test/java/com/twidere/services/service/mastodon/MastodonTrendsServiceTest.kt index 2cebead95..5a4dcd21c 100644 --- a/services/src/test/java/com/twidere/services/service/mastodon/MastodonTrendsServiceTest.kt +++ b/services/src/test/java/com/twidere/services/service/mastodon/MastodonTrendsServiceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/service/twitter/TwitterListServiceTest.kt b/services/src/test/java/com/twidere/services/service/twitter/TwitterListServiceTest.kt index ea4823bf0..50379ac71 100644 --- a/services/src/test/java/com/twidere/services/service/twitter/TwitterListServiceTest.kt +++ b/services/src/test/java/com/twidere/services/service/twitter/TwitterListServiceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/test/java/com/twidere/services/service/twitter/TwitterTrendsServiceTest.kt b/services/src/test/java/com/twidere/services/service/twitter/TwitterTrendsServiceTest.kt index 769d2e618..ce444ec93 100644 --- a/services/src/test/java/com/twidere/services/service/twitter/TwitterTrendsServiceTest.kt +++ b/services/src/test/java/com/twidere/services/service/twitter/TwitterTrendsServiceTest.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/spotless/license b/spotless/license index 63d8873d1..091ba5f51 100644 --- a/spotless/license +++ b/spotless/license @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * From e7472b77c266732f12981f7819ff5dedd7da4e4a Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 16 Dec 2021 11:52:13 +0800 Subject: [PATCH 500/615] remove nothing to inline --- .../com/twidere/twiderex/navigation/IRoute.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt index 421b9459a..d5ebd5986 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt @@ -37,12 +37,11 @@ fun List.mapToString() = map { } } -@Suppress("NOTHING_TO_INLINE") -inline fun RouteBuilder.scene( +fun RouteBuilder.scene( route: IRoute, deepLinks: List = emptyList(), navTransition: NavTransition? = null, - noinline content: @Composable (BackStackEntry) -> Unit, + content: @Composable (BackStackEntry) -> Unit, ) = scene( route = route.route, deepLinks = deepLinks.mapToString(), @@ -50,12 +49,11 @@ inline fun RouteBuilder.scene( content = content ) -@Suppress("NOTHING_TO_INLINE") -inline fun RouteBuilder.authorizedScene( +fun RouteBuilder.authorizedScene( route: IRoute, deepLinks: List = emptyList(), navTransition: NavTransition? = null, - noinline content: @Composable (BackStackEntry) -> Unit, + content: @Composable (BackStackEntry) -> Unit, ) = authorizedScene( route = route.route, deepLinks = deepLinks.mapToString(), @@ -63,8 +61,7 @@ inline fun RouteBuilder.authorizedScene( content = content ) -@Suppress("NOTHING_TO_INLINE") -inline fun RouteBuilder.authorizedDialog( +fun RouteBuilder.authorizedDialog( route: IRoute, - noinline content: @Composable (BackStackEntry) -> Unit, + content: @Composable (BackStackEntry) -> Unit, ) = authorizedDialog(route.route, content) From 425884a09143e662f2b8ab92ee8224f241a7ee15 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 16 Dec 2021 12:24:57 +0800 Subject: [PATCH 501/615] update ktlint --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 3be8ef604..00ed1e659 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -15,7 +15,7 @@ object Versions { const val ksp = "${Kotlin.lang}-1.0.2" const val agp = "7.0.3" const val spotless = "6.0.4" - const val ktlint = "0.42.1" + const val ktlint = "0.43.2" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" From cad7461cfc0e06d30540ac4f5f66a48617fc005f Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 16 Dec 2021 15:38:48 +0800 Subject: [PATCH 502/615] use super types qualifiedName to avoid hard code --- routeProcessor/src/main/kotlin/RouteDefinition.kt | 6 +++--- routeProcessor/src/main/kotlin/RouteProcessor.kt | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/routeProcessor/src/main/kotlin/RouteDefinition.kt b/routeProcessor/src/main/kotlin/RouteDefinition.kt index 59ffedc7e..4401f25bb 100644 --- a/routeProcessor/src/main/kotlin/RouteDefinition.kt +++ b/routeProcessor/src/main/kotlin/RouteDefinition.kt @@ -70,12 +70,12 @@ internal data class PrefixRouteDefinition( internal data class NestedRouteDefinition( override val name: String, override var parent: RouteDefinition? = null, - val iRouteName: String, + val superQualifiedName: String, val childRoute: ArrayList = arrayListOf(), ) : RouteDefinition { override fun generateRoute(): String { - return if (iRouteName.isEmpty()) generateRootRoute() else generateIRoute() + return if (superQualifiedName.isEmpty()) generateRootRoute() else generateIRoute() } private fun generateRootRoute(): String { @@ -97,7 +97,7 @@ internal data class NestedRouteDefinition( overrideRoute = "override val route = \"${functions.parentPath}$pathWithParameter\"" } - return "${indent}actual object $name: $iRouteName {${System.lineSeparator()}" + + return "${indent}actual object $name: $superQualifiedName {${System.lineSeparator()}" + "${indent}$StandardIndent$overrideRoute${System.lineSeparator()}" + childRoute.joinToString(System.lineSeparator()) { it.generateRoute() } + System.lineSeparator() + diff --git a/routeProcessor/src/main/kotlin/RouteProcessor.kt b/routeProcessor/src/main/kotlin/RouteProcessor.kt index 4b316fb34..efba4e13f 100644 --- a/routeProcessor/src/main/kotlin/RouteProcessor.kt +++ b/routeProcessor/src/main/kotlin/RouteProcessor.kt @@ -61,7 +61,7 @@ internal class RouteProcessor( val schema = annotation.getStringValue(AppRoute::schema.name) ?: "" val packageName = annotation.getStringValue(AppRoute::packageName.name) ?: node.packageName.asString() - val className = node.qualifiedName!!.getShortName() + val className = node.qualifiedName?.getShortName() ?: "" val route = generateRoute(declaration = node) .takeIf { @@ -110,13 +110,13 @@ internal class RouteProcessor( val name = declaration.simpleName.getShortName() return when (declaration) { is KSClassDeclaration -> { - val isIRoute = declaration.superTypes.any { - it.resolve().declaration.simpleName.getShortName() == "IRoute" - } + val superQualifiedName = declaration.superTypes.firstOrNull()?.resolve() + ?.declaration?.qualifiedName?.asString() + NestedRouteDefinition( name = name, parent = parent, - iRouteName = if (isIRoute) "com.twidere.twiderex.navigation.IRoute" else "" + superQualifiedName = superQualifiedName.orEmpty() ).also { nestedRouteDefinition -> nestedRouteDefinition.childRoute.addAll( declaration.declarations From d8a80f2087ab6e5c9fd48a20e208d3597942f336 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 16 Dec 2021 16:11:07 +0800 Subject: [PATCH 503/615] update ci config --- .github/workflows/android.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index a77296d19..742157d84 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -111,6 +111,7 @@ jobs: with: api-level: ${{ matrix.api-level }} profile: 4in WVGA (Nexus S) + ram-size: 2048M target: ${{ matrix.target }} script: | adb logcat > logcat.txt & From 95d872ae0be5b71effbd278f22335d68b8fc5f63 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Dec 2021 16:32:05 +0800 Subject: [PATCH 504/615] support nitter instance config verify --- .../twiderex/di/modules/RepositoryModule.kt | 2 + .../twiderex/di/modules/ViewModelModule.kt | 2 +- .../twiderex/repository/NitterRepository.kt | 28 +++++++++++ .../twiderex/scenes/settings/MiscScene.kt | 46 +++++++++++++++---- .../viewmodel/settings/MiscViewModel.kt | 33 +++++++++++++ .../twidere/services/nitter/NitterService.kt | 17 +++++-- .../twidere/services/nitter/model/Users.kt | 28 +++++++++++ 7 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt create mode 100644 services/src/main/java/com/twidere/services/nitter/model/Users.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt index 6decfe047..25bb432ad 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/RepositoryModule.kt @@ -28,6 +28,7 @@ import com.twidere.twiderex.repository.GifRepository import com.twidere.twiderex.repository.ListsRepository import com.twidere.twiderex.repository.ListsUsersRepository import com.twidere.twiderex.repository.MediaRepository +import com.twidere.twiderex.repository.NitterRepository import com.twidere.twiderex.repository.NotificationRepository import com.twidere.twiderex.repository.ReactionRepository import com.twidere.twiderex.repository.SearchRepository @@ -54,4 +55,5 @@ val repositoryModule = module { single { UserListRepository() } single { UserRepository(get(), get()) } single { GifRepository(get()) } + single { NitterRepository() } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt index 9dac52438..aafa44ce6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -186,7 +186,7 @@ private fun Module.settings() { viewModel { AppearanceViewModel(get().appearancePreferences) } viewModel { DisplayViewModel(get().displayPreferences) } viewModel { LayoutViewModel(get()) } - viewModel { MiscViewModel(get().miscPreferences) } + viewModel { MiscViewModel(get().miscPreferences, get(), get()) } viewModel { NotificationViewModel(get().notificationPreferences) } viewModel { StorageViewModel(get()) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt new file mode 100644 index 000000000..5882f5d14 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.repository + +import com.twidere.services.nitter.NitterService +import com.twidere.twiderex.http.TwidereServiceFactory + +class NitterRepository { + suspend fun verifyInstance(userName: String, instance: String) = NitterService(instance, TwidereServiceFactory.createHttpClientFactory()).verifyInstance(userName) +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 665204fef..484a92511 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -20,19 +20,25 @@ */ package com.twidere.twiderex.scenes.settings +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.ContentAlpha import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.ListItem import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.material.TextButton @@ -53,6 +59,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.unit.dp import com.twidere.twiderex.component.foundation.AlertDialog import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton @@ -285,6 +292,9 @@ fun ItemProxy( @Composable fun NitterPreference(viewModel: MiscViewModel) { val value by viewModel.nitter.observeAsState(initial = "") + val nitterVerify by viewModel.nitterVerify.observeAsState(initial = false) + val nitterVerifyError by viewModel.nitterVerifyError.observeAsState(initial = "") + val nitterVerifyLoading by viewModel.nitterVerifyLoading.observeAsState(initial = false) var showInformationDialog by remember { mutableStateOf(false) } @@ -332,21 +342,37 @@ fun NitterPreference(viewModel: MiscViewModel) { ) }, trailingIcon = { - IconButton( - onClick = { - showUsageDialog = true - } - ) { - Icon( - painter = painterResource(res = com.twidere.twiderex.MR.files.ic_info_circle), - contentDescription = null, + if (nitterVerifyLoading) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp) ) + } else { + IconButton( + onClick = { + showUsageDialog = true + } + ) { + Icon( + painter = painterResource(res = com.twidere.twiderex.MR.files.ic_info_circle), + contentDescription = null, + ) + } } - } + }, + colors = TextFieldDefaults.textFieldColors(), + isError = !nitterVerify && value.isNotEmpty() ) }, secondaryText = { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) + Row { + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) + AnimatedVisibility( + visible = !nitterVerify, + modifier = Modifier.padding(start = 8.dp) + ) { + Text(nitterVerifyError, color = MaterialTheme.colors.error) + } + } } ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index e7874c984..a35500018 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,20 +21,53 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore +import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.preferences.model.MiscPreferences +import com.twidere.twiderex.repository.AccountRepository +import com.twidere.twiderex.repository.NitterRepository import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope class MiscViewModel( private val miscPreferences: DataStore, + private val accountRepository: AccountRepository, + private val nitterRepository: NitterRepository, ) : ViewModel() { + private val account by lazy { + accountRepository.activeAccount.mapNotNull { it } + } + val nitter by lazy { MutableStateFlow("") } + val nitterVerifyLoading by lazy { + MutableStateFlow(false) + } + + val nitterVerifyError by lazy { + MutableStateFlow("") + } + + val nitterVerify = nitter.filter { it.isNotEmpty() }.combine(account) { n, account -> + try { + nitterVerifyLoading.value = true + nitterRepository.verifyInstance(account.user.screenName, instance = n) + true + } catch (e: Exception) { + nitterVerifyError.value = e.message ?: "UnKnown Error" + false + } finally { + nitterVerifyLoading.value = false + } + }.asStateIn(viewModelScope, false) + val useProxy by lazy { MutableStateFlow(false) } diff --git a/services/src/main/java/com/twidere/services/nitter/NitterService.kt b/services/src/main/java/com/twidere/services/nitter/NitterService.kt index ccb9e9419..2f7ea4ecd 100644 --- a/services/src/main/java/com/twidere/services/nitter/NitterService.kt +++ b/services/src/main/java/com/twidere/services/nitter/NitterService.kt @@ -24,6 +24,7 @@ import com.twidere.services.http.HttpClientFactory import com.twidere.services.http.MicroBlogHttpException import com.twidere.services.nitter.model.ConversationTimeline import com.twidere.services.nitter.model.TweetNotFound +import com.twidere.services.nitter.model.Users import com.twidere.services.utils.await import moe.tlaster.hson.Hson import okhttp3.Request @@ -32,6 +33,11 @@ class NitterService( private val host: String, private val httpClientFactory: HttpClientFactory, ) { + suspend fun verifyInstance(userName: String) { + val target = "$host/search?f=users&q=$userName" + if (request(target)?.users.isNullOrEmpty()) throw TweetNotFoundException() + } + suspend fun conversation( screenName: String, statusId: String, @@ -44,11 +50,16 @@ class NitterService( it } } + println("target: $target") + return request(target) + } + + private suspend inline fun request(target: String,): T? { return httpClientFactory.createHttpClientBuilder() .addNetworkInterceptor { - it.proceed(it.request()).also { - if (it.code != 200) { - throw MicroBlogHttpException(it.code) + it.proceed(it.request()).also { response -> + if (response.code != 200) { + throw MicroBlogHttpException(response.code) } } } diff --git a/services/src/main/java/com/twidere/services/nitter/model/Users.kt b/services/src/main/java/com/twidere/services/nitter/model/Users.kt new file mode 100644 index 000000000..798c8859c --- /dev/null +++ b/services/src/main/java/com/twidere/services/nitter/model/Users.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.nitter.model + +import moe.tlaster.hson.annotations.HtmlSerializable + +data class Users( + @HtmlSerializable(".timeline-item") + val users: List, +) From 017e8e9bb108797636ba9985ab20c3d596cd8c51 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 16 Dec 2021 18:25:02 +0800 Subject: [PATCH 505/615] update ui for nitter instance settings --- .../twiderex/scenes/settings/MiscScene.kt | 109 +++++++++++------- .../viewmodel/settings/MiscViewModel.kt | 41 ++++--- .../resources/MR/files/svg/ic_link_error.svg | 8 ++ .../MR/files/svg/ic_link_success.svg | 4 + 4 files changed, 103 insertions(+), 59 deletions(-) create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_link_error.svg create mode 100644 common/src/commonMain/resources/MR/files/svg/ic_link_success.svg diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 484a92511..35e208ee8 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -20,12 +20,11 @@ */ package com.twidere.twiderex.scenes.settings -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -33,13 +32,13 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.ContentAlpha +import androidx.compose.material.Divider import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.ListItem import androidx.compose.material.LocalContentAlpha import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.material.TextField @@ -293,7 +292,6 @@ fun ItemProxy( fun NitterPreference(viewModel: MiscViewModel) { val value by viewModel.nitter.observeAsState(initial = "") val nitterVerify by viewModel.nitterVerify.observeAsState(initial = false) - val nitterVerifyError by viewModel.nitterVerifyError.observeAsState(initial = "") val nitterVerifyLoading by viewModel.nitterVerifyLoading.observeAsState(initial = false) var showInformationDialog by remember { mutableStateOf(false) @@ -305,6 +303,10 @@ fun NitterPreference(viewModel: MiscViewModel) { NitterUsageDialog( onDismissRequest = { showUsageDialog = false + }, + value = value, + onConfirm = { + viewModel.setNitterInstance(it) } ) } @@ -333,65 +335,90 @@ fun NitterPreference(viewModel: MiscViewModel) { } ListItem( text = { - OutlinedTextField( - value = value, - onValueChange = { viewModel.setNitterInstance(it) }, - placeholder = { - Text( - text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_placeholder) - ) - }, - trailingIcon = { - if (nitterVerifyLoading) { - CircularProgressIndicator( - modifier = Modifier.size(24.dp) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_placeholder)) + }, + trailing = { + if (nitterVerifyLoading) { + CircularProgressIndicator( + modifier = Modifier.size(48.dp) + .padding(12.dp), + strokeWidth = 2.dp + ) + } else if (value.isNotEmpty()) { + IconButton( + onClick = { + viewModel.verifyNitterInstance() + } + ) { + if (nitterVerify) { + Icon( + painter = painterResource(res = com.twidere.twiderex.MR.files.ic_link_success), + contentDescription = null, + tint = MaterialTheme.colors.primary ) } else { - IconButton( - onClick = { - showUsageDialog = true - } - ) { - Icon( - painter = painterResource(res = com.twidere.twiderex.MR.files.ic_info_circle), - contentDescription = null, - ) - } + Icon( + painter = painterResource(res = com.twidere.twiderex.MR.files.ic_link_error), + contentDescription = null, + tint = MaterialTheme.colors.error + ) } - }, - colors = TextFieldDefaults.textFieldColors(), - isError = !nitterVerify && value.isNotEmpty() - ) - }, - secondaryText = { - Row { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) - AnimatedVisibility( - visible = !nitterVerify, - modifier = Modifier.padding(start = 8.dp) - ) { - Text(nitterVerifyError, color = MaterialTheme.colors.error) } } + }, + secondaryText = { + Text(text = value.takeIf { it.isNotEmpty() } ?: "Instance URL") + }, + modifier = Modifier.clickable { + showUsageDialog = true } ) + Column(modifier = Modifier.padding(start = 16.dp)) { + Divider() + Spacer(modifier = Modifier.padding(top = 5.dp)) + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) + } } @Composable fun NitterUsageDialog( onDismissRequest: () -> Unit, + value: String, + onConfirm: (String) -> Unit ) { val navigator = LocalNavigator.current + var input by remember { + mutableStateOf(value) + } AlertDialog( onDismissRequest = onDismissRequest, title = { Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_title)) }, text = { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_content)) + Column { + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_dialog_usage_content)) + TextField( + value = input, + onValueChange = { + input = it + }, + placeholder = { + // TOOD LOCALIZE + Text("Instance URL") + }, + colors = TextFieldDefaults.textFieldColors( + backgroundColor = Color.Transparent + ), + ) + } }, confirmButton = { - TextButton(onClick = onDismissRequest) { + TextButton(onClick = { + // TODO check if input is valid + onConfirm(input) + onDismissRequest.invoke() + }) { Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) } }, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index a35500018..df30b4c9e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -21,13 +21,10 @@ package com.twidere.twiderex.viewmodel.settings import androidx.datastore.core.DataStore -import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.preferences.model.MiscPreferences import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.NitterRepository import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch @@ -51,23 +48,10 @@ class MiscViewModel( MutableStateFlow(false) } - val nitterVerifyError by lazy { - MutableStateFlow("") + val nitterVerify by lazy { + MutableStateFlow(false) } - val nitterVerify = nitter.filter { it.isNotEmpty() }.combine(account) { n, account -> - try { - nitterVerifyLoading.value = true - nitterRepository.verifyInstance(account.user.screenName, instance = n) - true - } catch (e: Exception) { - nitterVerifyError.value = e.message ?: "UnKnown Error" - false - } finally { - nitterVerifyLoading.value = false - } - }.asStateIn(viewModelScope, false) - val useProxy by lazy { MutableStateFlow(false) } @@ -102,6 +86,7 @@ class MiscViewModel( proxyPassword.value = miscPreferences.data.first().proxyPassword proxyType.value = miscPreferences.data.first().proxyType } + verifyNitterInstance() } fun setNitterInstance(value: String) { @@ -111,6 +96,26 @@ class MiscViewModel( it.copy(nitterInstance = value) } } + verifyNitterInstance() + } + + fun verifyNitterInstance() { + if (nitter.value.isEmpty()) { + nitterVerify.value = false + nitterVerifyLoading.value = false + return + } + viewModelScope.launch { + try { + nitterVerifyLoading.value = true + nitterRepository.verifyInstance(account.first().user.screenName, instance = nitter.value) + nitterVerify.value = true + } catch (e: Exception) { + nitterVerify.value = false + } finally { + nitterVerifyLoading.value = false + } + } } fun setUseProxy(value: Boolean) { diff --git a/common/src/commonMain/resources/MR/files/svg/ic_link_error.svg b/common/src/commonMain/resources/MR/files/svg/ic_link_error.svg new file mode 100644 index 000000000..d67ea3051 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_link_error.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/common/src/commonMain/resources/MR/files/svg/ic_link_success.svg b/common/src/commonMain/resources/MR/files/svg/ic_link_success.svg new file mode 100644 index 000000000..429117336 --- /dev/null +++ b/common/src/commonMain/resources/MR/files/svg/ic_link_success.svg @@ -0,0 +1,4 @@ + + + + From 30d6ba0b8ca06438cb77df56a8e3e0d395cfe497 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 16 Dec 2021 18:49:35 +0800 Subject: [PATCH 506/615] update ci config --- .github/workflows/common.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 4fbec27a7..6fdca6e27 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -85,6 +85,7 @@ jobs: api-level: ${{ matrix.api-level }} profile: 4in WVGA (Nexus S) target: ${{ matrix.target }} + ram-size: 2048M script: | adb logcat > logcat.txt & ./gradlew :common:connectedCheck From f8f44157de22c40ad1ac4b9054a97b4f263ba877 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Dec 2021 12:07:14 +0800 Subject: [PATCH 507/615] add input valid check for nitter instance config --- .../twiderex/repository/NitterRepository.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 35 ++++++++++++++----- .../viewmodel/settings/MiscViewModel.kt | 15 ++++++++ .../twidere/services/nitter/NitterService.kt | 21 +++++++---- .../nitter/model/{Users.kt => Profile.kt} | 6 ++-- 5 files changed, 61 insertions(+), 18 deletions(-) rename services/src/main/java/com/twidere/services/nitter/model/{Users.kt => Profile.kt} (90%) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt index 5882f5d14..7121f3ddd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt @@ -24,5 +24,5 @@ import com.twidere.services.nitter.NitterService import com.twidere.twiderex.http.TwidereServiceFactory class NitterRepository { - suspend fun verifyInstance(userName: String, instance: String) = NitterService(instance, TwidereServiceFactory.createHttpClientFactory()).verifyInstance(userName) + suspend fun verifyInstance(screenName: String, instance: String) = NitterService(instance, TwidereServiceFactory.createHttpClientFactory()).verifyInstance(screenName) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 35e208ee8..563124705 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -293,6 +293,7 @@ fun NitterPreference(viewModel: MiscViewModel) { val value by viewModel.nitter.observeAsState(initial = "") val nitterVerify by viewModel.nitterVerify.observeAsState(initial = false) val nitterVerifyLoading by viewModel.nitterVerifyLoading.observeAsState(initial = false) + val isNitterInputValid by viewModel.isNitterInputValid.observeAsState(initial = false) var showInformationDialog by remember { mutableStateOf(false) } @@ -307,7 +308,11 @@ fun NitterPreference(viewModel: MiscViewModel) { value = value, onConfirm = { viewModel.setNitterInstance(it) - } + }, + onValidCheck = { + viewModel.checkIfNitterInputValid(it) + }, + isValid = isNitterInputValid ) } if (showInformationDialog) { @@ -384,12 +389,15 @@ fun NitterPreference(viewModel: MiscViewModel) { fun NitterUsageDialog( onDismissRequest: () -> Unit, value: String, - onConfirm: (String) -> Unit + onConfirm: (String) -> Unit, + onValidCheck: (String) -> Unit, + isValid: Boolean ) { val navigator = LocalNavigator.current var input by remember { mutableStateOf(value) } + AlertDialog( onDismissRequest = onDismissRequest, title = { @@ -401,6 +409,7 @@ fun NitterUsageDialog( TextField( value = input, onValueChange = { + onValidCheck.invoke(it) input = it }, placeholder = { @@ -410,17 +419,27 @@ fun NitterUsageDialog( colors = TextFieldDefaults.textFieldColors( backgroundColor = Color.Transparent ), + isError = !isValid, ) + if (!isValid) { + Text( + // TOOD LOCALIZE + text = "Nitter instance URL is invalid, e.g. https://nitter.net", + modifier = Modifier.padding(top = 8.dp), + style = MaterialTheme.typography.body2.copy(color = MaterialTheme.colors.error) + ) + } } }, confirmButton = { TextButton(onClick = { - // TODO check if input is valid - onConfirm(input) - onDismissRequest.invoke() - }) { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) - } + if (isValid) { + onConfirm.invoke(input) + onDismissRequest.invoke() + } + }, enabled = isValid) { + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) + } }, dismissButton = { TextButton( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt index df30b4c9e..9d454f598 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/MiscViewModel.kt @@ -36,6 +36,7 @@ class MiscViewModel( private val accountRepository: AccountRepository, private val nitterRepository: NitterRepository, ) : ViewModel() { + private val account by lazy { accountRepository.activeAccount.mapNotNull { it } } @@ -44,6 +45,10 @@ class MiscViewModel( MutableStateFlow("") } + val isNitterInputValid by lazy { + MutableStateFlow(true) + } + val nitterVerifyLoading by lazy { MutableStateFlow(false) } @@ -89,7 +94,16 @@ class MiscViewModel( verifyNitterInstance() } + fun checkIfNitterInputValid(value: String) { + isNitterInputValid.value = value.isEmpty() || + ( + (value.startsWith("http://") || value.startsWith("https://")) && + !value.endsWith("/") + ) + } + fun setNitterInstance(value: String) { + if (nitter.value == value) return nitter.value = value viewModelScope.launch { miscPreferences.updateData { @@ -111,6 +125,7 @@ class MiscViewModel( nitterRepository.verifyInstance(account.first().user.screenName, instance = nitter.value) nitterVerify.value = true } catch (e: Exception) { + e.printStackTrace() nitterVerify.value = false } finally { nitterVerifyLoading.value = false diff --git a/services/src/main/java/com/twidere/services/nitter/NitterService.kt b/services/src/main/java/com/twidere/services/nitter/NitterService.kt index 2f7ea4ecd..6064755b2 100644 --- a/services/src/main/java/com/twidere/services/nitter/NitterService.kt +++ b/services/src/main/java/com/twidere/services/nitter/NitterService.kt @@ -23,19 +23,21 @@ package com.twidere.services.nitter import com.twidere.services.http.HttpClientFactory import com.twidere.services.http.MicroBlogHttpException import com.twidere.services.nitter.model.ConversationTimeline +import com.twidere.services.nitter.model.Profile import com.twidere.services.nitter.model.TweetNotFound -import com.twidere.services.nitter.model.Users +import com.twidere.services.utils.DEBUG import com.twidere.services.utils.await import moe.tlaster.hson.Hson import okhttp3.Request +import okhttp3.logging.HttpLoggingInterceptor class NitterService( private val host: String, private val httpClientFactory: HttpClientFactory, ) { - suspend fun verifyInstance(userName: String) { - val target = "$host/search?f=users&q=$userName" - if (request(target)?.users.isNullOrEmpty()) throw TweetNotFoundException() + suspend fun verifyInstance(screenName: String) { + val target = "$host/$screenName" + if (request(target) == null) throw TweetNotFoundException() } suspend fun conversation( @@ -50,11 +52,10 @@ class NitterService( it } } - println("target: $target") return request(target) } - private suspend inline fun request(target: String,): T? { + private suspend inline fun request(target: String): T? { return httpClientFactory.createHttpClientBuilder() .addNetworkInterceptor { it.proceed(it.request()).also { response -> @@ -62,6 +63,14 @@ class NitterService( throw MicroBlogHttpException(response.code) } } + }.apply { + if (DEBUG) { + addInterceptor( + HttpLoggingInterceptor().apply { + setLevel(HttpLoggingInterceptor.Level.BODY) + } + ) + } } .build() .newCall( diff --git a/services/src/main/java/com/twidere/services/nitter/model/Users.kt b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt similarity index 90% rename from services/src/main/java/com/twidere/services/nitter/model/Users.kt rename to services/src/main/java/com/twidere/services/nitter/model/Profile.kt index 798c8859c..0449c046c 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Users.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt @@ -22,7 +22,7 @@ package com.twidere.services.nitter.model import moe.tlaster.hson.annotations.HtmlSerializable -data class Users( - @HtmlSerializable(".timeline-item") - val users: List, +data class Profile( + @HtmlSerializable(".profile-card-username",) + val username: String, ) From 3a0d72199ce98b12f9cf204641aa9466b678fc47 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Dec 2021 12:27:42 +0800 Subject: [PATCH 508/615] update localization --- .../twiderex/scenes/settings/MiscScene.kt | 36 +++++--- .../commonMain/resources/MR/base/strings.xml | 89 +++++++++++++------ localization | 2 +- 3 files changed, 87 insertions(+), 40 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 563124705..68355a7bc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -24,8 +24,10 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState @@ -345,9 +347,9 @@ fun NitterPreference(viewModel: MiscViewModel) { trailing = { if (nitterVerifyLoading) { CircularProgressIndicator( - modifier = Modifier.size(48.dp) - .padding(12.dp), - strokeWidth = 2.dp + modifier = Modifier.size(NitterPreferenceDefaults.Loading.Size) + .padding(NitterPreferenceDefaults.Loading.Padding), + strokeWidth = NitterPreferenceDefaults.Loading.StrokeWidth ) } else if (value.isNotEmpty()) { IconButton( @@ -372,19 +374,29 @@ fun NitterPreference(viewModel: MiscViewModel) { } }, secondaryText = { - Text(text = value.takeIf { it.isNotEmpty() } ?: "Instance URL") + Text(text = value.takeIf { it.isNotEmpty() } ?: stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_value)) }, modifier = Modifier.clickable { showUsageDialog = true } ) - Column(modifier = Modifier.padding(start = 16.dp)) { + Column(modifier = Modifier.padding(start = NitterPreferenceDefaults.ContentPaddingStart)) { Divider() - Spacer(modifier = Modifier.padding(top = 5.dp)) + Spacer(modifier = Modifier.height(NitterPreferenceDefaults.ContentVerticalSpacing)) Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_description)) } } +private object NitterPreferenceDefaults { + object Loading { + val Size = 48.dp + val Padding = PaddingValues(12.dp) + val StrokeWidth = 2.dp + } + val ContentPaddingStart = 16.dp + val ContentVerticalSpacing = 8.dp +} + @Composable fun NitterUsageDialog( onDismissRequest: () -> Unit, @@ -413,8 +425,7 @@ fun NitterUsageDialog( input = it }, placeholder = { - // TOOD LOCALIZE - Text("Instance URL") + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_value),) }, colors = TextFieldDefaults.textFieldColors( backgroundColor = Color.Transparent @@ -423,9 +434,8 @@ fun NitterUsageDialog( ) if (!isValid) { Text( - // TOOD LOCALIZE - text = "Nitter instance URL is invalid, e.g. https://nitter.net", - modifier = Modifier.padding(top = 8.dp), + text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_nitter_input_invalid), + modifier = Modifier.padding(NitterUsageDialogDefaults.InvalidTextPadding), style = MaterialTheme.typography.body2.copy(color = MaterialTheme.colors.error) ) } @@ -456,6 +466,10 @@ fun NitterUsageDialog( ) } +private object NitterUsageDialogDefaults { + val InvalidTextPadding = PaddingValues(top = 8.dp) +} + @Composable fun NitterInformationDialog( onDismissRequest: () -> Unit, diff --git a/common/src/commonMain/resources/MR/base/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml index 2f1f5acd0..65e3a0ea1 100644 --- a/common/src/commonMain/resources/MR/base/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -1,68 +1,87 @@ - %s has been unmuted + Do you want to unmute %s? Unfollow user %s? - Media will be shared after download is completed Failed to Unfollowing Please try again Tweet Sent Failed to Login Server URL is incorrect. - Too Many Requests - Following Succeeded - Failed to Following - Please try again - %s has been reported for spam + Toot Deleted + Failed to Toot + Please try again + Your toot has been saved to Drafts. Twitter Rules Account Suspended Twitter suspends accounts which violate the %s - Failed to save media - Please try again %s has been blocked - Cancel follow request for %s? - Failed to report %s - Please try again %s has been reported for spam and blocked + Delete Toot + Do you want to delete this toot? Failed to Save Photo Please try again - No Tweets Found Saving media - Failed to Login - Connection timeout. + Do you want to mute %s? %s has been unblocked - %s has been muted Permission Denied Sorry, you are not authorized Sending message Failed to send message Failed to unmute %s Please try again + Permission Denied + You have been blocked from following this account at the request of the user + Media saved + Following Request Sent + Do you want to unblock %s? + Sending toot + Toot Posted + Sending tweet + Do you want to block %s? + Photo Saved + Failed to Tweet + Please try again + Your tweet has been saved to Drafts. + Failed to unblock %s + Please try again + Delete Tweet + Do you want to delete this tweet? + %s has been unmuted + Media will be shared after download is completed + Too Many Requests + Failed to Delete Toot + Please try again + Following Succeeded + Failed to Following + Please try again + %s has been reported for spam + Failed to save media + Please try again + Cancel follow request for %s? + Failed to report %s + Please try again + No Tweets Found + Failed to Login + Connection timeout. + %s has been muted Rate Limit Exceeded Reached Twitter API usage limit Failed to Load Please try again - Permission Denied - You have been blocked from following this account at the request of the user - Media saved Tweet Deleted - Following Request Sent Failed to mute %s Please try again - Sending tweet - Do you want to block %s? Failed to Delete Tweet Please try again - Photo Saved Account Temporarily Locked Open Twitter to unlock Failed to block %s Please try again - Failed to Tweet - Please try again Unfollowing Succeeded Failed to report and block %s Please try again - Failed to unblock %s - Please try again + Sign out + Do you want to sign out? + Tweet Posted Messages Direct messages Interactions @@ -85,6 +104,7 @@ Follows you Mute %s %s is following you + Request Unblock Blocked Report and Block @@ -109,12 +129,15 @@ Open in Safari Yes Save + Delete Remove Confirm Save photo Take photo OK + Browse Sign in + Sign out %s boosted %s retweeted You retweeted @@ -179,8 +202,8 @@ Add mention Browse Library Add GIF - Take Photo Record Video + Take Photo Open draft Enable location Disable location @@ -210,13 +233,16 @@ Add Remove All + Mentions Notification Delete account Accounts Show less Search tweets or users + Toots Media Tweets + People Users Hashtag Search @@ -319,8 +345,10 @@ Proxy type Reverse Username + Nitter instance URL is invalid, e.g. https://nitter.net Alternative Twitter front-end focused on privacy. Nitter Instance + Instance URL Using Third-party data provider in Project URL - Twitter status threading @@ -358,6 +386,7 @@ Compose Reply Others in this conversation: + Choice %d Multiple choice 1 day 30 minutes @@ -366,6 +395,10 @@ 3 days 5 minutes 6 hours + Visible for followers only + Visible for all, shown in public timelines + Visible for all, but not in public timelines + Visible for mentioned users only Replying to SUBSCRIBED MY LISTS diff --git a/localization b/localization index aa706e1af..53bd6cacc 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit aa706e1afa05dd93d001da630c6932b42d1db07f +Subproject commit 53bd6caccf213766751460de4170be33a5264b8e From 17a1d8b2da1f129b32dd912169c0d2ba3ecadae2 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Dec 2021 12:32:05 +0800 Subject: [PATCH 509/615] update ic_info_cricle.svg --- .../commonMain/resources/MR/files/svg/ic_info_circle.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/src/commonMain/resources/MR/files/svg/ic_info_circle.svg b/common/src/commonMain/resources/MR/files/svg/ic_info_circle.svg index 3aee4d8da..7c09b6ffb 100644 --- a/common/src/commonMain/resources/MR/files/svg/ic_info_circle.svg +++ b/common/src/commonMain/resources/MR/files/svg/ic_info_circle.svg @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file From f82dbee9243600f54a1e718e2793d4e3f2308928 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Dec 2021 18:03:40 +0800 Subject: [PATCH 510/615] fixed timeline footer can't display when media count is 3 --- .../status/TimelineStatusComponent.kt | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 5c2a241c3..f5cc49bd7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -430,46 +430,42 @@ fun StatusContent( modifier = Modifier .weight(1f) ) { - Row { - Column { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Row( - modifier = Modifier.weight(1f), - ) { - UserName(status.user, fontWeight = FontWeight.W600) - if (type == StatusContentType.Normal) { - Spacer(modifier = Modifier.width(StatusContentDefaults.Normal.UserNameSpacing)) - UserScreenName(status.user) - } - } - CompositionLocalProvider( - LocalContentAlpha provides ContentAlpha.disabled - ) { - val mastodonExtra = status.mastodonExtra - if (status.platformType == PlatformType.Mastodon && mastodonExtra != null) { - Icon( - modifier = Modifier.size(LocalTextStyle.current.fontSize.value.dp), - painter = mastodonExtra.visibility.icon(), - contentDescription = mastodonExtra.visibility.name - ) - Spacer(modifier = Modifier.width(StatusContentDefaults.Mastodon.VisibilitySpacing)) - } - if (type == StatusContentType.Normal) { - HumanizedTime(time = status.timestamp) - } - } + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + modifier = Modifier.weight(1f), + ) { + UserName(status.user, fontWeight = FontWeight.W600) + if (type == StatusContentType.Normal) { + Spacer(modifier = Modifier.width(StatusContentDefaults.Normal.UserNameSpacing)) + UserScreenName(status.user) + } + } + CompositionLocalProvider( + LocalContentAlpha provides ContentAlpha.disabled + ) { + val mastodonExtra = status.mastodonExtra + if (status.platformType == PlatformType.Mastodon && mastodonExtra != null) { + Icon( + modifier = Modifier.size(LocalTextStyle.current.fontSize.value.dp), + painter = mastodonExtra.visibility.icon(), + contentDescription = mastodonExtra.visibility.name + ) + Spacer(modifier = Modifier.width(StatusContentDefaults.Mastodon.VisibilitySpacing)) } - when (type) { - StatusContentType.Normal -> { - Spacer(modifier = Modifier.height(StatusContentDefaults.Normal.BodySpacing)) - StatusBody(status, type = type) - } - StatusContentType.Extend -> UserScreenName(status.user) + if (type == StatusContentType.Normal) { + HumanizedTime(time = status.timestamp) } } } + when (type) { + StatusContentType.Normal -> { + Spacer(modifier = Modifier.height(StatusContentDefaults.Normal.BodySpacing)) + StatusBody(status, type = type) + } + StatusContentType.Extend -> UserScreenName(status.user) + } if (type == StatusContentType.Extend) { Column { Spacer(modifier = Modifier.height(StatusContentDefaults.Extend.BodySpacing)) From ad02a4e738d455a01c6572febe89970128a2d8e6 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 17 Dec 2021 18:36:46 +0800 Subject: [PATCH 511/615] fixed expanded icon causing timeline footer can't display --- .../kotlin/com/twidere/twiderex/component/status/StatusText.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index e164c9c45..213e44a40 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme @@ -75,7 +76,7 @@ fun ColumnScope.StatusText( }, ) { Icon( - modifier = Modifier.padding(StatusTextDefaults.Mastodon.SpoilerButtonPadding), + modifier = Modifier.size(width = 46.dp, height = 20.dp).padding(StatusTextDefaults.Mastodon.SpoilerButtonPadding), painter = painterResource(res = com.twidere.twiderex.MR.files.ic_expand_more), contentDescription = null, tint = MaterialTheme.colors.primary, From 03475da15d46f3882ff36eef8e4b586c0225b903 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Dec 2021 20:49:29 +0800 Subject: [PATCH 512/615] upgrade kotlin --- buildSrc/src/main/kotlin/Versions.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 97c608639..9645b8663 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,8 +2,8 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.6.10-RC" - const val coroutines = "1.6.0-RC" + const val lang = "1.6.10" + const val coroutines = "1.6.0-RC3" const val serialization = "1.3.1" } @@ -12,7 +12,7 @@ object Versions { val java = JavaVersion.VERSION_11 } - const val ksp = "${Kotlin.lang}-1.0.1" + const val ksp = "${Kotlin.lang}-1.0.2" const val agp = "7.0.3" const val spotless = "6.0.4" const val ktlint = "0.42.1" @@ -24,7 +24,7 @@ object Versions { const val activity = "1.4.0" const val datastore = "1.0.0" const val androidx_hilt = "1.0.0" - const val room = "2.4.0-rc01" + const val room = "2.4.0" const val lifecycle = "2.4.0" const val lifecycle_compose = "2.4.0" const val work = "2.7.1" From 055c28309571c822ffcde6eefc97655667c1e1f5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Dec 2021 21:55:33 +0800 Subject: [PATCH 513/615] update ci --- .github/workflows/common.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 6fdca6e27..72f8b0d63 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -70,7 +70,7 @@ jobs: - name: set up JDK uses: actions/setup-java@v1 with: - java-version: 17 + java-version: 11 - name: Set up Android SDK License run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses From bb4e834a8fff59b0597aa60d24659822610ff17c Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 20 Dec 2021 14:50:26 +0800 Subject: [PATCH 514/615] remove hardcode dimension --- .../com/twidere/twiderex/component/status/StatusText.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index 213e44a40..e9fce83c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -76,7 +76,7 @@ fun ColumnScope.StatusText( }, ) { Icon( - modifier = Modifier.size(width = 46.dp, height = 20.dp).padding(StatusTextDefaults.Mastodon.SpoilerButtonPadding), + modifier = Modifier.size(width = StatusTextDefaults.Mastodon.MoreButton.Width, height = StatusTextDefaults.Mastodon.MoreButton.Height).padding(StatusTextDefaults.Mastodon.SpoilerButtonPadding), painter = painterResource(res = com.twidere.twiderex.MR.files.ic_expand_more), contentDescription = null, tint = MaterialTheme.colors.primary, @@ -106,6 +106,10 @@ fun ColumnScope.StatusText( object StatusTextDefaults { object Mastodon { + object MoreButton { + val Width = 46.dp + val Height = 20.dp + } val SpoilerSpacing = 2.dp val SpoilerButtonPadding = PaddingValues( 2.dp From 7ae0050eeb7f87e809dc45f388d225fc4579b991 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 17 Dec 2021 21:55:33 +0800 Subject: [PATCH 515/615] Revert "update ci" This reverts commit 055c28309571c822ffcde6eefc97655667c1e1f5. --- .github/workflows/common.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 72f8b0d63..6fdca6e27 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -70,7 +70,7 @@ jobs: - name: set up JDK uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Set up Android SDK License run: (while sleep 3; do echo "y"; done) | /Users/runner/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager --licenses From a1b5e5f3b56662dc80639de5b08af82a1cc67f6f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 20 Dec 2021 15:15:07 +0800 Subject: [PATCH 516/615] hide Nitter Preference when platform is not Twitter --- .../com/twidere/twiderex/scenes/settings/MiscScene.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 68355a7bc..bfd0cff28 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -73,14 +73,16 @@ import com.twidere.twiderex.component.settings.switchItem import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState +import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.preferences.model.MiscPreferences +import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.settings.MiscViewModel @Composable fun MiscScene() { val viewModel: MiscViewModel = getViewModel() - + val account = LocalActiveAccount.current TwidereScene { InAppNotificationScaffold( topBar = { @@ -114,7 +116,9 @@ fun MiscScene() { rememberScrollState() ) ) { - NitterPreference(viewModel) + if (account?.type == PlatformType.Twitter) { + NitterPreference(viewModel) + } ProxyPreference( viewModel = viewModel, showProxyInputDialog = showProxyInputDialog, From c0f46c537f119bf99123f2fb5cf81aa585f06b50 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 20 Dec 2021 18:49:50 +0800 Subject: [PATCH 517/615] update gradle --- buildSrc/src/main/kotlin/Versions.kt | 6 +++--- common/build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a4e563364..153e81601 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -13,8 +13,8 @@ object Versions { } const val ksp = "${Kotlin.lang}-1.0.2" - const val agp = "7.0.3" - const val spotless = "6.0.4" + const val agp = "7.0.4" + const val spotless = "6.0.5" const val ktlint = "0.43.2" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" @@ -30,7 +30,7 @@ object Versions { const val work = "2.7.1" const val startup = "1.1.0" const val coil = "2.0.0-alpha05" - const val accompanist = "0.21.4-beta" + const val accompanist = "0.21.5-rc" const val accompanist_jb = "0.18.1" const val androidx_exifinterface = "1.3.3" const val exoplayer = "2.16.1" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 784299457..4ba075537 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -78,7 +78,7 @@ kotlin { dependencies { implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") implementation("androidx.savedstate:savedstate-ktx:1.1.0") - implementation("androidx.core:core-ktx:1.8.0-alpha01") + implementation("androidx.core:core-ktx:1.8.0-alpha02") implementation("io.insert-koin:koin-android:${Versions.koin}") implementation("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") implementation("androidx.room:room-runtime:${Versions.room}") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 946e1bda2..46dcc6610 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=9afb3ca688fc12c761a0e9e4321e4d24e977a4a8916c8a768b1fe05ddb4d6b66 \ No newline at end of file +distributionSha256Sum=23b89f8eac363f5f4b8336e0530c7295c55b728a9caa5268fdd4a532610d5392 \ No newline at end of file From ed5e789c59ecf27c095b953d0554abe4d6346f17 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 22 Dec 2021 16:36:51 +0800 Subject: [PATCH 518/615] begin to support image compress --- .../kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt new file mode 100644 index 000000000..ec4879753 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt @@ -0,0 +1,5 @@ +package com.twidere.twiderex.kmp + +expect class ImageCompressor { + fun compress(file:String, targetSize:Long):String +} \ No newline at end of file From ed3a8eb3547e9507f8f09683cf4afc18150b8148 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 18:03:51 +0800 Subject: [PATCH 519/615] update packages --- .github/workflows/android.yml | 2 +- .github/workflows/common.yml | 2 +- buildSrc/src/main/kotlin/Versions.kt | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 742157d84..2928f23d1 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -80,7 +80,7 @@ jobs: fail-fast: false matrix: include: - - api-level: 29 + - api-level: 30 target: default - api-level: 28 target: default diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 6fdca6e27..ea7b56619 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -53,7 +53,7 @@ jobs: fail-fast: false matrix: include: - - api-level: 29 + - api-level: 30 target: default - api-level: 28 target: default diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 153e81601..44d4223a9 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -3,8 +3,8 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { const val lang = "1.6.10" - const val coroutines = "1.6.0-RC3" - const val serialization = "1.3.1" + const val coroutines = "1.6.0" + const val serialization = "1.3.2" } object Java { @@ -19,7 +19,7 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.0.1-rc2" + const val compose_jb = "1.0.1" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" @@ -30,7 +30,7 @@ object Versions { const val work = "2.7.1" const val startup = "1.1.0" const val coil = "2.0.0-alpha05" - const val accompanist = "0.21.5-rc" + const val accompanist = "0.22.0-rc" const val accompanist_jb = "0.18.1" const val androidx_exifinterface = "1.3.3" const val exoplayer = "2.16.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46dcc6610..3e000b9fe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=23b89f8eac363f5f4b8336e0530c7295c55b728a9caa5268fdd4a532610d5392 \ No newline at end of file +distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 \ No newline at end of file From 6df6132a646ed343f599e3147332337593fb1b3d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 18:24:57 +0800 Subject: [PATCH 520/615] update ci android target --- .github/workflows/android.yml | 2 +- .github/workflows/common.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 2928f23d1..27f631e95 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -81,7 +81,7 @@ jobs: matrix: include: - api-level: 30 - target: default + target: google_apis - api-level: 28 target: default - api-level: 26 diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index ea7b56619..305009913 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -54,7 +54,7 @@ jobs: matrix: include: - api-level: 30 - target: default + target: google_apis - api-level: 28 target: default - api-level: 26 From 55d5e548f41addd8997aa4822fd86020916d7a41 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 21:14:23 +0800 Subject: [PATCH 521/615] upgrade ci config --- .github/workflows/android.yml | 2 ++ .github/workflows/desktop.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 27f631e95..8c0445aae 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -2,6 +2,8 @@ name: Android CI on: push: + tags: + - '**' branches: - master - develop diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index a8ef14d45..a95f2f1c9 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -2,6 +2,8 @@ name: Desktop CI on: push: + tags: + - '**' branches: - master - develop From d3e2425b90c266a3f8db77a6cfe1d3d134843140 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 21:50:43 +0800 Subject: [PATCH 522/615] upgrade ci config --- .github/workflows/android.yml | 71 --------------- .github/workflows/desktop.yml | 105 --------------------- .github/workflows/release.yml | 167 ++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 176 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8c0445aae..93e356bff 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -2,8 +2,6 @@ name: Android CI on: push: - tags: - - '**' branches: - master - develop @@ -132,72 +130,3 @@ jobs: with: name: test-results-${{ matrix.api-level }}-${{ matrix.target }} path: "**/build/outputs/**/connected/**/*.xml" - - release : - if: startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - needs: [build, unit-test, connected-test] - timeout-minutes: 30 - - steps: - - uses: actions/checkout@v2 - - - name: Set up JDK - uses: actions/setup-java@v1 - with: - java-version: 17 - - - name: Set up Android SDK License - run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses - - - name: Apply Signing - env: - ALIAS: ${{ secrets.ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }} - run: ./.github/apply_signing.sh - - - name: Build with Gradle - run: ./gradlew :android:assembleRelease :android:bundleRelease - - - name: Check if is prelease - if: startsWith(github.ref, 'refs/tags/') - id: check-tag - run: | - if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then - echo ::set-output name=prelease::true - fi - - - name: Create Prerelease - if: steps.check-tag.outputs.prelease == 'true' - run: | - set -x - assets=() - for asset in $(find -name *-release.apk); do - assets+=("-a" "$asset") - done - for asset in $(find -name *-release.aab); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: steps.check-tag.outputs.prelease != 'true' - run: | - set -x - assets=() - for asset in $(find -name *-release.apk); do - assets+=("-a" "$asset") - done - for asset in $(find -name *-release.aab); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index a95f2f1c9..9c84c5599 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -2,8 +2,6 @@ name: Desktop CI on: push: - tags: - - '**' branches: - master - develop @@ -38,40 +36,6 @@ jobs: name: build-binaries-ubuntu path: '**/build/compose/binaries' - - name: Check if is prelease - if: startsWith(github.ref, 'refs/tags/') - id: check-tag - run: | - if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then - echo ::set-output name=prelease::true - fi - - - name: Create Prerelease - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' - run: | - set -x - assets=() - for asset in $(find -name *.deb); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' - run: | - set -x - assets=() - for asset in $(find -name *.deb); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build-windows: runs-on: windows-latest timeout-minutes: 30 @@ -92,40 +56,6 @@ jobs: name: build-binaries-windows path: '**/build/compose/binaries' - - name: Check if is prelease - if: startsWith(github.ref, 'refs/tags/') - id: check-tag - run: | - if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then - echo ::set-output name=prelease::true - fi - - - name: Create Prerelease - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' - run: | - set -x - assets=() - for asset in $(find -name *.msi); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' - run: | - set -x - assets=() - for asset in $(find -name *.msi); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build-macos: runs-on: macos-latest timeout-minutes: 30 @@ -145,38 +75,3 @@ jobs: with: name: build-binaries-macos path: '**/build/compose/binaries' - - - name: Check if is prelease - if: startsWith(github.ref, 'refs/tags/') - id: check-tag - run: | - if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then - echo ::set-output name=prelease::true - fi - - - name: Create Prerelease - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' - run: | - set -x - assets=() - for asset in $(find -name *.Dmg); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' - run: | - set -x - assets=() - for asset in $(find -name *.Dmg); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..cd34e8e6c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,167 @@ +name: Release CI + +on: + push: + tags: + - '**' + +jobs: + release-android: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Set up Android SDK License + run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses + + - name: Apply Signing + env: + ALIAS: ${{ secrets.ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }} + run: ./.github/apply_signing.sh + + - name: Build with Gradle + run: ./gradlew :android:assembleRelease :android:bundleRelease + + - name: Archive android artifacts + uses: actions/upload-artifact@v2 + with: + name: android-release + path: | + **/*-release.aab + **/*-release.apk + + release-windows: + runs-on: windows-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Build with Gradle + run: ./gradlew :desktop:packageMsi + + - name: Archive windows artifacts + uses: actions/upload-artifact@v2 + with: + name: windows-release + path: "**/*.msi" + + release-macos: + runs-on: macos-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Build with Gradle + run: ./gradlew :desktop:packageDmg + + - name: Archive macos artifacts + uses: actions/upload-artifact@v2 + with: + name: macos-release + path: "**/*.dmg" + + release-linux: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Build with Gradle + run: ./gradlew :desktop:packageDeb + + - name: Archive linux artifacts + uses: actions/upload-artifact@v2 + with: + name: linux-release + path: "**/*.deb" + + upload-release: + runs-on: ubuntu-latest + needs: [release-android, release-linux, release-macos, release-windows] + timeout-minutes: 30 + steps: + - uses: actions/download-artifact@v2 + + - name: Check if is prelease + if: startsWith(github.ref, 'refs/tags/') + id: check-tag + run: | + if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then + echo ::set-output name=prelease::true + fi + + - name: Create Prerelease + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' + run: | + set -x + assets=() + for asset in $(find -name *.deb); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.msi); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.dmg); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.apk); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.aab); do + assets+=("-a" "$asset") + done + tag_name="${GITHUB_REF##*/}" + hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Release + if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' + run: | + set -x + assets=() + for asset in $(find -name *.deb); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.msi); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.dmg); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.apk); do + assets+=("-a" "$asset") + done + for asset in $(find -name *.aab); do + assets+=("-a" "$asset") + done + tag_name="${GITHUB_REF##*/}" + hub release create "${assets[@]}" -m "$tag_name" "$tag_name" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9f95260baed952f2ca71e43fffbb36af596ce85f Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 22:45:12 +0800 Subject: [PATCH 523/615] fix signing --- .github/apply_signing.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/apply_signing.sh b/.github/apply_signing.sh index cdfc22ab0..06878ad54 100755 --- a/.github/apply_signing.sh +++ b/.github/apply_signing.sh @@ -1,5 +1,5 @@ echo $SIGNING_KEY | base64 -d > key.jks -echo $GOOGLE_SERVICES | base64 -d > app/google-services.json +echo $GOOGLE_SERVICES | base64 -d > android/google-services.json echo "storeFile=key.jks storePassword=$KEY_STORE_PASSWORD keyAlias=$ALIAS From 93581983549f11df75dbf8358eca5856ae65cc71 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 24 Dec 2021 22:46:28 +0800 Subject: [PATCH 524/615] update package to 1.6.0-dev01 --- buildSrc/src/main/kotlin/Package.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index b6c794e70..1ebc2f120 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -2,6 +2,6 @@ object Package { const val group = "com.twidere" const val name = "Twidere X" const val id = "$group.twiderex" - const val versionName = "1.5.1" - const val versionCode = 55 + const val versionName = "1.6.0-dev01" + const val versionCode = 56 } From 9f9720a86cd2688844c8465a108304a7849e6d71 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sat, 25 Dec 2021 11:03:52 +0800 Subject: [PATCH 525/615] update desktop packages --- buildSrc/src/main/kotlin/Package.kt | 14 ++++++++++++-- desktop/build.gradle.kts | 4 +++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index 1ebc2f120..767ddd9bc 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -2,6 +2,16 @@ object Package { const val group = "com.twidere" const val name = "Twidere X" const val id = "$group.twiderex" - const val versionName = "1.6.0-dev01" - const val versionCode = 56 + val versionName = + "${Version.main}.${Version.mirror}.${Version.patch}${if (Version.revision.isNotEmpty()) "-${Version.revision}" else ""}" + const val copyright = "Copyright (C) TwidereProject and Contributors" + const val versionCode = Version.build + + object Version { + const val main = "1" + const val mirror = "6" + const val patch = "0" + const val revision = "dev01" + const val build = 56 + } } diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 72f29d451..02002f141 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -39,7 +39,9 @@ compose { nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = Package.name - packageVersion = Package.versionName.split("-").firstOrNull() + packageVersion = "${Package.Version.main}.${Package.Version.mirror}.${Package.versionCode}" + copyright = Package.copyright + licenseFile.set(rootProject.file("LICENSE")) modules("java.sql") // https://github.com/JetBrains/compose-jb/issues/381 modules("jdk.unsupported") modules("jdk.unsupported.desktop") From 446d382afbf911bc0c9f1c88382754c78e438c70 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Sat, 25 Dec 2021 11:40:46 +0800 Subject: [PATCH 526/615] update ci config --- .github/workflows/release.yml | 67 ++++++----------------------------- 1 file changed, 10 insertions(+), 57 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd34e8e6c..e04987843 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,60 +108,13 @@ jobs: steps: - uses: actions/download-artifact@v2 - - name: Check if is prelease - if: startsWith(github.ref, 'refs/tags/') - id: check-tag - run: | - if [[ ${{ github.event.ref }} =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+\-(dev|beta)[0-9]+$ ]]; then - echo ::set-output name=prelease::true - fi - - - name: Create Prerelease - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease == 'true' - run: | - set -x - assets=() - for asset in $(find -name *.deb); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.msi); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.dmg); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.apk); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.aab); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" -p - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Release - if: startsWith(github.ref, 'refs/tags/') && steps.check-tag.outputs.prelease != 'true' - run: | - set -x - assets=() - for asset in $(find -name *.deb); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.msi); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.dmg); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.apk); do - assets+=("-a" "$asset") - done - for asset in $(find -name *.aab); do - assets+=("-a" "$asset") - done - tag_name="${GITHUB_REF##*/}" - hub release create "${assets[@]}" -m "$tag_name" "$tag_name" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Draft a new release + uses: softprops/action-gh-release@v1 + with: + draft: true + files: | + **/*.deb + **/*.msi + **/*.dmg + **/*.apk + **/*.aab From 0fab2822464fdca6c7c80b53917c7ea32d2f8a4d Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 27 Dec 2021 10:39:02 +0800 Subject: [PATCH 527/615] wip image compress --- buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 2 +- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 2 ++ .../twiderex/jobs/compose/ComposeJob.kt | 7 ++++++- .../jobs/compose/MastodonComposeJob.kt | 4 ++++ .../jobs/compose/TwitterComposeJob.kt | 19 +++++++++++++++++++ .../twidere/twiderex/kmp/ImageCompressor.kt | 5 ----- 7 files changed, 33 insertions(+), 7 deletions(-) delete mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index e794973d4..c63c968b1 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -49,4 +49,5 @@ object Versions { const val moko = "0.17.2" const val sqlDelight = "1.5.1" const val javafx = "0.0.10" + const val kFilePicker = "1.0.4" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 40219ec3d..f56fa423e 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -66,7 +66,7 @@ kotlin { implementation("app.cash.turbine:turbine:0.6.1") implementation("ca.gosyer:accompanist-pager:${Versions.accompanist_jb}") implementation("ca.gosyer:accompanist-pager-indicators:${Versions.accompanist_jb}") - api("com.github.Tlaster.KFilePicker:KFilePicker:1.0.2") + api("com.github.Tlaster.KFilePicker:KFilePicker:${Versions.kFilePicker}") } } val commonTest by getting { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 4ffc0c228..8406a7206 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -41,6 +41,7 @@ actual class ExifScrambler(private val context: Context) { } catch (oom: OutOfMemoryError) { return file } + println("exif ==> file:$uri, size:${(contentResolver.openFileDescriptor(uri, "r")?.statSize ?: 0) /(1024.0 * 1024)}, compress$compress") // create an cache image val mimeType = contentResolver.getType(uri) ?: "" val imageType = getImageType(mimeType) @@ -72,6 +73,7 @@ actual class ExifScrambler(private val context: Context) { return uri.toString() } } + println("exif ==> imageCache:${imageCache.absolutePath}, size:${imageCache.length() /(1024.0 * 1024)}") return imageCache.toUri().toString() } return uri.toString() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index ad9b6e079..81d9d2d92 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -63,7 +63,8 @@ abstract class ComposeJob( val mediaIds = arrayListOf() val images = composeData.images images.forEachIndexed { index, uri -> - val scramblerUri = exifScrambler.removeExifData(uri) + val scramblerUri = exifScrambler.removeExifData(uri, compress = imageCompression(uri)) + if (true)throw Error("") val id = uploadImage(uri, scramblerUri, service) id?.let { mediaIds.add(it) } builder.setProgress( @@ -114,4 +115,8 @@ abstract class ComposeJob( scramblerUri: String, service: T ): String? + + protected abstract suspend fun imageCompression( + file: String, + ): Int } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index 92b6dc7e2..620c57ce9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -100,4 +100,8 @@ class MastodonComposeJob( } ?: throw Error() return id.id } + + override suspend fun imageCompression(file: String): Int { + return 100 + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index 4f30f5057..8a76d86f5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -51,6 +51,10 @@ class TwitterComposeJob constructor( remoteNavigator, resLoader, ) { + companion object { + private const val MaxImageSize = 3 * 1024 * 1024 + } + override suspend fun compose( service: TwitterService, composeData: ComposeData, @@ -98,4 +102,19 @@ class TwitterComposeJob constructor( ) } ?: throw Error() } + + override suspend fun imageCompression(file: String): Int { + println("exif ==> getImage Compression:$file") + return fileResolver.getFileSize(file)?.let { + println("exif ==> origin size: $it, maxImageSize: $MaxImageSize") + if (it > MaxImageSize) { + (MaxImageSize/it.toFloat()) * 100 + } else { + 100 + }.toInt().apply { + println("exif ==> compress size: $this") + } + } ?: 100 + + } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt deleted file mode 100644 index ec4879753..000000000 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ImageCompressor.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.twidere.twiderex.kmp - -expect class ImageCompressor { - fun compress(file:String, targetSize:Long):String -} \ No newline at end of file From 80d14face4398fb9b1bc98462faf87c3a44d04fc Mon Sep 17 00:00:00 2001 From: itsMimao Date: Mon, 27 Dec 2021 11:05:33 +0800 Subject: [PATCH 528/615] moved sqldelight dependencies from androidMain to androidTest/androidAndroidTest --- common/build.gradle.kts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 4ba075537..477e0b428 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -91,8 +91,6 @@ kotlin { implementation("io.coil-kt:coil-svg:${Versions.coil}") implementation("com.google.android.exoplayer:exoplayer:${Versions.exoplayer}") implementation("com.google.android.exoplayer:extension-okhttp:${Versions.exoplayer}") - implementation("com.squareup.sqldelight:android-driver:${Versions.sqlDelight}") - implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") implementation("androidx.datastore:datastore:${Versions.datastore}") implementation("androidx.datastore:datastore-preferences:${Versions.datastore}") implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") @@ -112,9 +110,14 @@ kotlin { implementation("androidx.test.ext:junit-ktx:${Versions.extJUnitVersion}") implementation("androidx.test.espresso:espresso-core:${Versions.espressoVersion}") implementation("androidx.room:room-testing:${Versions.room}") + implementation("com.squareup.sqldelight:android-driver:${Versions.sqlDelight}") + } + } + val androidTest by getting { + dependencies { + implementation("com.squareup.sqldelight:sqlite-driver:${Versions.sqlDelight}") } } - val androidTest by getting val desktopMain by getting { dependencies { implementation("uk.co.caprica:vlcj:4.7.1") From b2bc507c4ca9a36838f691a0312f87a9f71d9298 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Mon, 27 Dec 2021 16:57:02 +0800 Subject: [PATCH 529/615] append content if share link --- .../twidere/twiderex/component/status/StatusActions.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index bcafd7332..006080d12 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -334,7 +334,13 @@ fun ShareButton( DropdownMenuItem( onClick = { expanded = false - remoteNavigator.shareText(status.generateShareLink()) + remoteNavigator.shareText( + buildString { + append(text) + append("\n\n") + append(status.generateShareLink()) + } + ) } ) { Text( From dcb3a12a53e1fe1f115acf7e112b515c188945a8 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Mon, 27 Dec 2021 20:08:35 +0800 Subject: [PATCH 530/615] apply spotless --- .../platform/PlatformEmojiPanel.android.kt | 2 +- .../foundation/platform/platformEmojiPanel.kt | 2 +- .../component/lazy/ui/LazyUiDMEventList.kt | 14 ++++++-- .../com/twidere/twiderex/navigation/IRoute.kt | 2 +- .../twiderex/repository/NitterRepository.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 4 +-- .../viewmodel/compose/ComposeViewModel.kt | 32 +++++++++---------- .../platform/PlatformEmojiPanel.desktop.kt | 2 +- .../twidere/services/nitter/model/Profile.kt | 2 +- 9 files changed, 36 insertions(+), 26 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt index 7f2c66643..b9f6501aa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt index 08c72fe77..413af5877 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 786e312c0..0f91aee2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -131,7 +131,11 @@ private object LazyUiDMEventListDefaults { } @Composable -private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> Unit) { +private fun DMOutComeEvent( + onResend: (event: UiDMEvent) -> Unit = {}, + event: UiDMEvent, + onItemLongClick: (event: UiDMEvent) -> Unit +) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { Column(horizontalAlignment = Alignment.End) { Row(verticalAlignment = Alignment.CenterVertically) { @@ -178,6 +182,7 @@ private object DMOutComeEventDefaults { val size = 24.dp val width = 2.dp } + object Error { val size = 24.dp val ContentPadding = PaddingValues(3.dp) @@ -206,6 +211,7 @@ private fun DMInComeEvent(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) private object DMEventDefaults { val ContentSpacing = 10.dp + object Time { val paddingTop = 8.dp val paddingStart = UserAvatarDefaults.AvatarSize + ContentSpacing @@ -246,7 +252,11 @@ private fun MessageBody(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> navController.navigate(Root.Media.Pure(event.messageKey, 0)) } ) - if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer(modifier = Modifier.height(MessageBodyDefaults.ContentSpacing)) + if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer( + modifier = Modifier.height( + MessageBodyDefaults.ContentSpacing + ) + ) val textColor = if (event.isInCome) MaterialTheme.colors.onSurface else MaterialTheme.colors.onPrimary val textStyle = MaterialTheme.typography.body1.copy(textColor) val linkStyle = textStyle.copy( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt index d5ebd5986..d71d2ea99 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt index 7121f3ddd..8960d778e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 590fede14..2be478ab3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -452,8 +452,8 @@ fun NitterUsageDialog( onDismissRequest.invoke() } }, enabled = isValid) { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) - } + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) + } }, dismissButton = { TextButton( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 16706d6c5..5ed6360e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -216,24 +216,24 @@ open class ComposeViewModel( val replyToUser by lazy { combine(account.mapNotNull { it }, replyToUserName) { account, list -> - if (list.isNotEmpty()) { - loadingReplyUser.value = true - try { - userRepository.lookupUsersByName( - list, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - } catch (e: Throwable) { - inAppNotification.notifyError(e) + if (list.isNotEmpty()) { + loadingReplyUser.value = true + try { + userRepository.lookupUsersByName( + list, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + emptyList() + } finally { + loadingReplyUser.value = false + } + } else { emptyList() - } finally { - loadingReplyUser.value = false } - } else { - emptyList() - } - }.asStateIn(viewModelScope, emptyList()) + }.asStateIn(viewModelScope, emptyList()) } val voteState = MutableStateFlow(null) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt index 2d31a4987..68cca0676 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt index 0449c046c..ece9e77b6 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * From 41f23a4a176b1345c9d0c696805c7f858d399569 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Mon, 27 Dec 2021 20:20:05 +0800 Subject: [PATCH 531/615] append content if share media --- .../com/twidere/twiderex/action/MediaAction.kt | 5 +++-- .../twiderex/extensions/ContextExtensions.kt | 5 ++++- .../com/twidere/twiderex/kmp/RemoteNavigator.kt | 8 +++++++- .../twidere/twiderex/worker/ShareMediaWorker.kt | 7 ++++--- .../com/twidere/twiderex/action/MediaAction.kt | 2 +- .../twidere/twiderex/jobs/common/ShareMediaJob.kt | 5 +++-- .../com/twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- .../com/twidere/twiderex/scenes/MediaScene.kt | 15 ++++++++++++++- .../twidere/twiderex/viewmodel/MediaViewModel.kt | 5 +++-- .../com/twidere/twiderex/action/MediaAction.kt | 4 ++-- .../com/twidere/twiderex/kmp/RemoteNavigator.kt | 3 ++- 11 files changed, 44 insertions(+), 17 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index 8286fe231..fe49c81f7 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -48,7 +48,7 @@ actual class MediaAction( ) } - actual fun share(source: String, fileName: String, accountKey: MicroBlogKey) { + actual fun share(source: String, fileName: String, accountKey: MicroBlogKey, extraText: String) { val uri = storageProvider.cacheFiles.mediaFile(fileName).toUri(context) DownloadMediaWorker.create( accountKey = accountKey, @@ -58,7 +58,8 @@ actual class MediaAction( workManager.beginWith(it) .then( ShareMediaWorker.create( - target = uri + target = uri, + extraText = extraText, ) ).enqueue() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt index a9eb3d12f..270623aa8 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/extensions/ContextExtensions.kt @@ -50,13 +50,16 @@ fun Context.shareText(content: String) { ) } -fun Context.shareMedia(uri: Uri, mimeType: String) { +fun Context.shareMedia(uri: Uri, mimeType: String, extraText: String) { startActivity( Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_STREAM, uri) type = mimeType addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + if (extraText.isNotEmpty()) { + putExtra(Intent.EXTRA_TEXT, extraText) + } }.let { Intent.createChooser(it, null).apply { if (this@shareMedia !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index fe8c11cb5..5219c98dd 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -39,10 +39,16 @@ actual class RemoteNavigator(private val context: Context) { ) } - actual fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean) { + actual fun shareMedia( + filePath: String, + mimeType: String, + fromBackground: Boolean, + extraText: String, + ) { context.shareMedia( uri = Uri.parse(filePath), mimeType = mimeType, + extraText = extraText, ) } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt index 1f871649a..6952bafef 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/worker/ShareMediaWorker.kt @@ -37,10 +37,12 @@ class ShareMediaWorker( companion object { fun create( target: Uri, + extraText: String = "", ) = OneTimeWorkRequestBuilder() .setInputData( Data.Builder() .putString("target", target.toString()) + .putString("extraText", extraText) .build() ) .build() @@ -48,10 +50,9 @@ class ShareMediaWorker( override suspend fun doWork(): Result { val target = inputData.getString("target") ?: return Result.failure() + val extraText = inputData.getString("extraText").orEmpty() return try { - shareMediaJob.execute( - target - ) + shareMediaJob.execute(target, extraText) Result.success() } catch (e: Throwable) { e.printStackTrace() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index 3b199c556..bee4f8917 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -24,5 +24,5 @@ import com.twidere.twiderex.model.MicroBlogKey expect class MediaAction { fun download(source: String, target: String, accountKey: MicroBlogKey) - fun share(source: String, fileName: String, accountKey: MicroBlogKey) + fun share(source: String, fileName: String, accountKey: MicroBlogKey, extraText: String = "") } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt index 7da420779..4c0e09eda 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/common/ShareMediaJob.kt @@ -27,12 +27,13 @@ class ShareMediaJob( private val fileResolver: FileResolver, private val remoteNavigator: RemoteNavigator ) { - fun execute(target: String) { + fun execute(target: String, extraText: String) { fileResolver.getMimeType(target)?.let { type -> remoteNavigator.shareMedia( filePath = target, mimeType = type, - fromBackground = true + fromBackground = true, + extraText = extraText, ) true } ?: throw Error("Unresolved file:$target") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index a89aa5740..937b9b59d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.staticCompositionLocalOf expect class RemoteNavigator { fun openDeepLink(deeplink: String, fromBackground: Boolean = false) - fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean = false) + fun shareMedia(filePath: String, mimeType: String, fromBackground: Boolean = false, extraText: String = "") fun shareText(content: String, fromBackground: Boolean = false) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index fabe89739..033e29245 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -61,6 +61,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.ExperimentalUnitApi import androidx.compose.ui.unit.dp import com.mxalbert.zoomable.Zoomable import com.twidere.twiderex.component.bottomInsetsHeight @@ -86,6 +87,8 @@ import com.twidere.twiderex.component.status.StatusText import com.twidere.twiderex.component.status.UserAvatar import com.twidere.twiderex.component.status.UserName import com.twidere.twiderex.component.status.UserScreenName +import com.twidere.twiderex.component.status.renderContentAnnotatedString +import com.twidere.twiderex.component.status.resolveLink import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.component.topInsetsPadding import com.twidere.twiderex.di.ext.getViewModel @@ -329,6 +332,7 @@ private fun StatusMediaBottomContent( } } +@OptIn(ExperimentalUnitApi::class) @Composable private fun StatusMediaInfo( videoPlayerState: VideoPlayerState?, @@ -337,6 +341,10 @@ private fun StatusMediaInfo( currentMedia: UiMedia, ) { val scope = rememberCoroutineScope() + val text = renderContentAnnotatedString( + htmlText = status.htmlText, + linkResolver = { status.resolveLink(it) }, + ) Column( modifier = Modifier .padding(StatusMediaInfoDefaults.ContentPadding), @@ -384,7 +392,12 @@ private fun StatusMediaInfo( currentMedia.fileName?.let { scope.launch { viewModel.shareMedia( - currentMedia = currentMedia + currentMedia = currentMedia, + extraText = buildString { + append(text) + append("\n\n") + append(status.generateShareLink()) + } ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 8944e950d..10f83a718 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -55,14 +55,15 @@ class MediaViewModel( } } - suspend fun shareMedia(currentMedia: UiMedia) { + suspend fun shareMedia(currentMedia: UiMedia, extraText: String) { val account = account.firstOrNull() ?: return currentMedia.mediaUrl?.let { mediaUrl -> currentMedia.fileName?.let { fileName -> mediaAction.share( source = mediaUrl, fileName = fileName, - accountKey = account.accountKey + accountKey = account.accountKey, + extraText = extraText ) } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt index ea9a317dd..2151394c5 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/action/MediaAction.kt @@ -48,7 +48,7 @@ actual class MediaAction( } } - actual fun share(source: String, fileName: String, accountKey: MicroBlogKey) { + actual fun share(source: String, fileName: String, accountKey: MicroBlogKey, extraText: String) { scope.launchCatching { val f = File(URI(source)) val target = File.createTempFile( @@ -66,7 +66,7 @@ actual class MediaAction( source = source, accountKey = accountKey, ) - shareMediaJob.execute(target) + shareMediaJob.execute(target, extraText) } } } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index c8ad8cf0d..8e48f5113 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -31,7 +31,8 @@ actual class RemoteNavigator { actual fun shareMedia( filePath: String, mimeType: String, - fromBackground: Boolean + fromBackground: Boolean, + extraText: String, ) { // TODO: Show native UI for sharing } From e053ba2eda5cc435b96d68289959a3842b6e1fb7 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Mon, 27 Dec 2021 20:52:28 +0800 Subject: [PATCH 532/615] fixed crashes caused by openDeepLink --- .../kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt index 5219c98dd..890338f67 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/RemoteNavigator.kt @@ -34,7 +34,7 @@ actual class RemoteNavigator(private val context: Context) { Intent.ACTION_VIEW, Uri.parse(deeplink).normalizeScheme() ).apply { - if (fromBackground) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } ) } From 26cc8afbd8fd3335aabc2d6c5a499aa9dac53591 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 28 Dec 2021 11:25:12 +0800 Subject: [PATCH 533/615] add share with content to display settings --- .../twiderex/component/status/StatusActions.kt | 14 ++++++++++---- .../preferences/model/DisplayPreferences.kt | 3 ++- .../com/twidere/twiderex/scenes/MediaScene.kt | 14 +++++++++----- .../twiderex/scenes/settings/DisplayScene.kt | 13 +++++++++++++ .../twidere/twiderex/viewmodel/MediaViewModel.kt | 2 +- .../viewmodel/settings/DisplayViewModel.kt | 6 ++++++ .../src/commonMain/resources/MR/base/strings.xml | 3 +++ localization | 2 +- 8 files changed, 45 insertions(+), 12 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 006080d12..eb0b90f6c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -65,6 +65,7 @@ import com.twidere.twiderex.kmp.LocalRemoteNavigator import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus +import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.ui.LocalActiveAccount private val rippleSize = 24.dp @@ -259,6 +260,7 @@ fun ShareButton( ) val clipboardManager = LocalClipboardManager.current val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_more) + val shareWithContent = LocalDisplayPreferences.current.shareWithContent Box( modifier = modifier, ) { @@ -335,10 +337,14 @@ fun ShareButton( onClick = { expanded = false remoteNavigator.shareText( - buildString { - append(text) - append("\n\n") - append(status.generateShareLink()) + if (shareWithContent) { + buildString { + append(text) + append("\n\n") + append(status.generateShareLink()) + } + } else { + status.generateShareLink() } ) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt index 70120d2a2..bf65242d6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt @@ -30,7 +30,8 @@ data class DisplayPreferences( val mediaPreview: Boolean = true, val autoPlayback: AutoPlayback = AutoPlayback.Auto, val urlPreview: Boolean = false, - val muteByDefault: Boolean = false + val muteByDefault: Boolean = false, + val shareWithContent: Boolean = false, ) { @Serializable enum class AvatarStyle { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 033e29245..73a36deeb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -341,6 +341,8 @@ private fun StatusMediaInfo( currentMedia: UiMedia, ) { val scope = rememberCoroutineScope() + + val shareWithContent = LocalDisplayPreferences.current.shareWithContent val text = renderContentAnnotatedString( htmlText = status.htmlText, linkResolver = { status.resolveLink(it) }, @@ -393,11 +395,13 @@ private fun StatusMediaInfo( scope.launch { viewModel.shareMedia( currentMedia = currentMedia, - extraText = buildString { - append(text) - append("\n\n") - append(status.generateShareLink()) - } + extraText = if (shareWithContent) { + buildString { + append(text) + append("\n\n") + append(status.generateShareLink()) + } + } else "" ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index a064c9070..a7762aae5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -218,6 +218,19 @@ fun DisplayScene() { } ) } + ItemDivider() + ItemHeader() { + Text(text = stringResource(com.twidere.twiderex.MR.strings.common_controls_status_actions_share)) + } + switchItem( + value = display.shareWithContent, + onChanged = { + viewModel.setShareWithContent(it) + }, + title = { + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_share_with_content)) + } + ) } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt index 10f83a718..d2da85faa 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/MediaViewModel.kt @@ -55,7 +55,7 @@ class MediaViewModel( } } - suspend fun shareMedia(currentMedia: UiMedia, extraText: String) { + suspend fun shareMedia(currentMedia: UiMedia, extraText: String = "") { val account = account.firstOrNull() ?: return currentMedia.mediaUrl?.let { mediaUrl -> currentMedia.fileName?.let { fileName -> diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index 8112c7913..8e77ecab9 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -70,4 +70,10 @@ class DisplayViewModel( it.copy(muteByDefault = value) } } + + fun setShareWithContent(value: Boolean) = viewModelScope.launch { + displayPreferences.updateData { + it.copy(shareWithContent = value) + } + } } diff --git a/common/src/commonMain/resources/MR/base/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml index 65e3a0ea1..51310a4e6 100644 --- a/common/src/commonMain/resources/MR/base/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -120,6 +120,7 @@ Listed Following Load More + No results Photo Library Add Cancel @@ -301,6 +302,7 @@ Theme Scrolling timeline Thanks for using @TwidereProject! + Share link (media) and content Url previews Absolute Relative @@ -362,6 +364,7 @@ Me Permission Denied You have been blocked from viewing this user’s profile. + Joined in %s Manage accounts Sign in Drafts diff --git a/localization b/localization index 53bd6cacc..c0bae6988 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 53bd6caccf213766751460de4170be33a5264b8e +Subproject commit c0bae69882c69b65e70725803df28bb255da1858 From 542d0b4490d6ee2ffc77004279d0486b569402ee Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 14:37:22 +0800 Subject: [PATCH 534/615] add compress image before compose to Android --- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 96 ++++++++++++------- .../twiderex/jobs/compose/ComposeJob.kt | 7 +- .../jobs/compose/MastodonComposeJob.kt | 5 +- .../jobs/compose/TwitterComposeJob.kt | 20 +--- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 2 +- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 10 +- 6 files changed, 71 insertions(+), 69 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 96c9bd3b0..d64498cbe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -20,65 +20,87 @@ */ package com.twidere.twiderex.kmp +import android.content.ContentResolver import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import androidx.core.net.toUri import androidx.exifinterface.media.ExifInterface +import com.twidere.twiderex.di.ext.get +import java.io.ByteArrayOutputStream import java.io.File +import java.io.FileOutputStream import java.util.UUID actual class ExifScrambler(private val context: Context) { - actual fun removeExifData(file: String, compress: Int): String { - // first get input stream + + actual fun removeExifData(file: String, maxImageSize: Long): String { val uri = Uri.parse(file) val contentResolver = context.contentResolver - contentResolver.openInputStream(uri)?.use { input -> - // decode to bitmap because bitmap won't store exif meta data - val bitmap = try { - BitmapFactory.decodeStream(input) - } catch (oom: OutOfMemoryError) { - return file - } - println("exif ==> file:$uri, size:${(contentResolver.openFileDescriptor(uri, "r")?.statSize ?: 0) /(1024.0 * 1024)}, compress$compress") - // create an cache image - val mimeType = contentResolver.getType(uri) ?: "" - val imageType = getImageType(mimeType) - val imageCache = File(context.externalCacheDir, "${UUID.randomUUID()}.${imageType.name.lowercase()}") - if (!imageCache.exists()) imageCache.createNewFile() - // write to disk without exif meta data - when (imageType) { - ImageType.JPG -> { - val originExif = ExifInterface(input) - imageCache.outputStream().use { - bitmap.compress(Bitmap.CompressFormat.JPEG, compress, it) - it.flush() + try { + contentResolver.openInputStream(uri)?.use { input -> + // create an cache image + val mimeType = contentResolver.getType(uri) ?: "" + val imageType = getImageType(mimeType) + val imageCache = File(get().appFiles.mediaDir, "${UUID.randomUUID()}.${imageType.name.lowercase()}") + if (!imageCache.exists()) imageCache.createNewFile() + // write to disk without exif meta data + when (imageType) { + ImageType.JPG -> { + imageCache.outputStream().use { + compressImage(contentResolver, uri, maxImageSize, it) + } + val originExif = ExifInterface(input) + // keep origin images orientation + originExif.getAttribute(ExifInterface.TAG_ORIENTATION)?.let { + ExifInterface(imageCache.absolutePath).apply { + setAttribute(ExifInterface.TAG_ORIENTATION, it) + saveAttributes() + } + } } - // keep origin images orientation - originExif.getAttribute(ExifInterface.TAG_ORIENTATION)?.let { - ExifInterface(imageCache.absolutePath).apply { - setAttribute(ExifInterface.TAG_ORIENTATION, it) - saveAttributes() + ImageType.PNG -> { + imageCache.outputStream().use { + compressImage(contentResolver, uri, maxImageSize, it) } } - } - ImageType.PNG -> { - imageCache.outputStream().use { - bitmap.compress(Bitmap.CompressFormat.PNG, compress, it) - it.flush() + ImageType.UNKNOWN -> { + return uri.toString() } } - ImageType.UNKNOWN -> { - return uri.toString() - } + return imageCache.toUri().toString() } - println("exif ==> imageCache:${imageCache.absolutePath}, size:${imageCache.length() /(1024.0 * 1024)}") - return imageCache.toUri().toString() + } catch (e: Exception) { + e.printStackTrace() } return uri.toString() } + private fun compressImage(contentResolver: ContentResolver, uri: Uri, maxImageSize: Long, fos: FileOutputStream) { + contentResolver.openInputStream(uri)?.use { + val bitmap = try { + BitmapFactory.decodeStream(it) + } catch (oom: OutOfMemoryError) { + throw oom + } + var currSize: Int + var currQuality = 100 + val stream = ByteArrayOutputStream() + do { + stream.flush() + stream.reset() + bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream) + currSize = stream.toByteArray().size + currQuality -= 5 + } while (currSize >= maxImageSize && currQuality >= 80 ) + stream.toByteArray() + }?.apply{ + fos.write(this) + fos.flush() + } ?: throw Error("Failed to open input stream") + } + actual fun deleteCacheFile(file: String) { Uri.parse(file).path?.let { File(it) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt index 23bc4c88d..2b04fdd5a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/ComposeJob.kt @@ -63,8 +63,7 @@ abstract class ComposeJob( val mediaIds = arrayListOf() val images = composeData.images images.forEachIndexed { index, uri -> - val scramblerUri = exifScrambler.removeExifData(uri, compress = imageCompression(uri)) - if (true)throw Error("") + val scramblerUri = exifScrambler.removeExifData(uri, imageMaxSize) val id = uploadImage(uri, scramblerUri, service) id?.let { mediaIds.add(it) } builder.setProgress( @@ -116,7 +115,5 @@ abstract class ComposeJob( service: T ): String? - protected abstract suspend fun imageCompression( - file: String, - ): Int + protected abstract val imageMaxSize: Long } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt index f0753aac9..fc8c26a65 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/MastodonComposeJob.kt @@ -101,7 +101,6 @@ class MastodonComposeJob( return id.id } - override suspend fun imageCompression(file: String): Int { - return 100 - } + override val imageMaxSize: Long + get() = 100 * 1024 * 1024 } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt index ba2de6ace..05b808f5a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/jobs/compose/TwitterComposeJob.kt @@ -51,10 +51,6 @@ class TwitterComposeJob constructor( remoteNavigator, resLoader, ) { - companion object { - private const val MaxImageSize = 3 * 1024 * 1024 - } - override suspend fun compose( service: TwitterService, composeData: ComposeData, @@ -103,18 +99,6 @@ class TwitterComposeJob constructor( } ?: throw Error() } - override suspend fun imageCompression(file: String): Int { - println("exif ==> getImage Compression:$file") - return fileResolver.getFileSize(file)?.let { - println("exif ==> origin size: $it, maxImageSize: $MaxImageSize") - if (it > MaxImageSize) { - (MaxImageSize/it.toFloat()) * 100 - } else { - 100 - }.toInt().apply { - println("exif ==> compress size: $this") - } - } ?: 100 - - } + override val imageMaxSize: Long + get() = 5 * 1024 * 1024 // https://help.twitter.com/en/using-twitter/tweeting-gifs-and-pictures } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 6aed04741..3d0437173 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -21,7 +21,7 @@ package com.twidere.twiderex.kmp expect class ExifScrambler { - fun removeExifData(file: String, compress: Int = 100): String + fun removeExifData(file: String, maxImageSize: Long): String fun deleteCacheFile(file: String) } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 3b4d79a2c..9eaecd40c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -24,15 +24,15 @@ import java.io.File import javax.imageio.ImageIO actual class ExifScrambler { - actual fun removeExifData(file: String, compress: Int): String { + actual fun deleteCacheFile(file: String) { + File(file).takeIf { it.exists() && it.isFile }?.delete() + } + + actual fun removeExifData(file: String, maxImageSize: Long): String { val imgFile = File(file) val image = ImageIO.read(imgFile) val result = File.createTempFile(imgFile.name, null) ImageIO.write(image, imgFile.extension, result) return result.absolutePath } - - actual fun deleteCacheFile(file: String) { - File(file).takeIf { it.exists() && it.isFile }?.delete() - } } From a41c879c78e2b6e214b0af60f8f8b8ae19e978f1 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 14:46:52 +0800 Subject: [PATCH 535/615] format code --- .../platform/PlatformEmojiPanel.android.kt | 2 +- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 8 ++--- .../foundation/platform/platformEmojiPanel.kt | 2 +- .../component/lazy/ui/LazyUiDMEventList.kt | 14 ++++++-- .../com/twidere/twiderex/navigation/IRoute.kt | 2 +- .../twiderex/repository/NitterRepository.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 4 +-- .../viewmodel/compose/ComposeViewModel.kt | 32 +++++++++---------- .../platform/PlatformEmojiPanel.desktop.kt | 2 +- .../twidere/services/nitter/model/Profile.kt | 2 +- 10 files changed, 40 insertions(+), 30 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt index 7f2c66643..b9f6501aa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index d64498cbe..af8e4e48f 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -62,7 +62,7 @@ actual class ExifScrambler(private val context: Context) { } ImageType.PNG -> { imageCache.outputStream().use { - compressImage(contentResolver, uri, maxImageSize, it) + compressImage(contentResolver, uri, maxImageSize, it) } } ImageType.UNKNOWN -> { @@ -77,7 +77,7 @@ actual class ExifScrambler(private val context: Context) { return uri.toString() } - private fun compressImage(contentResolver: ContentResolver, uri: Uri, maxImageSize: Long, fos: FileOutputStream) { + private fun compressImage(contentResolver: ContentResolver, uri: Uri, maxImageSize: Long, fos: FileOutputStream) { contentResolver.openInputStream(uri)?.use { val bitmap = try { BitmapFactory.decodeStream(it) @@ -93,9 +93,9 @@ actual class ExifScrambler(private val context: Context) { bitmap.compress(Bitmap.CompressFormat.JPEG, currQuality, stream) currSize = stream.toByteArray().size currQuality -= 5 - } while (currSize >= maxImageSize && currQuality >= 80 ) + } while (currSize >= maxImageSize && currQuality >= 80) stream.toByteArray() - }?.apply{ + }?.apply { fos.write(this) fos.flush() } ?: throw Error("Failed to open input stream") diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt index 08c72fe77..413af5877 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 786e312c0..0f91aee2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -131,7 +131,11 @@ private object LazyUiDMEventListDefaults { } @Composable -private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> Unit) { +private fun DMOutComeEvent( + onResend: (event: UiDMEvent) -> Unit = {}, + event: UiDMEvent, + onItemLongClick: (event: UiDMEvent) -> Unit +) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { Column(horizontalAlignment = Alignment.End) { Row(verticalAlignment = Alignment.CenterVertically) { @@ -178,6 +182,7 @@ private object DMOutComeEventDefaults { val size = 24.dp val width = 2.dp } + object Error { val size = 24.dp val ContentPadding = PaddingValues(3.dp) @@ -206,6 +211,7 @@ private fun DMInComeEvent(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) private object DMEventDefaults { val ContentSpacing = 10.dp + object Time { val paddingTop = 8.dp val paddingStart = UserAvatarDefaults.AvatarSize + ContentSpacing @@ -246,7 +252,11 @@ private fun MessageBody(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> navController.navigate(Root.Media.Pure(event.messageKey, 0)) } ) - if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer(modifier = Modifier.height(MessageBodyDefaults.ContentSpacing)) + if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer( + modifier = Modifier.height( + MessageBodyDefaults.ContentSpacing + ) + ) val textColor = if (event.isInCome) MaterialTheme.colors.onSurface else MaterialTheme.colors.onPrimary val textStyle = MaterialTheme.typography.body1.copy(textColor) val linkStyle = textStyle.copy( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt index d5ebd5986..d71d2ea99 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt index 7121f3ddd..8960d778e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 590fede14..2be478ab3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -452,8 +452,8 @@ fun NitterUsageDialog( onDismissRequest.invoke() } }, enabled = isValid) { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) - } + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) + } }, dismissButton = { TextButton( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 16706d6c5..5ed6360e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -216,24 +216,24 @@ open class ComposeViewModel( val replyToUser by lazy { combine(account.mapNotNull { it }, replyToUserName) { account, list -> - if (list.isNotEmpty()) { - loadingReplyUser.value = true - try { - userRepository.lookupUsersByName( - list, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - } catch (e: Throwable) { - inAppNotification.notifyError(e) + if (list.isNotEmpty()) { + loadingReplyUser.value = true + try { + userRepository.lookupUsersByName( + list, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + emptyList() + } finally { + loadingReplyUser.value = false + } + } else { emptyList() - } finally { - loadingReplyUser.value = false } - } else { - emptyList() - } - }.asStateIn(viewModelScope, emptyList()) + }.asStateIn(viewModelScope, emptyList()) } val voteState = MutableStateFlow(null) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt index 2d31a4987..68cca0676 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt index 0449c046c..ece9e77b6 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * From a24743eb6f135a64889f97cb164c510d8c5d87c4 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 15:17:19 +0800 Subject: [PATCH 536/615] add spotless check to ci --- .github/workflows/android.yml | 2 +- .github/workflows/common.yml | 19 +++++++++++ .github/workflows/desktop.yml | 14 ++++++++ android/src/main/AndroidManifest.xml | 26 --------------- common/src/androidMain/AndroidManifest.xml | 31 +++++++++++++++++- .../platform/PlatformEmojiPanel.android.kt | 2 +- .../foundation/platform/platformEmojiPanel.kt | 2 +- .../component/lazy/ui/LazyUiDMEventList.kt | 14 ++++++-- .../com/twidere/twiderex/navigation/IRoute.kt | 2 +- .../twiderex/repository/NitterRepository.kt | 2 +- .../twiderex/scenes/settings/MiscScene.kt | 4 +-- .../viewmodel/compose/ComposeViewModel.kt | 32 +++++++++---------- .../platform/PlatformEmojiPanel.desktop.kt | 2 +- .../twidere/services/nitter/model/Profile.kt | 2 +- 14 files changed, 100 insertions(+), 54 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 93e356bff..5ebd0a193 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -43,7 +43,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: build-mapping - path: ./app/build/outputs/mapping + path: ./android/build/outputs/mapping unit-test: runs-on: ubuntu-latest diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 305009913..2952e2a33 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -16,7 +16,26 @@ concurrency: cancel-in-progress: true jobs: + spotless-check: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Set up Android SDK License + run: yes | /usr/local/lib/android/sdk/tools/bin/sdkmanager --licenses + + - name: spotlessCheck with Gradle + run: ./gradlew :common:spotlessCheck :common:lint + + unit-test: + needs: spotless-check runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index 9c84c5599..991cfef1c 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -16,6 +16,20 @@ concurrency: cancel-in-progress: true jobs: + spotless-check: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: spotlessCheck with Gradle + run: ./gradlew :desktop:spotlessCheck + build-linux: runs-on: ubuntu-latest timeout-minutes: 30 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index e13cbc18d..bea54d93f 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -22,32 +22,6 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.TwidereX"> - - - - - - - - + @@ -9,4 +10,32 @@ + + + + + + + + + \ No newline at end of file diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt index 7f2c66643..b9f6501aa 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt index 08c72fe77..413af5877 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/platform/platformEmojiPanel.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt index 786e312c0..0f91aee2d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiDMEventList.kt @@ -131,7 +131,11 @@ private object LazyUiDMEventListDefaults { } @Composable -private fun DMOutComeEvent(onResend: (event: UiDMEvent) -> Unit = {}, event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> Unit) { +private fun DMOutComeEvent( + onResend: (event: UiDMEvent) -> Unit = {}, + event: UiDMEvent, + onItemLongClick: (event: UiDMEvent) -> Unit +) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { Column(horizontalAlignment = Alignment.End) { Row(verticalAlignment = Alignment.CenterVertically) { @@ -178,6 +182,7 @@ private object DMOutComeEventDefaults { val size = 24.dp val width = 2.dp } + object Error { val size = 24.dp val ContentPadding = PaddingValues(3.dp) @@ -206,6 +211,7 @@ private fun DMInComeEvent(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) private object DMEventDefaults { val ContentSpacing = 10.dp + object Time { val paddingTop = 8.dp val paddingStart = UserAvatarDefaults.AvatarSize + ContentSpacing @@ -246,7 +252,11 @@ private fun MessageBody(event: UiDMEvent, onItemLongClick: (event: UiDMEvent) -> navController.navigate(Root.Media.Pure(event.messageKey, 0)) } ) - if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer(modifier = Modifier.height(MessageBodyDefaults.ContentSpacing)) + if (event.media.isNotEmpty() && event.htmlText.isNotEmpty()) Spacer( + modifier = Modifier.height( + MessageBodyDefaults.ContentSpacing + ) + ) val textColor = if (event.isInCome) MaterialTheme.colors.onSurface else MaterialTheme.colors.onPrimary val textStyle = MaterialTheme.typography.body1.copy(textColor) val linkStyle = textStyle.copy( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt index d5ebd5986..d71d2ea99 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/IRoute.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt index 7121f3ddd..8960d778e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/NitterRepository.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 590fede14..2be478ab3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -452,8 +452,8 @@ fun NitterUsageDialog( onDismissRequest.invoke() } }, enabled = isValid) { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) - } + Text(text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_actions_ok)) + } }, dismissButton = { TextButton( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt index 16706d6c5..5ed6360e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/compose/ComposeViewModel.kt @@ -216,24 +216,24 @@ open class ComposeViewModel( val replyToUser by lazy { combine(account.mapNotNull { it }, replyToUserName) { account, list -> - if (list.isNotEmpty()) { - loadingReplyUser.value = true - try { - userRepository.lookupUsersByName( - list, - accountKey = account.accountKey, - lookupService = account.service as LookupService, - ) - } catch (e: Throwable) { - inAppNotification.notifyError(e) + if (list.isNotEmpty()) { + loadingReplyUser.value = true + try { + userRepository.lookupUsersByName( + list, + accountKey = account.accountKey, + lookupService = account.service as LookupService, + ) + } catch (e: Throwable) { + inAppNotification.notifyError(e) + emptyList() + } finally { + loadingReplyUser.value = false + } + } else { emptyList() - } finally { - loadingReplyUser.value = false } - } else { - emptyList() - } - }.asStateIn(viewModelScope, emptyList()) + }.asStateIn(viewModelScope, emptyList()) } val voteState = MutableStateFlow(null) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt index 2d31a4987..68cca0676 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.desktop.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * diff --git a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt index 0449c046c..ece9e77b6 100644 --- a/services/src/main/java/com/twidere/services/nitter/model/Profile.kt +++ b/services/src/main/java/com/twidere/services/nitter/model/Profile.kt @@ -1,7 +1,7 @@ /* * Twidere X * - * Copyright (C) 2020-2021 Tlaster + * Copyright (C) TwidereProject and Contributors * * This file is part of Twidere X. * From 57d56cb1a9ca1fac5514d3175eca1aa7530a9d61 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 16:16:08 +0800 Subject: [PATCH 537/615] add image compression before compose on Desktop --- .../com/twidere/twiderex/kmp/ExifScrambler.kt | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt index 9eaecd40c..d9d24d08d 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ExifScrambler.kt @@ -20,8 +20,11 @@ */ package com.twidere.twiderex.kmp +import java.io.ByteArrayOutputStream import java.io.File +import javax.imageio.IIOImage import javax.imageio.ImageIO +import javax.imageio.ImageWriteParam actual class ExifScrambler { actual fun deleteCacheFile(file: String) { @@ -29,10 +32,33 @@ actual class ExifScrambler { } actual fun removeExifData(file: String, maxImageSize: Long): String { - val imgFile = File(file) - val image = ImageIO.read(imgFile) - val result = File.createTempFile(imgFile.name, null) - ImageIO.write(image, imgFile.extension, result) - return result.absolutePath + return try { + val imgFile = File(file) + val image = ImageIO.read(imgFile) + val compressed = ByteArrayOutputStream() + val outputStream = ImageIO.createImageOutputStream(compressed) + val writer = ImageIO.getImageWritersByFormatName("JPEG").next() + val writeParam: ImageWriteParam = writer.defaultWriteParam + writeParam.compressionMode = ImageWriteParam.MODE_EXPLICIT + writeParam.compressionQuality = 1f + writer.output = outputStream + do { + compressed.flush() + compressed.reset() + writer.write(null, IIOImage(image, null, null), writeParam) + writeParam.compressionQuality -= 0.05f + } while (compressed.toByteArray().size > maxImageSize && writeParam.compressionQuality > 0.7f) + writer.dispose() + val result = File.createTempFile(imgFile.name, null).apply { + outputStream().use { + it.write(compressed.toByteArray()) + it.flush() + } + } + result.absolutePath + } catch (e: Exception) { + // not an image or compression failed + file + } } } From 114e08e18ff341ff47c6ffc8be6084c364b14518 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 16:26:31 +0800 Subject: [PATCH 538/615] fixed MemoryCachePagingSource keeps loading dublicate data --- .../com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt index 49125b64b..21697fded 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt @@ -40,7 +40,7 @@ class MemoryCachePagingSource( LoadResult.Page( data = result, null, - if (result.isEmpty()) null else page + 1 + if (result.isEmpty() || result.size < count) null else page + 1 ) } catch (e: Exception) { LoadResult.Error(e) From e8c86cd9954fc8c901928dc9b5b8a232a9a66c8a Mon Sep 17 00:00:00 2001 From: itsMimao Date: Tue, 28 Dec 2021 17:25:14 +0800 Subject: [PATCH 539/615] fixed MemoryCachePagingSource nextKey --- .../twidere/twiderex/paging/crud/MemoryCachePagingSource.kt | 5 ++--- .../twiderex/paging/crud/MemoryCachePagingSourceTest.kt | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt index 21697fded..17a52a9fe 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt @@ -32,15 +32,14 @@ class MemoryCachePagingSource( override suspend fun load(params: LoadParams): LoadResult { return try { - val page = params.key ?: 0 val count = params.loadSize - val startIndex = page * count + val startIndex = params.key ?: 0 val endIndex = startIndex + count val result = memoryCache.find(startIndex, endIndex) LoadResult.Page( data = result, null, - if (result.isEmpty() || result.size < count) null else page + 1 + if (result.isEmpty() || result.size < count) null else startIndex + result.size ) } catch (e: Exception) { LoadResult.Error(e) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt index e6fff9f19..ca2869f77 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt @@ -34,11 +34,12 @@ class MemoryCachePagingSourceTest { val source = MemoryCachePagingSource(pagingMemoryCache) var result = source.load(PagingSource.LoadParams.Refresh(null, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isNotEmpty()) - assertEquals(1, result.nextKey) + // index of next item + assertEquals(2, result.nextKey) result = source.load(PagingSource.LoadParams.Append(result.nextKey!!, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isNotEmpty()) - assertEquals(2, result.nextKey) + assertEquals(4, result.nextKey) result = source.load(PagingSource.LoadParams.Append(result.nextKey!!, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isEmpty()) From 4873bcd16e2c0d461817c907373354f976cab858 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 28 Dec 2021 19:51:42 +0800 Subject: [PATCH 540/615] use System.lineSeparator() --- .../com/twidere/twiderex/component/status/StatusActions.kt | 3 ++- .../kotlin/com/twidere/twiderex/scenes/MediaScene.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index eb0b90f6c..40c31841b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -340,7 +340,8 @@ fun ShareButton( if (shareWithContent) { buildString { append(text) - append("\n\n") + append(System.lineSeparator()) + append(System.lineSeparator()) append(status.generateShareLink()) } } else { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index 73a36deeb..cd5e1b7e5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -398,7 +398,8 @@ private fun StatusMediaInfo( extraText = if (shareWithContent) { buildString { append(text) - append("\n\n") + append(System.lineSeparator()) + append(System.lineSeparator()) append(status.generateShareLink()) } } else "" From 3e499de9486acd19eb98a5a127b36985f2c5460c Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 29 Dec 2021 08:57:27 +0800 Subject: [PATCH 541/615] save search input to vm --- .../scenes/search/SearchInputScene.kt | 21 +++++++------------ .../viewmodel/search/SearchInputViewModel.kt | 10 +++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index 6538396f9..b7acde6a7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -37,9 +37,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.History import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.GraphicsLayerScope @@ -76,15 +73,13 @@ val fadeResumeTransition: GraphicsLayerScope.(factor: Float) -> Unit = { factor fun SearchInputScene(initial: String? = null) { val viewModel: SearchInputViewModel = getViewModel() val source by viewModel.source.observeAsState(initial = emptyList()) - val initialText = initial ?: "" - var textFieldValue by remember { - mutableStateOf( - TextFieldValue( - text = initialText, - selection = TextRange(initialText.length), - ) - ) - } + val textFieldValue by viewModel.searchInput.observeAsState( + initial = if (initial != null) { + TextFieldValue(text = initial, selection = TextRange(initial.length)) + } else { + TextFieldValue() + } + ) val navigator = LocalNavigator.current TwidereScene { InAppNotificationScaffold( @@ -98,7 +93,7 @@ fun SearchInputScene(initial: String? = null) { TextInput( value = textFieldValue, onValueChange = { - textFieldValue = it + viewModel.updateSearchInput(it) }, maxLines = 1, placeholder = { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index bbe72c1d6..3ab07d463 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,11 +20,14 @@ */ package com.twidere.twiderex.viewmodel.search +import androidx.compose.ui.text.input.TextFieldValue import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull @@ -54,6 +57,13 @@ class SearchInputViewModel( } } + private val _searchInput = MutableSharedFlow(replay = 1) + val searchInput = _searchInput.asSharedFlow() + + fun updateSearchInput(searchInput: TextFieldValue) = viewModelScope.launch { + _searchInput.emit(searchInput) + } + val expandSearch = MutableStateFlow(false) fun remove(item: UiSearch) = viewModelScope.launch { From 25fb32030241131cdbc173e8961383131f48e917 Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 29 Dec 2021 09:18:44 +0800 Subject: [PATCH 542/615] fill select item --- .../com/twidere/twiderex/scenes/search/SearchInputScene.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index b7acde6a7..d134662e7 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -141,6 +141,7 @@ fun SearchInputScene(initial: String? = null) { modifier = Modifier.clickable( onClick = { viewModel.addOrUpgrade(it.content) + viewModel.updateSearchInput(TextFieldValue(it.content, TextRange(it.content.length))) navigator.search(it.content) } ), From e715eb45ece1b037a322cf774e8afd1ad095f473 Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 29 Dec 2021 10:30:48 +0800 Subject: [PATCH 543/615] set initial field as vm param --- .../twidere/twiderex/di/modules/ViewModelModule.kt | 2 +- .../com/twidere/twiderex/scenes/home/SearchItem.kt | 5 ++++- .../twiderex/scenes/search/SearchInputScene.kt | 14 ++++++-------- .../viewmodel/search/SearchInputViewModel.kt | 13 +++++++------ 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt index 36f0a3594..d7f613ce1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/modules/ViewModelModule.kt @@ -175,7 +175,7 @@ private fun Module.mastodon() { } private fun Module.search() { - viewModel { SearchInputViewModel(get(), get()) } + viewModel { (keyword: String) -> SearchInputViewModel(get(), get(), keyword) } viewModel { (content: String) -> SearchSaveViewModel(get(), get(), content) } viewModel { (keyword: String) -> SearchTweetsViewModel(get(), get(), keyword) } viewModel { (keyword: String) -> SearchUserViewModel(get(), keyword) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt index 0f65c5ad0..e26db8f52 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/home/SearchItem.kt @@ -63,6 +63,7 @@ import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.search.SearchInputViewModel import com.twidere.twiderex.viewmodel.trend.TrendViewModel +import org.koin.core.parameter.parametersOf class SearchItem : HomeNavigationItem() { @@ -107,7 +108,9 @@ fun SearchScene() { @Composable fun SearchSceneContent() { val account = LocalActiveAccount.current ?: return - val viewModel: SearchInputViewModel = getViewModel() + val viewModel: SearchInputViewModel = getViewModel { + parametersOf("") + } val trendViewModel: TrendViewModel = getViewModel() val source by viewModel.savedSource.observeAsState(initial = emptyList()) val trends = trendViewModel.source.collectAsLazyPagingItems() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt index d134662e7..c49c813bf 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/search/SearchInputScene.kt @@ -54,6 +54,7 @@ import com.twidere.twiderex.di.ext.getViewModel import com.twidere.twiderex.extensions.observeAsState import com.twidere.twiderex.ui.TwidereScene import com.twidere.twiderex.viewmodel.search.SearchInputViewModel +import org.koin.core.parameter.parametersOf val fadeCreateTransition: GraphicsLayerScope.(factor: Float) -> Unit = { factor -> alpha = factor @@ -71,15 +72,12 @@ val fadeResumeTransition: GraphicsLayerScope.(factor: Float) -> Unit = { factor @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class) @Composable fun SearchInputScene(initial: String? = null) { - val viewModel: SearchInputViewModel = getViewModel() + + val viewModel: SearchInputViewModel = getViewModel { + parametersOf(initial ?: "") + } val source by viewModel.source.observeAsState(initial = emptyList()) - val textFieldValue by viewModel.searchInput.observeAsState( - initial = if (initial != null) { - TextFieldValue(text = initial, selection = TextRange(initial.length)) - } else { - TextFieldValue() - } - ) + val textFieldValue by viewModel.searchInput.observeAsState(TextFieldValue()) val navigator = LocalNavigator.current TwidereScene { InAppNotificationScaffold( diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt index 3ab07d463..75202eaf6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/search/SearchInputViewModel.kt @@ -20,14 +20,14 @@ */ package com.twidere.twiderex.viewmodel.search +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.repository.AccountRepository import com.twidere.twiderex.repository.SearchRepository import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapNotNull @@ -38,6 +38,7 @@ import moe.tlaster.precompose.viewmodel.viewModelScope class SearchInputViewModel( private val repository: SearchRepository, private val accountRepository: AccountRepository, + keyword: String ) : ViewModel() { private val account by lazy { accountRepository.activeAccount.mapNotNull { it } @@ -57,11 +58,11 @@ class SearchInputViewModel( } } - private val _searchInput = MutableSharedFlow(replay = 1) - val searchInput = _searchInput.asSharedFlow() + private val _searchInput = MutableStateFlow(TextFieldValue(keyword, TextRange(keyword.length))) + val searchInput = _searchInput.asStateFlow() - fun updateSearchInput(searchInput: TextFieldValue) = viewModelScope.launch { - _searchInput.emit(searchInput) + fun updateSearchInput(searchInput: TextFieldValue) { + _searchInput.value = searchInput } val expandSearch = MutableStateFlow(false) From 1aaa5ff00ac3bb28ed4f4bb3fb1445c66cd8163f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Dec 2021 15:07:21 +0800 Subject: [PATCH 544/615] refactored MemoryCachePagingSource --- .../paging/crud/LimitOffsetPagingSource.kt | 157 ++++++++++++++++++ .../paging/crud/MemoryCachePagingSource.kt | 36 +--- .../twiderex/paging/crud/PagingMemoryCache.kt | 3 +- .../paging/crud/PagingMemoryCacheTest.kt | 2 +- 4 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt new file mode 100644 index 000000000..0569eed65 --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt @@ -0,0 +1,157 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.paging.crud + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger + +private val INVALID = PagingSource.LoadResult.Invalid() + +internal abstract class LimitOffsetPagingSource : PagingSource() { + + protected val itemCount: AtomicInteger = AtomicInteger(-1) + + protected abstract fun registerInvalidateObserver() + + protected abstract suspend fun queryItemCount(): Int + + protected abstract suspend fun queryData(offset: Int, limit: Int): List + + private val registeredObserver: AtomicBoolean = AtomicBoolean(false) + + override suspend fun load(params: LoadParams): LoadResult { + return withContext(Dispatchers.IO) { + registerObserverIfNecessary() + // When Invalidated, Paging will create a new PagingSource, so itemCount will be reset to -1 + var tempCount = itemCount.get() + // if itemCount is < 0, then it is initial load + if (tempCount < 0) { + tempCount = queryItemCount() + itemCount.set(tempCount) + } + println("Desktop ==> load: $tempCount") + // loadData + val key = params.key ?: 0 + val limit: Int = getLimit(params, key) + val offset: Int = getOffset(params, key, tempCount) + val data = queryData(offset = offset, limit = limit) + val nextPosToLoad = offset + data.size + val nextKey = + if (data.isEmpty() || data.size < limit || nextPosToLoad >= tempCount) { + null + } else { + nextPosToLoad + } + // Refreshed key could be any where in the list, so we need both prevKey and nextKey + // in order to load more data both before and after the current key + val prevKey = if (offset <= 0 || data.isEmpty()) null else offset + val loadResult = LoadResult.Page( + data = data, + prevKey = prevKey, + nextKey = nextKey, + itemsBefore = offset, + itemsAfter = maxOf(0, tempCount - nextPosToLoad) + ) + @Suppress("UNCHECKED_CAST") + if (invalid) INVALID as LoadResult.Invalid else loadResult + } + } + + + /** + * Calculates query limit based on LoadType. + * + * Prepend: If requested loadSize is larger than available number of items to prepend, it will + * query with OFFSET = 0, LIMIT = prevKey + */ + private fun getLimit(params: LoadParams, key: Int): Int { + return when (params) { + is LoadParams.Prepend -> + if (key < params.loadSize) key else params.loadSize + else -> params.loadSize + } + } + + /** + * calculates query offset amount based on loadtype + * + * Prepend: OFFSET is calculated by counting backwards the number of items that needs to be + * loaded before [key]. For example, if key = 30 and loadSize = 5, then offset = 25 and items + * in db position 26-30 are loaded. + * If requested loadSize is larger than the number of available items to + * prepend, OFFSET clips to 0 to prevent negative OFFSET. + * + * Refresh: + * If initialKey is supplied through Pager, Paging 3 will now start loading from + * initialKey with initialKey being the first item. + * If key is supplied by [getRefreshKey],OFFSET will attempt to load around the anchorPosition + * with anchorPosition being the middle item. See comments on [getRefreshKey] for more details. + * If key (regardless if from initialKey or [getRefreshKey]) is larger than available items, + * the last page will be loaded by counting backwards the loadSize before last item in + * database. For example, this can happen if invalidation came from a large number of items + * dropped. i.e. in items 0 - 100, items 41-80 are dropped. Depending on last + * viewed item, hypothetically [getRefreshKey] may return key = 60. If loadSize = 10, then items + * 31-40 will be loaded. + */ + private fun getOffset(params: LoadParams, key: Int, itemCount: Int): Int { + return when (params) { + is LoadParams.Prepend -> + if (key < params.loadSize) 0 else (key - params.loadSize) + is LoadParams.Append -> key + is LoadParams.Refresh -> + if (key >= itemCount) { + maxOf(0, itemCount - params.loadSize) + } else { + key + } + } + } + + private fun registerObserverIfNecessary() { + if (registeredObserver.compareAndSet(false, true)) { + registerInvalidateObserver() + } + } + + /** + * It is unknown whether anchorPosition represents the item at the top of the screen or item at + * the bottom of the screen. To ensure the number of items loaded is enough to fill up the + * screen, half of loadSize is loaded before the anchorPosition and the other half is + * loaded after the anchorPosition -- anchorPosition becomes the middle item. + * + * To prevent a negative key, key = 0 when the number of items available before anchorPosition + * is less than the requested amount of initialLoadSize / 2. + */ + override fun getRefreshKey(state: PagingState): Int? { + val initialLoadSize = state.config.initialLoadSize + return when (state.anchorPosition) { + null -> null + else -> maxOf(0, state.anchorPosition!! - (initialLoadSize / 2)) + } + } + + override val jumpingSupported: Boolean + get() = true +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt index 17a52a9fe..f1fb25adb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt @@ -20,37 +20,15 @@ */ package com.twidere.twiderex.paging.crud -import androidx.paging.PagingSource -import androidx.paging.PagingState - -class MemoryCachePagingSource( +internal class MemoryCachePagingSource( private val memoryCache: PagingMemoryCache, -) : PagingSource(), OnInvalidateObserver { - init { - memoryCache.addWeakObserver(this) - } +) : LimitOffsetPagingSource(), OnInvalidateObserver { + + override fun registerInvalidateObserver() = memoryCache.addWeakObserver(this) - override suspend fun load(params: LoadParams): LoadResult { - return try { - val count = params.loadSize - val startIndex = params.key ?: 0 - val endIndex = startIndex + count - val result = memoryCache.find(startIndex, endIndex) - LoadResult.Page( - data = result, - null, - if (result.isEmpty() || result.size < count) null else startIndex + result.size - ) - } catch (e: Exception) { - LoadResult.Error(e) - } - } + override suspend fun queryItemCount() = memoryCache.size() - override fun onInvalidate() { - invalidate() - } + override suspend fun queryData(offset: Int, limit: Int) = memoryCache.find(offset, limit) - override fun getRefreshKey(state: PagingState): Int? { - return null - } + override fun onInvalidate() = invalidate() } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt index 6df8ac7f4..60671a23a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCache.kt @@ -69,7 +69,8 @@ class PagingMemoryCache : InvalidateTracker { cacheList.clear() } - fun find(startIndex: Int, endIndex: Int): List { + fun find(startIndex: Int, limit: Int): List { + val endIndex = startIndex + limit // exclusive return when { endIndex <= cacheList.size -> { cacheList.subList(startIndex, endIndex) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt index 630a19c8d..69c5b607a 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/PagingMemoryCacheTest.kt @@ -31,7 +31,7 @@ class PagingMemoryCacheTest { fun find() { pagingMemoryCache.insert(listOf("1", "2", "3", "4")) // check if - assertEquals("3", pagingMemoryCache.find(2, 3)[0]) + assertEquals("3", pagingMemoryCache.find(2, 1)[0]) // check if index out of bound assertEquals(4, pagingMemoryCache.find(0, 10).size) From e1f0a949c262fd800b39384b0b7e6c296a523d6f Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Dec 2021 15:09:29 +0800 Subject: [PATCH 545/615] format code --- .../twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt | 5 ++--- .../twidere/twiderex/paging/crud/MemoryCachePagingSource.kt | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt index 0569eed65..00ea90259 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt @@ -37,7 +37,7 @@ internal abstract class LimitOffsetPagingSource : PagingSource + protected abstract suspend fun queryData(offset: Int, limit: Int): List private val registeredObserver: AtomicBoolean = AtomicBoolean(false) @@ -67,7 +67,7 @@ internal abstract class LimitOffsetPagingSource : PagingSource : PagingSource( override fun registerInvalidateObserver() = memoryCache.addWeakObserver(this) - override suspend fun queryItemCount() = memoryCache.size() + override suspend fun queryItemCount() = memoryCache.size() override suspend fun queryData(offset: Int, limit: Int) = memoryCache.find(offset, limit) - override fun onInvalidate() = invalidate() + override fun onInvalidate() = invalidate() } From 90a0f46313846893793d7c653df413ac5fc93e9d Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Dec 2021 15:56:31 +0800 Subject: [PATCH 546/615] refactored LimitOffsetTransformPagingSource --- .../LimitOffsetTransformPagingSource.kt | 181 ++---------------- .../paging/crud/LimitOffsetPagingSource.kt | 25 ++- .../paging/crud/MemoryCachePagingSource.kt | 6 +- .../crud/MemoryCachePagingSourceTest.kt | 4 +- 4 files changed, 33 insertions(+), 183 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt index 517e4a1d4..334ea3fa4 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt @@ -21,24 +21,10 @@ package com.twidere.twiderex.room.db.paging import android.annotation.SuppressLint -import androidx.paging.PagingSource -import androidx.paging.PagingState import androidx.room.InvalidationTracker import androidx.room.RoomDatabase -import androidx.room.withTransaction +import com.twidere.twiderex.paging.crud.LimitOffsetPagingSource import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -/** - * An implementation of [PagingSource] to perform a LIMIT OFFSET query - * - * This class is used for Paging3 to perform Query and RawQuery in Room to return a PagingSource - * for Pager's consumption. Registers observers on tables lazily and automatically invalidates - * itself when data changes. - */ - -private val INVALID = PagingSource.LoadResult.Invalid() internal class LimitOffsetTransformPagingSource( /** @@ -52,173 +38,28 @@ internal class LimitOffsetTransformPagingSource( private val queryItemCount: suspend () -> Int, private val db: RoomDatabase, vararg tables: String -) : PagingSource() { - - private val itemCount: AtomicInteger = AtomicInteger(-1) +) : LimitOffsetPagingSource(Dispatchers.IO) { private val observer = object : InvalidationTracker.Observer(tables) { override fun onInvalidated(tables: MutableSet) { invalidate() } } - private val registeredObserver: AtomicBoolean = AtomicBoolean(false) @SuppressLint("RestrictedApi") - override suspend fun load(params: LoadParams): LoadResult { - return withContext(Dispatchers.IO) { - registerObserverIfNecessary() - val tempCount = itemCount.get() - // if itemCount is < 0, then it is initial load - if (tempCount < 0) { - initialLoad(params) - } else { - // otherwise, it is a subsequent load - val loadResult = loadFromDb(params, tempCount) - // manually check if database has been updated. If so, the observers's - // invalidation callback will invalidate this paging source - db.invalidationTracker.refreshVersionsSync() - @Suppress("UNCHECKED_CAST") - if (invalid) INVALID as LoadResult.Invalid else loadResult - } - } - } - - /** - * For the very first time that this PagingSource's [load] is called. Executes the count - * query (initializes [itemCount]) and db query within a transaction to ensure initial load's - * data integrity. - * - * For example, if the database gets updated after the count query but before the db query - * completes, the paging source may not invalidate in time, but this method will return - * data based on the original database that the count was performed on to ensure a valid - * initial load. - */ - private suspend fun initialLoad(params: LoadParams): LoadResult { - return db.withTransaction { - val tempCount = queryItemCount() - itemCount.set(tempCount) - loadFromDb(params, tempCount) - } + override fun registerInvalidateObserver() { + db.invalidationTracker.addWeakObserver(observer) } - private suspend fun loadFromDb( - params: LoadParams, - itemCount: Int, - ): LoadResult { - val key = params.key ?: 0 - val limit: Int = getLimit(params, key) - val offset: Int = getOffset(params, key, itemCount) - return queryDatabase(offset, limit, itemCount) - } + override suspend fun queryItemCount() = queryItemCount.invoke() - /** - * Calculates query limit based on LoadType. - * - * Prepend: If requested loadSize is larger than available number of items to prepend, it will - * query with OFFSET = 0, LIMIT = prevKey - */ - private fun getLimit(params: LoadParams, key: Int): Int { - return when (params) { - is LoadParams.Prepend -> - if (key < params.loadSize) key else params.loadSize - else -> params.loadSize - } - } - - /** - * calculates query offset amount based on loadtype - * - * Prepend: OFFSET is calculated by counting backwards the number of items that needs to be - * loaded before [key]. For example, if key = 30 and loadSize = 5, then offset = 25 and items - * in db position 26-30 are loaded. - * If requested loadSize is larger than the number of available items to - * prepend, OFFSET clips to 0 to prevent negative OFFSET. - * - * Refresh: - * If initialKey is supplied through Pager, Paging 3 will now start loading from - * initialKey with initialKey being the first item. - * If key is supplied by [getRefreshKey],OFFSET will attempt to load around the anchorPosition - * with anchorPosition being the middle item. See comments on [getRefreshKey] for more details. - * If key (regardless if from initialKey or [getRefreshKey]) is larger than available items, - * the last page will be loaded by counting backwards the loadSize before last item in - * database. For example, this can happen if invalidation came from a large number of items - * dropped. i.e. in items 0 - 100, items 41-80 are dropped. Depending on last - * viewed item, hypothetically [getRefreshKey] may return key = 60. If loadSize = 10, then items - * 31-40 will be loaded. - */ - private fun getOffset(params: LoadParams, key: Int, itemCount: Int): Int { - return when (params) { - is LoadParams.Prepend -> - if (key < params.loadSize) 0 else (key - params.loadSize) - is LoadParams.Append -> key - is LoadParams.Refresh -> - if (key >= itemCount) { - maxOf(0, itemCount - params.loadSize) - } else { - key - } - } - } - - /** - * calls RoomDatabase.query() to return a cursor and then calls convertRows() to extract and - * return list of data - * - * throws [IllegalArgumentException] from [CursorUtil] if column does not exist - * - * @param offset offset parameter for LIMIT/OFFSET query. Bounded within user-supplied offset - * if it is supplied - * - * @param limit limit parameter for LIMIT/OFFSET query. Bounded within user-supplied limit - * if it is supplied - */ - private suspend fun queryDatabase( - offset: Int, - limit: Int, - itemCount: Int, - ): LoadResult { - val data = loadPagingList(offset, limit) - val nextPosToLoad = offset + data.size - val nextKey = - if (data.isEmpty() || data.size < limit || nextPosToLoad >= itemCount) { - null - } else { - nextPosToLoad - } - val prevKey = if (offset <= 0 || data.isEmpty()) null else offset - return LoadResult.Page( - data = data, - prevKey = prevKey, - nextKey = nextKey, - itemsBefore = offset, - itemsAfter = maxOf(0, itemCount - nextPosToLoad) - ) - } + override suspend fun queryData(offset: Int, limit: Int) = loadPagingList(offset, limit) @SuppressLint("RestrictedApi") - private fun registerObserverIfNecessary() { - if (registeredObserver.compareAndSet(false, true)) { - db.invalidationTracker.addWeakObserver(observer) - } + override suspend fun processResult(result: LoadResult): LoadResult { + // manually check if database has been updated. If so, the observers's + // invalidation callback will invalidate this paging source + db.invalidationTracker.refreshVersionsSync() + return result } - - /** - * It is unknown whether anchorPosition represents the item at the top of the screen or item at - * the bottom of the screen. To ensure the number of items loaded is enough to fill up the - * screen, half of loadSize is loaded before the anchorPosition and the other half is - * loaded after the anchorPosition -- anchorPosition becomes the middle item. - * - * To prevent a negative key, key = 0 when the number of items available before anchorPosition - * is less than the requested amount of initialLoadSize / 2. - */ - override fun getRefreshKey(state: PagingState): Int? { - val initialLoadSize = state.config.initialLoadSize - return when { - state.anchorPosition == null -> null - else -> maxOf(0, state.anchorPosition!! - (initialLoadSize / 2)) - } - } - - override val jumpingSupported: Boolean - get() = true } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt index 00ea90259..5b46cccc5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/LimitOffsetPagingSource.kt @@ -22,14 +22,16 @@ package com.twidere.twiderex.paging.crud import androidx.paging.PagingSource import androidx.paging.PagingState -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger private val INVALID = PagingSource.LoadResult.Invalid() -internal abstract class LimitOffsetPagingSource : PagingSource() { +internal abstract class LimitOffsetPagingSource( + private val dispatcher: CoroutineDispatcher +) : PagingSource() { protected val itemCount: AtomicInteger = AtomicInteger(-1) @@ -39,10 +41,12 @@ internal abstract class LimitOffsetPagingSource : PagingSource + protected open suspend fun processResult(result: LoadResult): LoadResult = result + private val registeredObserver: AtomicBoolean = AtomicBoolean(false) override suspend fun load(params: LoadParams): LoadResult { - return withContext(Dispatchers.IO) { + return withContext(dispatcher) { registerObserverIfNecessary() // When Invalidated, Paging will create a new PagingSource, so itemCount will be reset to -1 var tempCount = itemCount.get() @@ -51,7 +55,6 @@ internal abstract class LimitOffsetPagingSource : PagingSource load: $tempCount") // loadData val key = params.key ?: 0 val limit: Int = getLimit(params, key) @@ -67,12 +70,14 @@ internal abstract class LimitOffsetPagingSource : PagingSource else loadResult diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt index 573dece1a..de8bb5311 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSource.kt @@ -20,9 +20,13 @@ */ package com.twidere.twiderex.paging.crud +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + internal class MemoryCachePagingSource( private val memoryCache: PagingMemoryCache, -) : LimitOffsetPagingSource(), OnInvalidateObserver { + dispatcher: CoroutineDispatcher = Dispatchers.IO +) : LimitOffsetPagingSource(dispatcher), OnInvalidateObserver { override fun registerInvalidateObserver() = memoryCache.addWeakObserver(this) diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt index ca2869f77..1dbf57e41 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/paging/crud/MemoryCachePagingSourceTest.kt @@ -30,7 +30,7 @@ class MemoryCachePagingSourceTest { @Test fun load_loadFromMemoryCache() = runBlocking { - pagingMemoryCache.insert(listOf("1", "2", "3", "4")) + pagingMemoryCache.insert(listOf("1", "2", "3", "4", "5")) val source = MemoryCachePagingSource(pagingMemoryCache) var result = source.load(PagingSource.LoadParams.Refresh(null, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page assert(result.data.isNotEmpty()) @@ -42,7 +42,7 @@ class MemoryCachePagingSourceTest { assertEquals(4, result.nextKey) result = source.load(PagingSource.LoadParams.Append(result.nextKey!!, 2, placeholdersEnabled = false)) as PagingSource.LoadResult.Page - assert(result.data.isEmpty()) + assert(result.data.isNotEmpty()) assertEquals(null, result.nextKey) } } From ae871e65b609986af06e5278f23f5e8ca8c3ae81 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Wed, 29 Dec 2021 20:40:49 +0800 Subject: [PATCH 547/615] fixed PagingWithGapMediator causing infinite api calls --- .../paging/mediator/paging/PagingWithGapMediator.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index 0f9e9bb04..ee161932d 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -124,7 +124,7 @@ abstract class PagingWithGapMediator( result.saveToDb(database) } return MediatorResult.Success( - endOfPaginationReached = result.isEmpty() + endOfPaginationReached = !hasMore(result, max_id), ) } catch (e: Throwable) { return MediatorResult.Error(e) @@ -147,4 +147,9 @@ abstract class PagingWithGapMediator( max_id: String? = null, since_id: String? = null, ): List + + protected open suspend fun hasMore(result: List, max_id: String?): Boolean { + // Twitter API returns single status with max_id when there is no more data + return result.size > 1 || result.firstOrNull()?.status?.statusId != max_id + } } From 7a6f42bcd97821c4cd13bd9c2cd72efed54f7c0d Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 30 Dec 2021 11:03:08 +0800 Subject: [PATCH 548/615] fixed hasMore in PagingWithGapMediator --- .../twiderex/paging/mediator/paging/PagingWithGapMediator.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index ee161932d..9c78b0785 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -150,6 +150,8 @@ abstract class PagingWithGapMediator( protected open suspend fun hasMore(result: List, max_id: String?): Boolean { // Twitter API returns single status with max_id when there is no more data - return result.size > 1 || result.firstOrNull()?.status?.statusId != max_id + return result.size > 1 || result.firstOrNull()?.let { + it.status.statusId != max_id + } ?: false } } From 5a70dc98911c8c2ca06b59480c2d30d78a0d8af7 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 30 Dec 2021 11:50:40 +0800 Subject: [PATCH 549/615] add not found exceptions for user lookup --- .../com/twidere/services/http/MicroBlogNotFoundException.kt | 4 ++++ .../main/java/com/twidere/services/twitter/TwitterService.kt | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt diff --git a/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt b/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt new file mode 100644 index 000000000..f5016c14b --- /dev/null +++ b/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt @@ -0,0 +1,4 @@ +package com.twidere.services.http + +class MicroBlogNotFoundException(override val microBlogErrorMessage: String?) : MicroBlogException() { +} \ No newline at end of file diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index fd27132c9..8f03b821c 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -23,6 +23,7 @@ package com.twidere.services.twitter import com.twidere.services.http.AuthorizationInterceptor import com.twidere.services.http.Errors import com.twidere.services.http.HttpClientFactory +import com.twidere.services.http.MicroBlogNotFoundException import com.twidere.services.http.authorization.OAuth1Authorization import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.DownloadMediaService @@ -248,6 +249,7 @@ class TwitterService( if (user.errors != null && user.errors.any()) { throw TwitterApiException( errors = user.errors.map { + if (it.title == "Not Found Error") throw MicroBlogNotFoundException(it.detail) Errors( code = null, message = null, From 8b1234c95bd7b52e93ad1e4f74f1dd73e8658bf9 Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 30 Dec 2021 14:41:39 +0800 Subject: [PATCH 550/615] fixed issue #384 --- .../repository/DirectMessageRepository.kt | 62 ++++++++++++------- .../http/MicroBlogNotFoundException.kt | 23 ++++++- .../services/twitter/TwitterService.kt | 2 +- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt index 33f7fb1d0..5e78f15b5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/repository/DirectMessageRepository.kt @@ -21,6 +21,7 @@ package com.twidere.twiderex.repository import androidx.paging.PagingData +import com.twidere.services.http.MicroBlogNotFoundException import com.twidere.services.microblog.DirectMessageService import com.twidere.services.microblog.LookupService import com.twidere.services.microblog.model.IDirectMessage @@ -43,6 +44,7 @@ import kotlinx.coroutines.flow.Flow class DirectMessageRepository( private val database: CacheDatabase ) { + private val userNotFound = mutableListOf() fun dmConversation( accountKey: MicroBlogKey, conversationKey: MicroBlogKey @@ -155,7 +157,8 @@ class DirectMessageRepository( } // if conversation is empty, delete conversation too if (database.directMessageDao().getMessageCount(accountKey, conversationKey) == 0L) { - val conversation = database.directMessageConversationDao().findWithConversationKey(accountKey, conversationKey) + val conversation = + database.directMessageConversationDao().findWithConversationKey(accountKey, conversationKey) conversation?.let { database.directMessageConversationDao().delete(it) } @@ -165,29 +168,38 @@ class DirectMessageRepository( suspend fun fetchEventAndSaveToDataBase(key: String?, accountKey: MicroBlogKey, service: DirectMessageService, lookupService: LookupService): List { val result = service.getDirectMessages(key, 50) - val events = result.map { + val events = result.mapNotNull { if (it is DirectMessageEvent) { - it.toUi(accountKey, lookupUser(accountKey, MicroBlogKey.twitter(it.messageCreate?.senderId ?: ""), lookupService)) - } else throw NotImplementedError() + // check if sender and receiver is not null + lookupUser( + accountKey, + MicroBlogKey.twitter(it.messageCreate?.senderId ?: ""), + lookupService + )?.let { sender -> + it.toUi(accountKey, sender) + }?.let { event -> + if (lookupUser(accountKey, event.recipientAccountKey, lookupService) != null) event else null + } + } else null } // save message, media database.withTransaction { database.directMessageDao().insertAll(events) events.groupBy { it.conversationKey } - .map { entry -> + .mapNotNull { entry -> val msgWithData = entry.value.first() - val chatUser = - lookupUser(accountKey, msgWithData.conversationUserKey, lookupService) - UiDMConversation( - accountKey = accountKey, - conversationId = msgWithData.conversationKey.id, - conversationKey = msgWithData.conversationKey, - conversationAvatar = chatUser.profileImage.toString(), - conversationName = chatUser.name, - conversationSubName = chatUser.screenName, - conversationType = UiDMConversation.Type.ONE_TO_ONE, - recipientKey = msgWithData.conversationUserKey - ) + lookupUser(accountKey, msgWithData.conversationUserKey, lookupService)?.let { + UiDMConversation( + accountKey = accountKey, + conversationId = msgWithData.conversationKey.id, + conversationKey = msgWithData.conversationKey, + conversationAvatar = it.profileImage.toString(), + conversationName = it.name, + conversationSubName = it.screenName, + conversationType = UiDMConversation.Type.ONE_TO_ONE, + recipientKey = msgWithData.conversationUserKey + ) + } }.let { database.directMessageConversationDao().insertAll(it) } @@ -195,12 +207,18 @@ class DirectMessageRepository( return result } - private suspend fun lookupUser(accountKey: MicroBlogKey, userKey: MicroBlogKey, service: LookupService): UiUser { + private suspend fun lookupUser(accountKey: MicroBlogKey, userKey: MicroBlogKey, service: LookupService): UiUser? { return database.userDao().findWithUserKey(userKey) ?: let { - val user = service.lookupUser(userKey.id) - .toUi(accountKey) - database.userDao().insertAll(listOf(user)) - user + try { + if (userNotFound.contains(userKey)) return null + val user = service.lookupUser(userKey.id) + .toUi(accountKey) + database.userDao().insertAll(listOf(user)) + user + } catch (e: MicroBlogNotFoundException) { + userNotFound.add(userKey) + null + } } } } diff --git a/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt b/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt index f5016c14b..330a0f895 100644 --- a/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt +++ b/services/src/main/java/com/twidere/services/http/MicroBlogNotFoundException.kt @@ -1,4 +1,23 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ package com.twidere.services.http -class MicroBlogNotFoundException(override val microBlogErrorMessage: String?) : MicroBlogException() { -} \ No newline at end of file +class MicroBlogNotFoundException(override val microBlogErrorMessage: String? = "") : MicroBlogException() diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 8f03b821c..c5b899639 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -249,7 +249,7 @@ class TwitterService( if (user.errors != null && user.errors.any()) { throw TwitterApiException( errors = user.errors.map { - if (it.title == "Not Found Error") throw MicroBlogNotFoundException(it.detail) + if ("Not Found Error".equals(it.title, true)) throw MicroBlogNotFoundException(it.detail) Errors( code = null, message = null, From 208fe5c71e7d2460cd56a0905ac4e11697179d08 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 14:59:55 +0800 Subject: [PATCH 551/615] refactor status actions of share content --- .../component/status/StatusActions.kt | 30 +++++++++++-------- .../preferences/model/DisplayPreferences.kt | 1 - .../com/twidere/twiderex/scenes/MediaScene.kt | 15 ++++------ .../twiderex/scenes/settings/DisplayScene.kt | 13 -------- .../viewmodel/settings/DisplayViewModel.kt | 6 ---- 5 files changed, 24 insertions(+), 41 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index 40c31841b..ca6d99888 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -65,7 +65,6 @@ import com.twidere.twiderex.kmp.LocalRemoteNavigator import com.twidere.twiderex.model.enums.ComposeType import com.twidere.twiderex.model.enums.PlatformType import com.twidere.twiderex.model.ui.UiStatus -import com.twidere.twiderex.preferences.LocalDisplayPreferences import com.twidere.twiderex.ui.LocalActiveAccount private val rippleSize = 24.dp @@ -260,7 +259,6 @@ fun ShareButton( ) val clipboardManager = LocalClipboardManager.current val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_more) - val shareWithContent = LocalDisplayPreferences.current.shareWithContent Box( modifier = modifier, ) { @@ -337,16 +335,7 @@ fun ShareButton( onClick = { expanded = false remoteNavigator.shareText( - if (shareWithContent) { - buildString { - append(text) - append(System.lineSeparator()) - append(System.lineSeparator()) - append(status.generateShareLink()) - } - } else { - status.generateShareLink() - } + status.generateShareLink() ) } ) { @@ -354,6 +343,23 @@ fun ShareButton( text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_share_link), ) } + DropdownMenuItem( + onClick = { + expanded = false + remoteNavigator.shareText( + buildString { + append(text) + append(System.lineSeparator()) + append(System.lineSeparator()) + append(status.generateShareLink()) + } + ) + } + ) { + Text( + text = stringResource(res = com.twidere.twiderex.MR.strings.common_controls_status_actions_share_content), + ) + } if (data.user.userKey == accountKey) { DropdownMenuItem( onClick = { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt index bf65242d6..d5aa960b3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/DisplayPreferences.kt @@ -31,7 +31,6 @@ data class DisplayPreferences( val autoPlayback: AutoPlayback = AutoPlayback.Auto, val urlPreview: Boolean = false, val muteByDefault: Boolean = false, - val shareWithContent: Boolean = false, ) { @Serializable enum class AvatarStyle { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt index cd5e1b7e5..816647b8f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/MediaScene.kt @@ -342,7 +342,6 @@ private fun StatusMediaInfo( ) { val scope = rememberCoroutineScope() - val shareWithContent = LocalDisplayPreferences.current.shareWithContent val text = renderContentAnnotatedString( htmlText = status.htmlText, linkResolver = { status.resolveLink(it) }, @@ -395,14 +394,12 @@ private fun StatusMediaInfo( scope.launch { viewModel.shareMedia( currentMedia = currentMedia, - extraText = if (shareWithContent) { - buildString { - append(text) - append(System.lineSeparator()) - append(System.lineSeparator()) - append(status.generateShareLink()) - } - } else "" + extraText = buildString { + append(text) + append(System.lineSeparator()) + append(System.lineSeparator()) + append(status.generateShareLink()) + } ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt index a7762aae5..a064c9070 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/DisplayScene.kt @@ -218,19 +218,6 @@ fun DisplayScene() { } ) } - ItemDivider() - ItemHeader() { - Text(text = stringResource(com.twidere.twiderex.MR.strings.common_controls_status_actions_share)) - } - switchItem( - value = display.shareWithContent, - onChanged = { - viewModel.setShareWithContent(it) - }, - title = { - Text(text = stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_display_share_with_content)) - } - ) } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt index 8e77ecab9..8112c7913 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/settings/DisplayViewModel.kt @@ -70,10 +70,4 @@ class DisplayViewModel( it.copy(muteByDefault = value) } } - - fun setShareWithContent(value: Boolean) = viewModelScope.launch { - displayPreferences.updateData { - it.copy(shareWithContent = value) - } - } } From d28ef7d81375a779aca022d143f6f6d4aacd0bfd Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 15:09:30 +0800 Subject: [PATCH 552/615] update localization --- common/src/commonMain/resources/MR/base/strings.xml | 12 ++++++------ localization | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/src/commonMain/resources/MR/base/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml index 51310a4e6..1a6b203f0 100644 --- a/common/src/commonMain/resources/MR/base/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -150,17 +150,18 @@ %s votes %s people Show this thread - Share link - Bookmark - Delete tweet - Quote Retweet Pin on Profile Copy text Unpin from Profile + Translate + Share link + Share content + Bookmark + Delete tweet + Quote Share Vote - Translate Copy link %s quote %s quotes @@ -302,7 +303,6 @@ Theme Scrolling timeline Thanks for using @TwidereProject! - Share link (media) and content Url previews Absolute Relative diff --git a/localization b/localization index c0bae6988..b5d016e01 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit c0bae69882c69b65e70725803df28bb255da1858 +Subproject commit b5d016e0185c540928076418798457622219d835 From 8fc15e164ae7d2a29239d2bd962e74f2dd3c9cc9 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 17:16:09 +0800 Subject: [PATCH 553/615] uncheck the timeline selection to prevent crashes --- .../twiderex/component/status/StatusText.kt | 19 ++++++++++++++++--- .../status/TimelineStatusComponent.kt | 19 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index 2eebe8771..e0b95fdbc 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -54,7 +54,8 @@ import com.twidere.twiderex.model.ui.UiStatus fun ColumnScope.StatusText( status: UiStatus, maxLines: Int = Int.MAX_VALUE, - showMastodonPoll: Boolean = true + showMastodonPoll: Boolean = true, + isSelectionAble: Boolean = true, ) { val expandable = status.platformType == PlatformType.Mastodon && status.spoilerText != null @@ -85,7 +86,19 @@ fun ColumnScope.StatusText( } AnimatedVisibility(visible = expanded) { Column { - SelectionContainer { + if (isSelectionAble) { + SelectionContainer { + HtmlText( + modifier = Modifier.fillMaxWidth(), + htmlText = status.htmlText, + maxLines = maxLines, + linkResolver = { href -> + status.resolveLink(href) + }, + positionWrapper = it + ) + } + } else { HtmlText( modifier = Modifier.fillMaxWidth(), htmlText = status.htmlText, @@ -93,7 +106,7 @@ fun ColumnScope.StatusText( linkResolver = { href -> status.resolveLink(href) }, - positionWrapper = it + positionWrapper = null ) } if (showMastodonPoll && status.platformType == PlatformType.Mastodon && status.poll != null) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index dd8824d78..537495b76 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -161,7 +161,8 @@ private fun NormalStatus( Spacer(modifier = Modifier.height(NormalStatusDefaults.ContentSpacing)) } } - } + }, + isSelectionAble = false, ) } } @@ -368,6 +369,7 @@ fun StatusContent( lineUp: Boolean = false, threadStyle: StatusThreadStyle = StatusThreadStyle.NONE, footer: @Composable () -> Unit = {}, + isSelectionAble: Boolean = true, ) { val layoutDirection = LocalLayoutDirection.current val status = data.retweet ?: data @@ -462,14 +464,22 @@ fun StatusContent( when (type) { StatusContentType.Normal -> { Spacer(modifier = Modifier.height(StatusContentDefaults.Normal.BodySpacing)) - StatusBody(status, type = type) + StatusBody( + status = status, + type = type, + isSelectionAble = isSelectionAble, + ) } StatusContentType.Extend -> UserScreenName(status.user) } if (type == StatusContentType.Extend) { Column { Spacer(modifier = Modifier.height(StatusContentDefaults.Extend.BodySpacing)) - StatusBody(status = status, type = type) + StatusBody( + status = status, + type = type, + isSelectionAble = isSelectionAble + ) } } Column { @@ -530,9 +540,11 @@ object StatusContentDefaults { fun ColumnScope.StatusBody( status: UiStatus, type: StatusContentType, + isSelectionAble: Boolean, ) { StatusText( status = status, + isSelectionAble = isSelectionAble ) StatusBodyMedia(status) @@ -703,6 +715,7 @@ fun StatusQuote(quote: UiStatus) { StatusText( status = quote, maxLines = 5, + isSelectionAble = false ) StatusBodyMedia(status = quote) } From 971916a093a8d6673b56d6c6a3e11095982bb287 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 18:08:27 +0800 Subject: [PATCH 554/615] cleanup status action button --- .../component/status/StatusActions.kt | 88 +++++++++---------- .../status/TimelineStatusComponent.kt | 17 ++-- 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt index ca6d99888..f4d252db6 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusActions.kt @@ -26,6 +26,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -75,14 +76,16 @@ fun ReplyButton( status: UiStatus, withNumber: Boolean = true, ) { - val navigator = LocalNavigator.current val icon = painterResource(res = com.twidere.twiderex.MR.files.ic_corner_up_left) val contentDescription = stringResource(res = com.twidere.twiderex.MR.strings.accessibility_common_status_actions_reply) + + val navigator = LocalNavigator.current val action = { navigator.compose(ComposeType.Reply, statusKey = status.statusKey) } - val data = status.retweet ?: status + if (withNumber) { + val data = status.retweet ?: status StatusActionButtonWithNumbers( modifier = modifier, icon = icon, @@ -388,55 +391,44 @@ private fun StatusActionButtonWithNumbers( onClick: () -> Unit, ) { val contentColor = color.copy(LocalContentAlpha.current) - Box( - modifier = modifier, + val source = remember { MutableInteractionSource() } + Row( + modifier = modifier + .defaultMinSize( + minHeight = ButtonDefaults.MinHeight + ) + .clickable( + onClick = onClick, + enabled = enabled, + interactionSource = source, + indication = null, + ) + .padding(StatusActionsDefaults.ContentPadding), + verticalAlignment = Alignment.CenterVertically, ) { - val source = remember { - MutableInteractionSource() - } - Row( + Icon( modifier = Modifier - .defaultMinSize( - minHeight = ButtonDefaults.MinHeight - ) - .clickable( - onClick = onClick, - enabled = enabled, - interactionSource = source, - indication = null, - ) - .padding(StatusActionsDefaults.ContentPadding), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - modifier = Modifier - .size(MaterialTheme.typography.body1.fontSize.value.dp) - .indication( - source, - rememberRipple( - bounded = false, - radius = rippleSize - ) - ), - tint = contentColor, - painter = icon, - contentDescription = contentDescription, - ) - Row( - modifier = modifier - .weight(1f), - ) { - if (count > 0) { - Box(modifier = Modifier.width(4.dp)) - Text( - text = count.humanizedCount(), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.body2, - color = contentColor, + .size(MaterialTheme.typography.body1.fontSize.value.dp) + .indication( + source, + rememberRipple( + bounded = false, + radius = rippleSize ) - } - } + ), + tint = contentColor, + painter = icon, + contentDescription = contentDescription, + ) + if (count > 0) { + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = count.humanizedCount(), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.body2, + color = contentColor, + ) } } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 537495b76..16825a337 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -24,6 +24,7 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -153,10 +154,7 @@ private fun NormalStatus( footer = { Column { if (showActions) { - Row { - Spacer(modifier = Modifier.width(UserAvatarDefaults.AvatarSize)) - StatusActions(data) - } + StatusActions(data) } else { Spacer(modifier = Modifier.height(NormalStatusDefaults.ContentSpacing)) } @@ -323,10 +321,13 @@ private fun StatusActions(status: UiStatus) { CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.medium, ) { - Row { - ReplyButton(modifier = Modifier.weight(1f), status = status) - RetweetButton(modifier = Modifier.weight(1f), status = status) - LikeButton(modifier = Modifier.weight(1f), status = status) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + ReplyButton(status = status) + RetweetButton(status = status) + LikeButton(status = status) ShareButton(status = status, compat = true) } } From 4a8ac769be60b395204e6de0f5948538f6c42ac8 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 18:10:03 +0800 Subject: [PATCH 555/615] use AnimatedContent to switch media --- .../status/TimelineStatusComponent.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 16825a337..93af9ce03 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -20,7 +20,7 @@ */ package com.twidere.twiderex.component.status -import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -629,17 +629,16 @@ private fun ColumnScope.StatusBodyMedia( val navigator = LocalNavigator.current if (status.media.any()) { Spacer(modifier = Modifier.height(StatusBodyMediaDefaults.Spacing)) - AnimatedVisibility(visible = LocalDisplayPreferences.current.mediaPreview) { - StatusMediaComponent( - status = status, - ) - } - AnimatedVisibility(visible = !LocalDisplayPreferences.current.mediaPreview) { - CompositionLocalProvider( - LocalContentAlpha provides ContentAlpha.medium - ) { - MediaPreviewButton { - navigator.media(statusKey = status.statusKey) + AnimatedContent(LocalDisplayPreferences.current.mediaPreview) { mediaPreview -> + if (mediaPreview) { + StatusMediaComponent(status = status) + } else { + CompositionLocalProvider( + LocalContentAlpha provides ContentAlpha.medium + ) { + MediaPreviewButton { + navigator.media(statusKey = status.statusKey) + } } } } From 6dcadeb900dd8b8fe849ecdf2b7de40258ab6102 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 18:10:18 +0800 Subject: [PATCH 556/615] fixed height of MediaPreviewButton to avoid parent height by cutting --- .../twiderex/component/status/TimelineStatusComponent.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index 93af9ce03..ae0915ce1 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -666,7 +666,8 @@ fun MediaPreviewButton( onClick.invoke() } ) - .padding(4.dp) + .padding(horizontal = 4.dp) + .height(30.dp) ) { Icon( painter = painterResource(res = com.twidere.twiderex.MR.files.ic_photo), From ec97ee5777d71b464a51caa213e61c24d468626d Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 18:17:20 +0800 Subject: [PATCH 557/615] save listState when timeline list is top --- .../kotlin/com/twidere/twiderex/component/TimelineComponent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index ecde09812..36059cb11 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -87,7 +87,6 @@ fun TimelineComponent( .distinctUntilChanged() .filter { !it } .filter { listState.layoutInfo.totalItemsCount != 0 } - .filter { listState.firstVisibleItemIndex != 0 && listState.firstVisibleItemScrollOffset != 0 } .collect { viewModel.saveScrollState( TimelineScrollState( From cb3282c3252e38e6270a82c84fdab3a17256776d Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Thu, 30 Dec 2021 18:23:00 +0800 Subject: [PATCH 558/615] open sensitive in twitter --- .../twiderex/component/status/StatusMediaComponent.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 5fcffc68f..0eee802bb 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -131,7 +131,7 @@ fun StatusMediaComponent( modifier = Modifier .weight(1f) .fillMaxSize(), - sensitive = sensitive, + sensitive = sensitive && status.platformType === PlatformType.Mastodon, onClick = onItemClick, ) } @@ -148,7 +148,7 @@ fun StatusMediaComponent( modifier = Modifier .weight(1f) .fillMaxSize(), - sensitive = sensitive, + sensitive = sensitive && status.platformType === PlatformType.Mastodon, onClick = onItemClick, ) if (it != media.last()) { @@ -169,7 +169,7 @@ fun StatusMediaComponent( StatusMediaPreviewItem( media = it, onClick = onItemClick, - sensitive = sensitive + sensitive = sensitive && status.platformType === PlatformType.Mastodon ) } } @@ -250,6 +250,7 @@ object StatusMediaDefaults { object Mastodon { val IconSpacing = 8.dp } + object Icon { val ContentPadding = 6.dp } From 28cbbb7324815900e391de5d97289c66f99e24f0 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Fri, 31 Dec 2021 15:02:24 +0800 Subject: [PATCH 559/615] add enable parameter to SelectionContainer --- .../component/status/SelectionContainer.kt | 5 +++++ .../twiderex/component/status/StatusText.kt | 16 ++-------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt index dd12ed893..66a6337ba 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt @@ -45,8 +45,13 @@ class PositionWrapper { @Composable fun SelectionContainer( modifier: Modifier = Modifier, + enable: Boolean = true, content: @Composable (PositionWrapper?) -> Unit, ) { + if (!enable) { + content.invoke(null) + return + } val positionWrapper = remember { if (currentPlatform != Platform.Android) PositionWrapper() else null } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt index e0b95fdbc..2e414d500 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusText.kt @@ -86,19 +86,7 @@ fun ColumnScope.StatusText( } AnimatedVisibility(visible = expanded) { Column { - if (isSelectionAble) { - SelectionContainer { - HtmlText( - modifier = Modifier.fillMaxWidth(), - htmlText = status.htmlText, - maxLines = maxLines, - linkResolver = { href -> - status.resolveLink(href) - }, - positionWrapper = it - ) - } - } else { + SelectionContainer(enable = isSelectionAble) { HtmlText( modifier = Modifier.fillMaxWidth(), htmlText = status.htmlText, @@ -106,7 +94,7 @@ fun ColumnScope.StatusText( linkResolver = { href -> status.resolveLink(href) }, - positionWrapper = null + positionWrapper = it ) } if (showMastodonPoll && status.platformType == PlatformType.Mastodon && status.poll != null) { From 583e88a7e8859f5721a41bcd113c034ea49a1c86 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 31 Dec 2021 16:07:59 +0800 Subject: [PATCH 560/615] version 1.6.0-dev02 --- buildSrc/src/main/kotlin/Package.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index 767ddd9bc..8338156ca 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -11,7 +11,7 @@ object Package { const val main = "1" const val mirror = "6" const val patch = "0" - const val revision = "dev01" - const val build = 56 + const val revision = "dev02" + const val build = 57 } } From ee8fe5827b33f91f6f1fb0a0b99110eed60da599 Mon Sep 17 00:00:00 2001 From: huixing Date: Fri, 31 Dec 2021 16:46:07 +0800 Subject: [PATCH 561/615] fix desktop save file --- .../kotlin/com/twidere/twiderex/kmp/FileResolver.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt index 506e8c7ab..3ba548cc9 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/FileResolver.kt @@ -44,7 +44,11 @@ actual class FileResolver { } actual fun openOutputStream(file: String): OutputStream? { - return File(file).takeIf { it.exists() && it.isFile }?.outputStream() + return File(file).apply { + if (!exists()) { + createNewFile() + } + }.takeIf { it.exists() && it.isFile }?.outputStream() } actual fun getMediaSize(file: String): MediaSize { From 17309dced3cf6193dcf3c56e3a74bcfcdb100672 Mon Sep 17 00:00:00 2001 From: seiko <605590140@qq.com> Date: Tue, 4 Jan 2022 17:17:19 +0800 Subject: [PATCH 562/615] optimize loadMore for timeline --- .../component/lazy/ui/LazyUiStatusList.kt | 63 ++++++++++++------- .../mediator/paging/PagingWithGapMediator.kt | 6 +- .../viewmodel/timeline/TimelineViewModel.kt | 20 ++++-- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt index 1ce83749d..afad47bdd 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/lazy/ui/LazyUiStatusList.kt @@ -77,7 +77,6 @@ import com.twidere.twiderex.component.status.TimelineStatusComponent import com.twidere.twiderex.component.stringResource import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.ui.UiStatus -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.mapNotNull @@ -86,11 +85,17 @@ import kotlinx.coroutines.flow.mapNotNull class LazyUiStatusListState( initialStatusKey: MicroBlogKey? = null, initialShowCursor: Boolean = false, + initialIsAutoLoadMore: Boolean = false, ) { - private var _statusKey by mutableStateOf(initialStatusKey) - private var _showCursor by mutableStateOf(initialShowCursor) + private var _statusKey: MicroBlogKey? by mutableStateOf(initialStatusKey) + private var _showCursor: Boolean by mutableStateOf(initialShowCursor) + val showCursor get() = _showCursor val statusKey get() = _statusKey + + var isAutoLoadMore: Boolean = initialIsAutoLoadMore + private set + fun update(newKey: MicroBlogKey) { if (!showCursor) { _showCursor = statusKey != null && statusKey != newKey @@ -102,13 +107,26 @@ class LazyUiStatusListState( _showCursor = false } + fun autoLoadMore() { + isAutoLoadMore = true + } + companion object { val Saver: Saver = listSaver( - save = { listOfNotNull(it.showCursor, it.statusKey?.toString()) }, + save = { + listOfNotNull( + it.showCursor, + it.isAutoLoadMore, + it.statusKey?.toString() + ) + }, restore = { LazyUiStatusListState( initialShowCursor = it[0] as Boolean, - initialStatusKey = it.getOrNull(1)?.let { MicroBlogKey.valueOf(it.toString()) } + initialIsAutoLoadMore = it[1] as Boolean, + initialStatusKey = it.getOrNull(2)?.let { key -> + MicroBlogKey.valueOf(key.toString()) + } ) } ) @@ -184,7 +202,22 @@ fun LazyUiStatusList( Divider() } item.isGap -> { - LoadMoreButton(items, index, onLoadBetweenClicked, item) + fun onLoadBetweenClicked() { + items.peek(index + 1)?.let { next -> + onLoadBetweenClicked(item.statusKey, next.statusKey) + } + } + + if (listState.isAutoLoadMore) { + LaunchedEffect(item.statusKey) { + onLoadBetweenClicked() + } + } else { + LoadMoreButton { + onLoadBetweenClicked() + listState.autoLoadMore() + } + } } else -> { StatusDivider() @@ -227,25 +260,11 @@ fun LazyUiStatusList( } @Composable -private fun LoadMoreButton( - items: LazyPagingItems, - index: Int, - onLoadBetweenClicked: (current: MicroBlogKey, next: MicroBlogKey) -> Unit, - item: UiStatus -) { +private fun LoadMoreButton(onClick: () -> Unit) { Box( modifier = Modifier .background(LocalContentColor.current.copy(alpha = 0.04f)) - .clickable { - items - .peek(index + 1) - ?.let { next -> - onLoadBetweenClicked( - item.statusKey, - next.statusKey, - ) - } - } + .clickable { onClick() } .fillMaxWidth(), contentAlignment = Alignment.Center, ) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt index 9c78b0785..b5403ca09 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/paging/mediator/paging/PagingWithGapMediator.kt @@ -82,7 +82,11 @@ abstract class PagingWithGapMediator( } } return loadBetween( - pageSize = state.config.pageSize, + pageSize = if (loadType === LoadType.REFRESH) { + state.config.initialLoadSize + } else { + state.config.pageSize + }, maxStatusKey = maxStatusKey, sinceStatusKey = sinceStatueKey ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index f8a0ee0da..38e7d1c1b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -24,10 +24,12 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey +import androidx.paging.PagingConfig +import androidx.paging.PagingData import androidx.paging.cachedIn -import com.twidere.twiderex.defaultLoadCount import com.twidere.twiderex.extensions.asStateIn import com.twidere.twiderex.model.MicroBlogKey +import com.twidere.twiderex.model.ui.UiStatus import com.twidere.twiderex.paging.mediator.paging.PagingWithGapMediator import com.twidere.twiderex.paging.mediator.paging.pager import com.twidere.twiderex.paging.mediator.paging.toUi @@ -48,9 +50,14 @@ abstract class TimelineViewModel( abstract val savedStateKey: Flow @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) - val source by lazy { + val source: Flow> by lazy { pagingMediator.mapNotNull { it }.flatMapLatest { - it.pager().toUi() + it.pager( + config = PagingConfig( + pageSize = timelinePageSize, + initialLoadSize = timelineInitialLoadSize + ) + ).toUi() }.cachedIn(viewModelScope) } @@ -82,7 +89,7 @@ abstract class TimelineViewModel( sinceStatueKey: MicroBlogKey, ) = viewModelScope.launch { pagingMediator.firstOrNull()?.loadBetween( - defaultLoadCount, + pageSize = timelinePageSize, maxStatusKey = maxStatusKey, sinceStatusKey = sinceStatueKey ) @@ -99,6 +106,11 @@ abstract class TimelineViewModel( } } } + + companion object { + private const val timelinePageSize = 20 + private const val timelineInitialLoadSize = 40 + } } data class TimelineScrollState( From e24947d23d245e6ab96f783aa7a3266c8e48dfcc Mon Sep 17 00:00:00 2001 From: enaix Date: Thu, 27 Jan 2022 13:51:08 +0300 Subject: [PATCH 563/615] Enable sensitive content blur for Twitter --- .../twiderex/component/status/StatusMediaComponent.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index 0eee802bb..f09f49d56 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -131,7 +131,7 @@ fun StatusMediaComponent( modifier = Modifier .weight(1f) .fillMaxSize(), - sensitive = sensitive && status.platformType === PlatformType.Mastodon, + sensitive = sensitive, onClick = onItemClick, ) } @@ -148,7 +148,7 @@ fun StatusMediaComponent( modifier = Modifier .weight(1f) .fillMaxSize(), - sensitive = sensitive && status.platformType === PlatformType.Mastodon, + sensitive = sensitive, onClick = onItemClick, ) if (it != media.last()) { @@ -169,7 +169,7 @@ fun StatusMediaComponent( StatusMediaPreviewItem( media = it, onClick = onItemClick, - sensitive = sensitive && status.platformType === PlatformType.Mastodon + sensitive = sensitive ) } } @@ -352,3 +352,4 @@ fun StatusMediaPreviewItem( } } } + From baa6b82717885e56163a74d10d9660f05432a0f1 Mon Sep 17 00:00:00 2001 From: huixing Date: Mon, 28 Mar 2022 23:03:48 +0800 Subject: [PATCH 564/615] fix crashed on android 12L --- common/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 751cd5de0..c5eb1dd3f 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -81,6 +81,7 @@ kotlin { implementation("androidx.core:core-ktx:1.8.0-alpha02") implementation("io.insert-koin:koin-android:${Versions.koin}") implementation("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") + implementation("androidx.work:work-runtime-ktx:${Versions.work}") implementation("androidx.room:room-runtime:${Versions.room}") implementation("androidx.room:room-ktx:${Versions.room}") implementation("androidx.room:room-paging:${Versions.room}") From 7cb3bc076130e65651faa0f649cbd3b543268c79 Mon Sep 17 00:00:00 2001 From: huixing Date: Sat, 2 Apr 2022 22:01:21 +0800 Subject: [PATCH 565/615] support currentBackStackEntryAsState --- .../precompose/navigation/Navigator.kt | 19 ++++++------------- .../precompose/navigation/RouteStack.kt | 13 +++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt index 6562e8270..eb11b1f66 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/Navigator.kt @@ -21,6 +21,8 @@ package moe.tlaster.precompose.navigation import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember /** @@ -92,16 +94,7 @@ class NavController { get() = stackManager?.canGoBack ?: false } -// @Composable -// fun NavController.currentBackStackEntryAsState(): State { -// val currentNavBackStackEntry = remember { mutableStateOf(stackManager.currentBackStackEntry) } -// // setup the onDestinationChangedListener responsible for detecting when the -// // current back stack entry changes -// DisposableEffect(this) { -// addOnDestinationChangedListener(callback) -// onDispose { -// removeOnDestinationChangedListener(callback) -// } -// } -// return currentNavBackStackEntry -// } +@Composable +fun NavController.currentBackStackEntryAsState(): State? { + return stackManager?.currentStack?.currentBackStackEntryFlow?.collectAsState(null) +} diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt index ea7d49248..b6993780f 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteStack.kt @@ -23,6 +23,10 @@ package moe.tlaster.precompose.navigation import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.snapshots.SnapshotStateList +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow import moe.tlaster.precompose.navigation.transition.NavTransition @Stable @@ -35,6 +39,12 @@ internal class RouteStack( val currentEntry: BackStackEntry? get() = stacks.lastOrNull() + private val _currentBackStackEntryFlow: MutableSharedFlow = + MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + + val currentBackStackEntryFlow: Flow = + _currentBackStackEntryFlow.asSharedFlow() + val canGoBack: Boolean get() = stacks.size > 1 @@ -46,6 +56,9 @@ internal class RouteStack( fun onActive() { currentEntry?.active() + currentEntry?.let { + _currentBackStackEntryFlow.tryEmit(it) + } } fun onInActive() { From 7a1d2350cedfe752e8abc9c5025d9e2cbef84a5a Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 6 Apr 2022 21:12:16 +0800 Subject: [PATCH 566/615] add debugTool (currently only working on android) --- .../com/twidere/twiderex/TwidereXActivity.kt | 3 +- .../twiderex/navigation/ComposeDebugTool.kt | 80 +++++++++++++++++++ .../com/twidere/twiderex/navigation/Router.kt | 6 +- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 78979d989..611c15417 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -187,7 +187,8 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { windowInsetsAnimationsEnabled = true ) { Router( - navController = navController + navController = navController, + isDebug = moe.tlaster.kfilepicker.BuildConfig.DEBUG ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt new file mode 100644 index 000000000..86d6b08fa --- /dev/null +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt @@ -0,0 +1,80 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.navigation + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.delay +import moe.tlaster.precompose.navigation.NavController +import moe.tlaster.precompose.navigation.currentBackStackEntryAsState + +@Composable +fun ComposeDebugTool( + rootNavController: NavController, +) { + + var showDebug by remember { + mutableStateOf(false) + } + + LaunchedEffect(Unit) { + delay(3000) + showDebug = true + } + + if (!showDebug) { + return + } + val state by rootNavController.currentBackStackEntryAsState()!! + var debugOpen by remember { + mutableStateOf(false) + } + Column(modifier = Modifier.fillMaxSize()) { + Button( + modifier = Modifier.padding(top = 56.dp), + onClick = { + debugOpen = !debugOpen + } + ) { + Text("Debug") + } + if (debugOpen) { + Text( + modifier = Modifier.background(MaterialTheme.colors.surface), + text = state?.route?.route ?: "UnKnow route", + color = MaterialTheme.colors.primary + ) + } + } +} diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt index ac2cd9472..c9f9c1fb4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/Router.kt @@ -33,7 +33,8 @@ import moe.tlaster.precompose.navigation.rememberNavController @Composable fun Router( - navController: NavController = rememberNavController() + navController: NavController = rememberNavController(), + isDebug: Boolean = false ) { val remoteNavigator = LocalRemoteNavigator.current CompositionLocalProvider( @@ -45,5 +46,8 @@ fun Router( route(constraints) } } + if (isDebug) { + ComposeDebugTool(navController) + } } } From ccfecb830764eca01721908a6752875298be55df Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 6 Apr 2022 21:14:18 +0800 Subject: [PATCH 567/615] support draggable --- .../twiderex/navigation/ComposeDebugTool.kt | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt index 86d6b08fa..e8fdf90c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt @@ -21,8 +21,11 @@ package com.twidere.twiderex.navigation import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.MaterialTheme @@ -34,10 +37,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.consumeAllChanges +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import moe.tlaster.precompose.navigation.NavController import moe.tlaster.precompose.navigation.currentBackStackEntryAsState +import kotlin.math.roundToInt @Composable fun ComposeDebugTool( @@ -60,21 +67,36 @@ fun ComposeDebugTool( var debugOpen by remember { mutableStateOf(false) } - Column(modifier = Modifier.fillMaxSize()) { - Button( - modifier = Modifier.padding(top = 56.dp), - onClick = { - debugOpen = !debugOpen - } + + Box(modifier = Modifier.fillMaxSize()) { + var offsetX by remember { mutableStateOf(0f) } + var offsetY by remember { mutableStateOf(0f) } + Column( + modifier = Modifier + .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) } + .pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consumeAllChanges() + offsetX += dragAmount.x + offsetY += dragAmount.y + } + } ) { - Text("Debug") - } - if (debugOpen) { - Text( - modifier = Modifier.background(MaterialTheme.colors.surface), - text = state?.route?.route ?: "UnKnow route", - color = MaterialTheme.colors.primary - ) + Button( + modifier = Modifier.padding(top = 56.dp), + onClick = { + debugOpen = !debugOpen + } + ) { + Text("Debug") + } + if (debugOpen) { + Text( + modifier = Modifier.background(MaterialTheme.colors.surface), + text = state?.route?.route ?: "UnKnow route", + color = MaterialTheme.colors.primary + ) + } } } } From 8f787aca65ed2d3768f17911f016f8d029a89096 Mon Sep 17 00:00:00 2001 From: huixing Date: Thu, 7 Apr 2022 13:24:36 +0800 Subject: [PATCH 568/615] =?UTF-8?q?fix=20viewModel=20create=20=EF=BC=88dif?= =?UTF-8?q?ferent=20params=20should=20create=20different=20instance=20?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt index a08455853..e1b7a0192 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/di/ext/ViewModelExt.kt @@ -73,7 +73,10 @@ fun ViewModelStoreOwner.getViewModel( clazz: KClass, parameters: ParametersDefinition? = null, ): T { - return this.viewModelStore.getViewModel(qualifier?.value ?: clazz.toString(), clazz) { + return this.viewModelStore.getViewModel( + key = qualifier?.value ?: clazz.toString() + parameters?.invoke(), + clazz = clazz + ) { KoinPlatformTools.defaultContext().get().get(clazz, qualifier, parameters) } } From bf1f6d4da5e26746988651c4b418a0f23511265e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Thu, 7 Apr 2022 15:07:57 +0800 Subject: [PATCH 569/615] upgrade compose-jb and other dependencies --- buildSrc/src/main/kotlin/Versions.kt | 16 ++++++++-------- common/build.gradle.kts | 1 + .../com/twidere/twiderex/kmp/ImagePainter.kt | 7 +++---- .../kotlin/com/twidere/twiderex/kmp/ResLoader.kt | 7 ++++--- .../jvmMain/kotlin/com/twidere/twiderex/main.kt | 2 ++ 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a647f0bd2..6a8d1fcd4 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -19,19 +19,19 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.0.1" + const val compose_jb = "1.1.1" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" const val androidx_hilt = "1.0.0" - const val room = "2.4.0" - const val lifecycle = "2.4.0" - const val lifecycle_compose = "2.4.0" + const val room = "2.4.2" + const val lifecycle = "2.4.1" + const val lifecycle_compose = "2.4.1" const val work = "2.7.1" const val startup = "1.1.0" - const val coil = "2.0.0-alpha05" - const val accompanist = "0.22.0-rc" - const val accompanist_jb = "0.18.1" + const val coil = "2.0.0-rc02" + const val accompanist = "0.23.0" + const val accompanist_jb = "0.20.1" const val androidx_exifinterface = "1.3.3" const val exoplayer = "2.16.1" const val browser = "1.4.0" @@ -39,7 +39,7 @@ object Versions { const val androidx_test = "1.4.1-alpha03" const val extJUnitVersion = "1.1.4-alpha03" const val espressoVersion = "3.5.0-alpha03" - const val koin = "3.1.4" + const val koin = "3.2.0-beta-1" const val moko = "0.17.3" const val sqlDelight = "1.5.3" const val javafx = "0.0.10" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 751cd5de0..9af9d7e52 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -48,6 +48,7 @@ kotlin { api(compose.materialIconsExtended) implementation(projects.services) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:${Versions.Kotlin.coroutines}") api("androidx.paging:paging-common:${Versions.paging}") api("androidx.datastore:datastore-core:${Versions.datastore}") api("androidx.datastore:datastore-preferences-core:${Versions.datastore}") diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 4a7baf260..12ad5354d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -27,11 +27,11 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import coil.ImageLoader import coil.annotation.ExperimentalCoilApi -import coil.compose.LocalImageLoader import coil.compose.rememberAsyncImagePainter import coil.decode.GifDecoder import coil.decode.ImageDecoderDecoder import coil.disk.DiskCache +import coil.imageLoader import coil.request.ErrorResult import coil.request.ImageRequest import coil.request.SuccessResult @@ -112,9 +112,8 @@ internal actual fun rememberNetworkImagePainter( @OptIn(ExperimentalCoilApi::class) @Composable private fun buildImageLoader(cacheDir: String): ImageLoader { - val context = LocalContext.current val httpConfig = LocalHttpConfig.current - return LocalImageLoader.current + return LocalContext.current.imageLoader .newBuilder() .apply { if (httpConfig.proxyConfig.enable && @@ -126,7 +125,7 @@ private fun buildImageLoader(cacheDir: String): ImageLoader { .build() } diskCache { - DiskCache.Builder(context) + DiskCache.Builder() .directory(File(cacheDir)) .build() } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt index 7907383f3..b3b8e7474 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/ResLoader.kt @@ -23,9 +23,10 @@ package com.twidere.twiderex.kmp import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -import coil.compose.LocalImageLoader +import androidx.compose.ui.platform.LocalContext import coil.compose.rememberAsyncImagePainter import coil.decode.SvgDecoder +import coil.imageLoader import dev.icerock.moko.resources.FileResource import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.StringResource @@ -46,7 +47,7 @@ actual class ResLoader( val data = "android.resource://${context.packageName}/raw/${context.resources.getResourceEntryName(res.rawResId)}" return rememberAsyncImagePainter( model = data, - imageLoader = LocalImageLoader.current.newBuilder() + imageLoader = LocalContext.current.imageLoader.newBuilder() .components { add(SvgDecoder.Factory()) } .build(), ) @@ -58,7 +59,7 @@ actual class ResLoader( val data = "android.resource://${context.packageName}/drawable/${context.resources.getResourceEntryName(res.drawableResId)}" return rememberAsyncImagePainter( model = data, - imageLoader = LocalImageLoader.current.newBuilder() + imageLoader = LocalContext.current.imageLoader.newBuilder() .components { add(SvgDecoder.Factory()) } .build(), ) diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index c615ddd20..9caa4976c 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -23,9 +23,11 @@ package com.twidere.twiderex import androidx.compose.ui.ExperimentalComposeUiApi import com.twidere.twiderex.component.foundation.DesktopMediaPlayerHelper import com.twidere.twiderex.media.DesktopMediaPlayerFactoryImpl +import kotlinx.coroutines.Dispatchers @ExperimentalComposeUiApi fun main(args: Array) { DesktopMediaPlayerHelper.register(DesktopMediaPlayerFactoryImpl()) + Dispatchers runDesktopApp(args) } From 64220fea3fe251fa4c9a0a4e623f7ccb4e8ab99e Mon Sep 17 00:00:00 2001 From: itsMimao Date: Fri, 8 Apr 2022 16:46:02 +0800 Subject: [PATCH 570/615] check if channel is closed before send --- .../com/twidere/twiderex/utils/CustomTabSignInChannel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt index 6dcb64b16..38cd57eb5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/utils/CustomTabSignInChannel.kt @@ -28,8 +28,9 @@ object CustomTabSignInChannel { private var waiting = false private val channel: Channel = Channel() + @OptIn(ExperimentalCoroutinesApi::class) suspend fun send(uri: String) { - if (waiting) { + if (waiting && !channel.isClosedForSend) { channel.send(uri) } waiting = false From e5a40c36237e62c1d793da520cc5103ca69df049 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 15 Apr 2022 15:56:13 +0800 Subject: [PATCH 571/615] fix windows startup crash --- desktop/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 02002f141..06ffa51a1 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -36,6 +36,7 @@ compose { desktop { application { mainClass = "com.twidere.twiderex.MainKt" + jvmArgs += listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED") nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = Package.name From 40a894b1376c0474a677ed34f4cc98c86124e574 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 15 Apr 2022 15:58:47 +0800 Subject: [PATCH 572/615] upgrade jvm target to 17 --- buildSrc/src/main/kotlin/Versions.kt | 4 ++-- desktop/build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 6a8d1fcd4..31c6d42fb 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -8,8 +8,8 @@ object Versions { } object Java { - const val jvmTarget = "11" - val java = JavaVersion.VERSION_11 + const val jvmTarget = "17" + val java = JavaVersion.VERSION_17 } const val ksp = "${Kotlin.lang}-1.0.2" diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 02002f141..6d0f90266 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { kotlinOptions.jvmTarget = Versions.Java.jvmTarget } javafx { - version = "15" // MediaPlayer doesn't work well with 11, use Versions.Java.jvmTarget after Versions.Java.jvmTarget updated + version = Versions.Java.jvmTarget modules = listOf("javafx.controls", "javafx.swing", "javafx.media") } } From e40db53363cca15f7656d68133a748073539e379 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 15 Apr 2022 16:02:34 +0800 Subject: [PATCH 573/615] apply spotless --- desktop/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 06ffa51a1..9a4744fbc 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -36,7 +36,7 @@ compose { desktop { application { mainClass = "com.twidere.twiderex.MainKt" - jvmArgs += listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED") + jvmArgs += listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED") nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) packageName = Package.name From 88e0a31f8c0c339e17d7536beb4e5424dffd37b1 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 15 Apr 2022 16:03:14 +0800 Subject: [PATCH 574/615] fix build --- .../twidere/twiderex/component/status/StatusMediaComponent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt index f09f49d56..257071345 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/StatusMediaComponent.kt @@ -352,4 +352,3 @@ fun StatusMediaPreviewItem( } } } - From d17496cf803e5e1fd2b9c5f7395d7f6821118a7d Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 18 Apr 2022 10:56:47 +0800 Subject: [PATCH 575/615] upgrade version --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3e000b9fe..c72892346 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 \ No newline at end of file +distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda \ No newline at end of file From 3ebb41a1cb39245878df12bc2c90277372aba24b Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 18 Apr 2022 10:57:24 +0800 Subject: [PATCH 576/615] Revert "upgrade jvm target to 17" --- buildSrc/src/main/kotlin/Versions.kt | 4 ++-- desktop/build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 31c6d42fb..6a8d1fcd4 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -8,8 +8,8 @@ object Versions { } object Java { - const val jvmTarget = "17" - val java = JavaVersion.VERSION_17 + const val jvmTarget = "11" + val java = JavaVersion.VERSION_11 } const val ksp = "${Kotlin.lang}-1.0.2" diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 499d545be..9a4744fbc 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { kotlinOptions.jvmTarget = Versions.Java.jvmTarget } javafx { - version = Versions.Java.jvmTarget + version = "15" // MediaPlayer doesn't work well with 11, use Versions.Java.jvmTarget after Versions.Java.jvmTarget updated modules = listOf("javafx.controls", "javafx.swing", "javafx.media") } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c72892346..3e000b9fe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda \ No newline at end of file +distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 \ No newline at end of file From 40c8f34cb90cad4af140736dfabcc3970e2093b6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 18 Apr 2022 11:07:05 +0800 Subject: [PATCH 577/615] upgrade dependency version --- buildSrc/src/main/kotlin/Versions.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 31c6d42fb..fb47c2771 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,10 +12,10 @@ object Versions { val java = JavaVersion.VERSION_17 } - const val ksp = "${Kotlin.lang}-1.0.2" + const val ksp = "${Kotlin.lang}-1.0.4" const val agp = "7.0.4" - const val spotless = "6.0.5" - const val ktlint = "0.43.2" + const val spotless = "6.4.2" + const val ktlint = "0.45.2" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" @@ -29,18 +29,18 @@ object Versions { const val lifecycle_compose = "2.4.1" const val work = "2.7.1" const val startup = "1.1.0" - const val coil = "2.0.0-rc02" + const val coil = "2.0.0-rc03" const val accompanist = "0.23.0" const val accompanist_jb = "0.20.1" const val androidx_exifinterface = "1.3.3" - const val exoplayer = "2.16.1" + const val exoplayer = "2.17.1" const val browser = "1.4.0" - const val protobuf = "3.19.0" + const val protobuf = "3.20.0" const val androidx_test = "1.4.1-alpha03" const val extJUnitVersion = "1.1.4-alpha03" const val espressoVersion = "3.5.0-alpha03" const val koin = "3.2.0-beta-1" - const val moko = "0.17.3" + const val moko = "0.19.0" const val sqlDelight = "1.5.3" const val javafx = "0.0.10" const val kFilePicker = "1.0.4" From 29921b3c9b25991d7de963690d5d041c1a4ea6ec Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 18 Apr 2022 11:12:53 +0800 Subject: [PATCH 578/615] migrate to jdk 17 --- buildSrc/src/main/kotlin/Versions.kt | 4 ++-- .../com/twidere/twiderex/scenes/settings/StorageScene.kt | 2 -- desktop/build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a26230ea3..fb47c2771 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -8,8 +8,8 @@ object Versions { } object Java { - const val jvmTarget = "11" - val java = JavaVersion.VERSION_11 + const val jvmTarget = "17" + val java = JavaVersion.VERSION_17 } const val ksp = "${Kotlin.lang}-1.0.4" diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt index 98a11e6ae..31650997f 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/StorageScene.kt @@ -32,8 +32,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -// import androidx.compose.ui.window.Dialog -// import androidx.compose.ui.window.DialogProperties import com.twidere.twiderex.component.foundation.AppBar import com.twidere.twiderex.component.foundation.AppBarNavigationButton import com.twidere.twiderex.component.foundation.Dialog diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 9a4744fbc..499d545be 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { kotlinOptions.jvmTarget = Versions.Java.jvmTarget } javafx { - version = "15" // MediaPlayer doesn't work well with 11, use Versions.Java.jvmTarget after Versions.Java.jvmTarget updated + version = Versions.Java.jvmTarget modules = listOf("javafx.controls", "javafx.swing", "javafx.media") } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3e000b9fe..8619119ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 \ No newline at end of file From 417285ca42e93db39c92e94571c43dfabb04c1b4 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 18 Apr 2022 11:14:24 +0800 Subject: [PATCH 579/615] fix gradle checksum --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8619119ba..c72892346 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302 \ No newline at end of file +distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda \ No newline at end of file From 09a169caf90806875c5b581a68a8bd0f045e01fa Mon Sep 17 00:00:00 2001 From: huixing Date: Wed, 20 Apr 2022 22:16:32 +0800 Subject: [PATCH 580/615] fix scroll state when switch account --- .../twiderex/component/TimelineComponent.kt | 18 +++++++----------- .../viewmodel/timeline/TimelineViewModel.kt | 8 +++++--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt index 36059cb11..b676e3e9c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/TimelineComponent.kt @@ -39,9 +39,10 @@ import com.twidere.twiderex.kmp.Platform import com.twidere.twiderex.kmp.currentPlatform import com.twidere.twiderex.viewmodel.timeline.TimelineScrollState import com.twidere.twiderex.viewmodel.timeline.TimelineViewModel -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.viewModelScope @@ -62,17 +63,12 @@ fun TimelineComponent( ) { val listState = rememberLazyListState() LaunchedEffect(Unit) { - var inited = false - val scrollState = viewModel.provideScrollState() - snapshotFlow { listState.layoutInfo.totalItemsCount } - .distinctUntilChanged() - .filter { it != 0 } - .filter { !inited } - .collect { - inited = true + viewModel.provideScrollState() + .filterNotNull() + .collectLatest { listState.scrollToItem( - scrollState.firstVisibleItemIndex, - scrollState.firstVisibleItemScrollOffset + it.firstVisibleItemIndex, + it.firstVisibleItemScrollOffset ) } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt index 38e7d1c1b..57ea51a96 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/TimelineViewModel.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch import moe.tlaster.precompose.viewmodel.ViewModel @@ -67,8 +68,9 @@ abstract class TimelineViewModel( .asStateIn(viewModelScope, emptyList()) } - suspend fun provideScrollState(): TimelineScrollState { - return savedStateKey.firstOrNull()?.let { + @OptIn(ExperimentalCoroutinesApi::class) + suspend fun provideScrollState(): Flow { + return savedStateKey.mapLatest { val firstVisibleItemIndexKey = intPreferencesKey("${it}_firstVisibleItemIndex") val firstVisibleItemScrollOffsetKey = intPreferencesKey("${it}_firstVisibleItemScrollOffset") @@ -80,7 +82,7 @@ abstract class TimelineViewModel( firstVisibleItemScrollOffset = firstVisibleItemScrollOffset, ) } - } ?: TimelineScrollState.Zero + } } @OptIn(androidx.paging.ExperimentalPagingApi::class) From e2ee160ab536c99844ba18a559cdd2af10105fb8 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 22 Apr 2022 09:38:15 +0800 Subject: [PATCH 581/615] fix build --- android/build.gradle.kts | 2 +- build.gradle.kts | 1 + buildSrc/src/main/kotlin/Versions.kt | 2 +- common/build.gradle.kts | 5 +++++ gradle.properties | 4 ++-- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 1887c1eb6..7f3dda6cb 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -50,7 +50,7 @@ android { } lint { - disable("MissingTranslation") + disable.add("MissingTranslation") } flavorDimensions.add("channel") diff --git a/build.gradle.kts b/build.gradle.kts index b9663d0ad..19674a214 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.diffplug.spotless").version(Versions.spotless) id("com.github.ben-manes.versions").version("0.39.0") + id("com.dipien.byebyejetifier") version "1.2.2" } buildscript { repositories { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index fb47c2771..141e98d28 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -13,7 +13,7 @@ object Versions { } const val ksp = "${Kotlin.lang}-1.0.4" - const val agp = "7.0.4" + const val agp = "7.2.0-rc01" const val spotless = "6.4.2" const val ktlint = "0.45.2" const val okhttp = "4.9.1" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index fcd222c81..ee39b7594 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -179,6 +179,11 @@ android { testInstrumentationRunnerArguments["notPackage"] = "com.twidere.twiderex.viewmodel" } + lint { + disable.add("MissingTranslation") + disable.add("JavascriptInterface") + } + compileOptions { sourceCompatibility = Versions.Java.java targetCompatibility = Versions.Java.java diff --git a/gradle.properties b/gradle.properties index 744f7eb87..bb5bddf64 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,10 +15,10 @@ # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true +android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.injected.testOnly=false kotlin.mpp.enableGranularSourceSetsMetadata=true -org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:+UseParallelGC android.defaults.buildfeatures.buildconfig = false \ No newline at end of file From 8e932d43773b6832d3bc2a8f8740f4e554729c46 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Fri, 22 Apr 2022 17:07:01 +0800 Subject: [PATCH 582/615] version 1.6.0-dev03 --- buildSrc/src/main/kotlin/Package.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index 8338156ca..3c5ccd4f5 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -11,7 +11,7 @@ object Package { const val main = "1" const val mirror = "6" const val patch = "0" - const val revision = "dev02" - const val build = 57 + const val revision = "dev03" + const val build = 58 } } From ee4579b20fb98a75e30a59651707c9875a0e29f5 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 27 Apr 2022 12:56:16 +0800 Subject: [PATCH 583/615] add custom title bar --- .../kotlin/com/twidere/twiderex/App.kt | 1 - .../kotlin/com/twidere/twiderex/DesktopApp.kt | 4 +- .../twiderex/component/NativeWindow.kt | 321 ++++++++++++++++++ .../tlaster/precompose/PreComposeWindow.kt | 7 +- 4 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt index 76120d32c..1adb3ab0c 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/App.kt @@ -48,7 +48,6 @@ fun App(navController: NavController = NavController()) { LocalActiveAccountViewModel provides accountViewModel, LocalStatusActions provides get(), LocalPlatformResolver provides get(), - LocalRemoteNavigator provides get(), ) { Router( navController = navController diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 27cdbca85..6c2dab7ee 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.painterResource import androidx.compose.ui.window.application +import com.twidere.twiderex.component.NativeWindow import com.twidere.twiderex.di.ext.get import com.twidere.twiderex.di.setupModules import com.twidere.twiderex.init.Initializer @@ -46,7 +47,6 @@ import it.sauronsoftware.junique.JUnique import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import moe.tlaster.kfilepicker.FilePicker -import moe.tlaster.precompose.PreComposeWindow import moe.tlaster.precompose.navigation.NavController import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -161,7 +161,7 @@ private fun startDesktopApp() { .add(TwidereServiceFactoryInitialTask()) .execute() ProvidePreferences(preferencesHolder) { - PreComposeWindow( + NativeWindow( onCloseRequest = { stopKoin() exitApplication() diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt new file mode 100644 index 000000000..935e05709 --- /dev/null +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt @@ -0,0 +1,321 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.window.WindowDraggableArea +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Fullscreen +import androidx.compose.material.icons.filled.FullscreenExit +import androidx.compose.material.icons.filled.Minimize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.FrameWindowScope +import androidx.compose.ui.window.WindowPlacement +import androidx.compose.ui.window.WindowScope +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.rememberWindowState +import com.twidere.twiderex.ui.TwidereTheme +import com.twidere.twiderex.ui.isDarkTheme +import com.twidere.twiderex.utils.OperatingSystem +import com.twidere.twiderex.utils.currentOperatingSystem +import moe.tlaster.precompose.PreComposeWindow + +@Composable +fun NativeWindow( + onCloseRequest: () -> Unit, + state: WindowState = rememberWindowState(), + visible: Boolean = true, + title: String = "Untitled", + icon: Painter? = null, + resizable: Boolean = true, + enabled: Boolean = true, + focusable: Boolean = true, + alwaysOnTop: Boolean = false, + onPreviewKeyEvent: (KeyEvent) -> Boolean = { false }, + onKeyEvent: (KeyEvent) -> Boolean = { false }, + content: @Composable FrameWindowScope.() -> Unit, +) { + PreComposeWindow( + state = state, + visible = visible, + title = title, + icon = icon, + undecorated = true, + transparent = true, + resizable = resizable, + enabled = enabled, + focusable = focusable, + alwaysOnTop = alwaysOnTop, + onPreviewKeyEvent = onPreviewKeyEvent, + onKeyEvent = onKeyEvent, + onCloseRequest = onCloseRequest, + content = { + Column( + modifier = Modifier.clip(RoundedCornerShape(8.dp)), + ) { + PlatformTitleBar( + title = title, + icon = icon, + operatingSystem = currentOperatingSystem, + onCloseRequest = onCloseRequest, + onMinimizeRequest = { state.isMinimized = true }, + onMaximizeRequest = { state.placement = WindowPlacement.Maximized }, + onUndoMaximizeRequest = { state.placement = WindowPlacement.Floating }, + isMaximized = state.placement == WindowPlacement.Maximized, + ) + content.invoke(this@PreComposeWindow) + } + }, + ) +} + +@Composable +private fun WindowScope.PlatformTitleBar( + title: String, + icon: Painter?, + operatingSystem: OperatingSystem, + onCloseRequest: () -> Unit, + onMinimizeRequest: () -> Unit, + onMaximizeRequest: () -> Unit, + onUndoMaximizeRequest: () -> Unit, + isMaximized: Boolean = false, +) { + WindowDraggableArea { + TwidereTheme(isDarkTheme()) { + Surface( + modifier = Modifier + .fillMaxWidth() + ) { + when (operatingSystem) { + OperatingSystem.MacOS -> OSXTitleBar( + title = title, + icon = icon, + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + else -> WindowsTitleBar( + title = title, + icon = icon, + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + } + } + } + } +} + +@Composable +private fun WindowsTitleBar( + title: String, + icon: Painter?, + onCloseRequest: () -> Unit, + onMinimizeRequest: () -> Unit, + onMaximizeRequest: () -> Unit, + onUndoMaximizeRequest: () -> Unit, + isMaximized: Boolean = false, +) { + Row( + modifier = Modifier + .padding( + start = 16.dp, + top = 8.dp, + end = 8.dp, + bottom = 8.dp + ) + ) { + WindowTitle( + title = title, + icon = icon, + ) + Spacer(modifier = Modifier.weight(1f)) + WindowsWindowButtons( + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + } +} + +@Composable +private fun WindowsWindowButtons( + onCloseRequest: () -> Unit, + onMinimizeRequest: () -> Unit, + onMaximizeRequest: () -> Unit, + onUndoMaximizeRequest: () -> Unit, + isMaximized: Boolean = false, +) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + WindowButton( + icon = Icons.Filled.Minimize, + onClick = onMinimizeRequest + ) + if (isMaximized) { + WindowButton( + icon = Icons.Filled.FullscreenExit, + onClick = onUndoMaximizeRequest + ) + } else { + WindowButton( + icon = Icons.Filled.Fullscreen, + onClick = onMaximizeRequest + ) + } + WindowButton( + icon = Icons.Filled.Close, + onClick = onCloseRequest + ) + } +} + +@Composable +private fun WindowButton(icon: ImageVector, onClick: () -> Unit) { + IconButton( + onClick = onClick, + modifier = Modifier.size(24.dp) + ) { + Icon(icon, contentDescription = null) + } +} + +@Composable +private fun WindowTitle( + modifier: Modifier = Modifier, + title: String, + icon: Painter?, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + if (icon != null) { + Image(painter = icon, contentDescription = null, modifier = Modifier.size(24.dp)) + Spacer(modifier = Modifier.width(8.dp)) + } + Text(text = title) + } +} + +@Composable +private fun OSXTitleBar( + title: String, + icon: Painter?, + onCloseRequest: () -> Unit, + onMinimizeRequest: () -> Unit, + onMaximizeRequest: () -> Unit, + onUndoMaximizeRequest: () -> Unit, + isMaximized: Boolean = false, +) { + Box( + modifier = Modifier + .padding(vertical = 8.dp, horizontal = 16.dp) + ) { + OSXWindowButtons( + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + WindowTitle( + title = title, + icon = icon, + ) + } + } +} + +@Composable +private fun OSXWindowButtons( + onCloseRequest: () -> Unit, + onMinimizeRequest: () -> Unit, + onMaximizeRequest: () -> Unit, + onUndoMaximizeRequest: () -> Unit, + isMaximized: Boolean = false, +) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Box( + modifier = Modifier + .size(12.dp) + .background(Color(0xFFff5150), shape = CircleShape) + .clickable(onClick = onCloseRequest) + .clip(CircleShape) + ) + Box( + modifier = Modifier + .size(12.dp) + .background(Color(0xFFffbc00), shape = CircleShape) + .clickable(onClick = onMinimizeRequest) + .clip(CircleShape) + ) + Box( + modifier = Modifier + .size(12.dp) + .background(Color(0xFF00cd1d), shape = CircleShape) + .clickable(onClick = { + if (isMaximized) { + onUndoMaximizeRequest() + } else { + onMaximizeRequest() + } + }) + .clip(CircleShape) + ) + } +} diff --git a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt index 6952c3c35..7817ebd1c 100644 --- a/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt +++ b/common/src/desktopMain/kotlin/moe/tlaster/precompose/PreComposeWindow.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.window.FrameWindowScope import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import moe.tlaster.precompose.lifecycle.Lifecycle import moe.tlaster.precompose.lifecycle.LifecycleOwner @@ -52,13 +51,14 @@ fun PreComposeWindow( title: String = "Untitled", icon: Painter? = null, undecorated: Boolean = false, + transparent: Boolean = false, resizable: Boolean = true, enabled: Boolean = true, focusable: Boolean = true, alwaysOnTop: Boolean = false, onPreviewKeyEvent: (KeyEvent) -> Boolean = { false }, onKeyEvent: (KeyEvent) -> Boolean = { false }, - content: @Composable FrameWindowScope.() -> Unit + content: @Composable FrameWindowScope.() -> Unit, ) { val holder = remember { PreComposeWindowHolder() @@ -87,6 +87,7 @@ fun PreComposeWindow( title = title, icon = icon, undecorated = undecorated, + transparent = transparent, resizable = resizable, enabled = enabled, focusable = focusable, @@ -95,7 +96,7 @@ fun PreComposeWindow( onKeyEvent = onKeyEvent, content = { content.invoke(this) - }, + } ) } } From 8d875725120ae7d6183a712f4d38a57cd84df314 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 27 Apr 2022 17:26:19 +0800 Subject: [PATCH 584/615] redesign custom title bar --- .../twiderex/component/PlatformInsets.kt | 2 +- .../platform/PlatformEmojiPanel.android.kt | 4 +- .../twiderex/component/PlatformInsets.kt | 2 +- .../kotlin/com/twidere/twiderex/ui/Theme.kt | 2 +- .../kotlin/com/twidere/twiderex/DesktopApp.kt | 3 - .../twiderex/component/NativeWindow.kt | 214 +++++++++++++----- .../twiderex/component/PlatformInsets.kt | 48 +++- 7 files changed, 201 insertions(+), 74 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 2acd33eee..7d557196b 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -189,7 +189,7 @@ actual fun ImeHeightWithInsets( } @Composable -actual fun ImeBottomInsets(): Dp { +actual fun imeBottomInsets(): Dp { val navigation = LocalWindowInsets.current.navigationBars val ime = LocalWindowInsets.current.ime ime.bottom.coerceAtLeast(navigation.bottom) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt index b9f6501aa..8a965991d 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/foundation/platform/PlatformEmojiPanel.android.kt @@ -35,8 +35,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.dp -import com.twidere.twiderex.component.ImeBottomInsets import com.twidere.twiderex.component.ImeHeightWithInsets +import com.twidere.twiderex.component.imeBottomInsets import com.twidere.twiderex.model.ui.UiEmoji import com.twidere.twiderex.model.ui.UiEmojiCategory import kotlin.math.max @@ -61,7 +61,7 @@ actual fun PlatformEmojiPanel( val targetHeight = with(LocalDensity.current) { height.toDp() } - val bottom = ImeBottomInsets() + val bottom = imeBottomInsets() var visibility by remember { mutableStateOf(false) } LaunchedEffect(showEmoji, bottom) { if (bottom == targetHeight || showEmoji) { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 89fdc1132..c2b8fa346 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -70,4 +70,4 @@ expect fun ImeHeightWithInsets( ) @Composable -expect fun ImeBottomInsets(): Dp +expect fun imeBottomInsets(): Dp diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt index 758cd5eef..1e3a02de5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/ui/Theme.kt @@ -22,7 +22,6 @@ package com.twidere.twiderex.ui import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.Colors import androidx.compose.material.LocalElevationOverlay @@ -127,6 +126,7 @@ fun TwidereScene( extendToBottom = extendViewIntoNavigationBar, extendToStart = extendViewIntoNavigationBar, extendToEnd = extendViewIntoNavigationBar, + darkTheme = darkTheme, ), color = NativeInsetsColor( top = statusBarColor, diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt index 6c2dab7ee..5d02c88e6 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/DesktopApp.kt @@ -30,8 +30,6 @@ import com.twidere.twiderex.di.ext.get import com.twidere.twiderex.di.setupModules import com.twidere.twiderex.init.Initializer import com.twidere.twiderex.init.TwidereServiceFactoryInitialTask -import com.twidere.twiderex.kmp.LocalPlatformWindow -import com.twidere.twiderex.kmp.PlatformWindow import com.twidere.twiderex.navigation.twidereXSchema import com.twidere.twiderex.preferences.PreferencesHolder import com.twidere.twiderex.preferences.ProvidePreferences @@ -171,7 +169,6 @@ private fun startDesktopApp() { ) { FilePicker.init(window) CompositionLocalProvider( - LocalPlatformWindow provides PlatformWindow(), LocalVideoPlayback provides DisplayPreferences.AutoPlayback.Off ) { App(navController = navController) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt index 935e05709..338f94c91 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt @@ -20,14 +20,17 @@ */ package com.twidere.twiderex.component +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandVertically +import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -37,14 +40,29 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.contentColorFor +import androidx.compose.material.darkColors import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Fullscreen import androidx.compose.material.icons.filled.FullscreenExit import androidx.compose.material.icons.filled.Minimize +import androidx.compose.material.lightColors import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.Stable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -58,12 +76,48 @@ import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowScope import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState -import com.twidere.twiderex.ui.TwidereTheme -import com.twidere.twiderex.ui.isDarkTheme +import androidx.compose.ui.zIndex +import com.twidere.twiderex.kmp.LocalPlatformWindow +import com.twidere.twiderex.kmp.PlatformWindow import com.twidere.twiderex.utils.OperatingSystem import com.twidere.twiderex.utils.currentOperatingSystem import moe.tlaster.precompose.PreComposeWindow +@Stable +class NativeWindowController( + initialIsAppearanceLightTitleBar: Boolean = false, +) { + companion object { + fun Saver(): Saver = listSaver( + save = { + listOf(it.isAppearanceLightTitleBar) + }, + restore = { + NativeWindowController( + initialIsAppearanceLightTitleBar = it[0], + ) + } + ) + } + + var isAppearanceLightTitleBar: Boolean by mutableStateOf(initialIsAppearanceLightTitleBar) +} + +@Composable +fun rememberNativeWindowController(): NativeWindowController { + val saver = remember { + NativeWindowController.Saver() + } + return rememberSaveable( + saver = saver + ) { + NativeWindowController() + } +} + +val LocalNativeWindowController = + staticCompositionLocalOf { error("No NativeWindowController") } + @Composable fun NativeWindow( onCloseRequest: () -> Unit, @@ -79,44 +133,61 @@ fun NativeWindow( onKeyEvent: (KeyEvent) -> Boolean = { false }, content: @Composable FrameWindowScope.() -> Unit, ) { - PreComposeWindow( - state = state, - visible = visible, - title = title, - icon = icon, - undecorated = true, - transparent = true, - resizable = resizable, - enabled = enabled, - focusable = focusable, - alwaysOnTop = alwaysOnTop, - onPreviewKeyEvent = onPreviewKeyEvent, - onKeyEvent = onKeyEvent, - onCloseRequest = onCloseRequest, - content = { - Column( - modifier = Modifier.clip(RoundedCornerShape(8.dp)), - ) { - PlatformTitleBar( - title = title, - icon = icon, - operatingSystem = currentOperatingSystem, - onCloseRequest = onCloseRequest, - onMinimizeRequest = { state.isMinimized = true }, - onMaximizeRequest = { state.placement = WindowPlacement.Maximized }, - onUndoMaximizeRequest = { state.placement = WindowPlacement.Floating }, - isMaximized = state.placement == WindowPlacement.Maximized, - ) - content.invoke(this@PreComposeWindow) - } - }, - ) + val nativeWindowController = rememberNativeWindowController() + CompositionLocalProvider( + LocalNativeWindowController provides nativeWindowController, + LocalPlatformWindow provides PlatformWindow(), + ) { + PreComposeWindow( + state = state, + visible = visible, + title = title, + icon = icon, + undecorated = true, + transparent = true, + resizable = resizable, + enabled = enabled, + focusable = focusable, + alwaysOnTop = alwaysOnTop, + onPreviewKeyEvent = onPreviewKeyEvent, + onKeyEvent = onKeyEvent, + onCloseRequest = onCloseRequest, + content = { + Box( + modifier = Modifier.clip(RoundedCornerShape(8.dp)), + ) { + Box( + modifier = Modifier.fillMaxSize(), + ) { + content.invoke(this@PreComposeWindow) + } + Box( + modifier = Modifier.fillMaxWidth().zIndex(Float.MAX_VALUE), + contentAlignment = Alignment.TopCenter, + ) { + PlatformTitleBar( + title = title, + icon = icon, + isAppearanceLightTitleBar = nativeWindowController.isAppearanceLightTitleBar, + operatingSystem = currentOperatingSystem, + onCloseRequest = onCloseRequest, + onMinimizeRequest = { state.isMinimized = true }, + onMaximizeRequest = { state.placement = WindowPlacement.Maximized }, + onUndoMaximizeRequest = { state.placement = WindowPlacement.Floating }, + isMaximized = state.placement == WindowPlacement.Maximized, + ) + } + } + }, + ) + } } @Composable private fun WindowScope.PlatformTitleBar( title: String, icon: Painter?, + isAppearanceLightTitleBar: Boolean, operatingSystem: OperatingSystem, onCloseRequest: () -> Unit, onMinimizeRequest: () -> Unit, @@ -124,31 +195,45 @@ private fun WindowScope.PlatformTitleBar( onUndoMaximizeRequest: () -> Unit, isMaximized: Boolean = false, ) { + val platformWindow = LocalPlatformWindow.current + val windowBarVisibility by platformWindow.windowBarVisibility.collectAsState(initial = true) WindowDraggableArea { - TwidereTheme(isDarkTheme()) { + MaterialTheme( + colors = if (isAppearanceLightTitleBar) { + lightColors() + } else { + darkColors() + } + ) { Surface( - modifier = Modifier - .fillMaxWidth() + color = Color.Transparent, + contentColor = contentColorFor(MaterialTheme.colors.background), ) { - when (operatingSystem) { - OperatingSystem.MacOS -> OSXTitleBar( - title = title, - icon = icon, - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) - else -> WindowsTitleBar( - title = title, - icon = icon, - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) + AnimatedVisibility( + windowBarVisibility, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + when (operatingSystem) { + OperatingSystem.MacOS -> OSXTitleBar( + title = title, + icon = icon, + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + else -> WindowsTitleBar( + title = title, + icon = icon, + onCloseRequest = onCloseRequest, + onMinimizeRequest = onMinimizeRequest, + onMaximizeRequest = onMaximizeRequest, + onUndoMaximizeRequest = onUndoMaximizeRequest, + isMaximized = isMaximized + ) + } } } } @@ -260,9 +345,11 @@ private fun OSXTitleBar( ) { Box( modifier = Modifier - .padding(vertical = 8.dp, horizontal = 16.dp) + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 16.dp), ) { OSXWindowButtons( + modifier = Modifier.align(Alignment.CenterStart), onCloseRequest = onCloseRequest, onMinimizeRequest = onMinimizeRequest, onMaximizeRequest = onMaximizeRequest, @@ -270,8 +357,7 @@ private fun OSXTitleBar( isMaximized = isMaximized ) Box( - modifier = Modifier.fillMaxWidth(), - contentAlignment = Alignment.Center, + modifier = Modifier.align(Alignment.Center), ) { WindowTitle( title = title, @@ -283,13 +369,17 @@ private fun OSXTitleBar( @Composable private fun OSXWindowButtons( + modifier: Modifier = Modifier, onCloseRequest: () -> Unit, onMinimizeRequest: () -> Unit, onMaximizeRequest: () -> Unit, onUndoMaximizeRequest: () -> Unit, isMaximized: Boolean = false, ) { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { Box( modifier = Modifier .size(12.dp) @@ -319,3 +409,5 @@ private fun OSXWindowButtons( ) } } + +val titleBarHeight = 8.dp * 2 + 24.dp diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index fb9bbd5ad..888e1accf 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -20,17 +20,26 @@ */ package com.twidere.twiderex.component +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex -actual fun Modifier.topInsetsPadding(): Modifier = this +actual fun Modifier.topInsetsPadding(): Modifier = this.padding(top = titleBarHeight) actual fun Modifier.bottomInsetsPadding(): Modifier = this actual fun Modifier.startInsetsPadding(): Modifier = this actual fun Modifier.endInsetsPadding(): Modifier = this -actual fun Modifier.topInsetsHeight(): Modifier = this +actual fun Modifier.topInsetsHeight(): Modifier = this.height(titleBarHeight) actual fun Modifier.bottomInsetsHeight(): Modifier = this actual fun Modifier.startInsetsWidth(): Modifier = this actual fun Modifier.endInsetsWidth(): Modifier = this @@ -41,8 +50,37 @@ actual fun PlatformInsets( color: NativeInsetsColor, content: @Composable () -> Unit, ) { - // TODO: implementation - content.invoke() + val nativeWindowController = LocalNativeWindowController.current + val darkTheme = control.darkTheme + LaunchedEffect(darkTheme) { + nativeWindowController.isAppearanceLightTitleBar = !control.darkTheme + } + Box { + Box( + modifier = Modifier + .padding( + top = if (control.extendToTop) { + 0.dp + } else { + titleBarHeight + } + ) + .align(Alignment.Center) + ) { + content() + } + Spacer( + modifier = if (!control.extendToTop) { + Modifier + .height(titleBarHeight) + .zIndex(999F) + .fillMaxWidth() + .background(color.top) + } else { + Modifier + }.align(Alignment.TopCenter) + ) + } } @Composable @@ -60,6 +98,6 @@ actual fun ImeHeightWithInsets( } @Composable -actual fun ImeBottomInsets(): Dp { +actual fun imeBottomInsets(): Dp { return 0.dp } From 2cd7f3128b7841a80cf265f40aa49028511c41b2 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 5 May 2022 12:03:55 +0800 Subject: [PATCH 585/615] use flow to check active network --- .../com/twidere/twiderex/TwidereXActivity.kt | 15 +++-- .../twiderex/utils/IsActiveNetworkFlow.kt | 44 +++++++++++++ .../utils/IsActiveNetworkMeteredLiveData.kt | 66 ------------------- 3 files changed, 52 insertions(+), 73 deletions(-) create mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkFlow.kt delete mode 100644 android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 611c15417..7e8607aa1 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.google.accompanist.insets.ProvideWindowInsets import com.twidere.twiderex.action.LocalStatusActions @@ -73,10 +74,12 @@ import com.twidere.twiderex.ui.LocalActiveAccount import com.twidere.twiderex.ui.LocalActiveAccountViewModel import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.utils.CustomTabSignInChannel -import com.twidere.twiderex.utils.IsActiveNetworkMeteredLiveData import com.twidere.twiderex.utils.LocalPlatformResolver import com.twidere.twiderex.utils.PlatformResolver +import com.twidere.twiderex.utils.asIsActiveNetworkFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import moe.tlaster.kfilepicker.FilePicker import moe.tlaster.precompose.lifecycle.PreComposeActivity import moe.tlaster.precompose.lifecycle.setContent @@ -103,9 +106,6 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { private val remoteNavigator: RemoteNavigator by inject() private val isActiveNetworkMetered = MutableStateFlow(false) - private val isActiveNetworkMeteredLiveData by lazy { - IsActiveNetworkMeteredLiveData(connectivityManager = connectivityManager) - } @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -113,9 +113,10 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { FilePicker.init(activityResultRegistry, this, contentResolver) WindowCompat.setDecorFitsSystemWindows(window, false) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) - isActiveNetworkMeteredLiveData.observe(this) { - isActiveNetworkMetered.value = it - } + connectivityManager.asIsActiveNetworkFlow() + .flowWithLifecycle(getLifecycle()) + .onEach { isActiveNetworkMetered.value = it } + .launchIn(lifecycleScope) setContent { var showSplash by rememberSaveable { mutableStateOf(true) } LaunchedEffect(Unit) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkFlow.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkFlow.kt new file mode 100644 index 000000000..e0408fb57 --- /dev/null +++ b/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkFlow.kt @@ -0,0 +1,44 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.twiderex.utils + +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import androidx.core.net.ConnectivityManagerCompat +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow + +fun ConnectivityManager.asIsActiveNetworkFlow(): Flow = callbackFlow { + val request = NetworkRequest.Builder().build() + val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities + ) { + trySend(ConnectivityManagerCompat.isActiveNetworkMetered(this@asIsActiveNetworkFlow)) + } + } + registerNetworkCallback(request, networkCallback) + awaitClose { unregisterNetworkCallback(networkCallback) } +} diff --git a/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt b/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt deleted file mode 100644 index a748ab20c..000000000 --- a/android/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Twidere X - * - * Copyright (C) TwidereProject and Contributors - * - * This file is part of Twidere X. - * - * Twidere X is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Twidere X is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Twidere X. If not, see . - */ -package com.twidere.twiderex.utils - -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import androidx.core.net.ConnectivityManagerCompat -import androidx.lifecycle.LiveData - -class IsActiveNetworkMeteredLiveData( - private val connectivityManager: ConnectivityManager, -) : LiveData() { - private val request = NetworkRequest.Builder().build() - private val networkCallback by lazy { - object : ConnectivityManager.NetworkCallback() { - override fun onCapabilitiesChanged( - network: Network, - networkCapabilities: NetworkCapabilities - ) { - updateValue() - } - } - } - - private fun updateValue() { - postValue( - ConnectivityManagerCompat.isActiveNetworkMetered( - connectivityManager - ) - ) - } - - override fun onActive() { - super.onActive() - updateValue() - connectivityManager.registerNetworkCallback( - request, - networkCallback, - ) - } - - override fun onInactive() { - super.onInactive() - connectivityManager.unregisterNetworkCallback(networkCallback) - } -} From a1db82c6862c8eb564435362bec40953ad1a5dbe Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 5 May 2022 12:31:53 +0800 Subject: [PATCH 586/615] safe cast MastodonService --- .../viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt index e0eb57c86..a36b38f4b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/viewmodel/timeline/mastodon/FederatedTimelineViewModel.kt @@ -43,8 +43,9 @@ class FederatedTimelineViewModel( override val pagingMediator by lazy { account.map { + val mastodonService = it.service as? MastodonService ?: return@map null FederatedTimelineMediator( - it.service as MastodonService, + mastodonService, it.accountKey, database, ) From 76fdfb12b495c8865158d53a1b2c55e8d9b8bb93 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 5 May 2022 18:52:50 +0800 Subject: [PATCH 587/615] support some unparseable date --- .../room/db/transform/StatusTransform.kt | 2 +- .../twiderex/dataprovider/mapper/Mastodon.kt | 8 +-- .../twiderex/dataprovider/mapper/Twitter.kt | 4 +- services/build.gradle.kts | 1 + .../twidere/services/mastodon/model/Field.kt | 4 +- .../services/mastodon/model/Notification.kt | 4 +- .../twidere/services/mastodon/model/Poll.kt | 4 +- .../twidere/services/mastodon/model/Status.kt | 4 +- .../services/serializer/DateSerializer.kt | 27 +++------ .../services/serializer/DateSerializerV2.kt | 27 +++------ .../serializer/DateSerializerV2WithOffset.kt | 27 +++------ .../twidere/services/twitter/model/PollV2.kt | 4 +- .../twidere/services/twitter/model/Status.kt | 4 +- .../services/twitter/model/StatusV2.kt | 4 +- .../services/twitter/model/TwitterList.kt | 4 +- .../twidere/services/utils/DateFormatUtils.kt | 58 +++++++++++++++++++ .../services/serializer/DateSerializerTest.kt | 49 ++++++++++++++++ 17 files changed, 158 insertions(+), 77 deletions(-) create mode 100644 services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt create mode 100644 services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt index f336df36f..4059a56fe 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/transform/StatusTransform.kt @@ -342,7 +342,7 @@ internal fun Poll.toUi() = id?.let { ) } ?: emptyList(), expired = expired ?: false, - expiresAt = expiresAt?.time, + expiresAt = expiresAt?.millis, multiple = multiple ?: false, voted = voted ?: false, votersCount = votersCount, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt index d3bc467cc..4a5eecd72 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Mastodon.kt @@ -68,7 +68,7 @@ fun Notification.toPagingTimeline( return PagingTimeLineWithStatus( timeline = PagingTimeLine( accountKey = accountKey, - timestamp = createdAt?.time ?: 0, + timestamp = createdAt?.millis ?: 0, isGap = false, statusKey = status.statusKey, pagingKey = pagingKey, @@ -95,7 +95,7 @@ fun Notification.toUiStatus( statusKey = statusKey, htmlText = "", rawText = "", - timestamp = this.createdAt?.time ?: 0, + timestamp = this.createdAt?.millis ?: 0, metrics = StatusMetrics( retweet = 0, like = 0, @@ -193,7 +193,7 @@ internal fun Status.toUiStatus( }?.let { generateWithHashtag(content = it) } ?: "", - timestamp = createdAt?.time ?: 0, + timestamp = createdAt?.millis ?: 0, metrics = StatusMetrics( retweet = reblogsCount ?: 0, like = favouritesCount ?: 0, @@ -274,7 +274,7 @@ fun Poll.toUi() = UiPoll( count = option.votesCount ?: 0 ) } ?: emptyList(), - expiresAt = expiresAt?.time, + expiresAt = expiresAt?.millis, expired = expired ?: false, multiple = multiple ?: false, voted = voted ?: false, diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt index 8571b20cc..d2781423b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/dataprovider/mapper/Twitter.kt @@ -142,7 +142,7 @@ internal fun StatusV2.toUiStatus( sensitive = possiblySensitive ?: false, rawText = text ?: "", htmlText = autolink.autoLink(text ?: ""), - timestamp = createdAt?.time ?: 0, + timestamp = createdAt?.millis ?: 0, metrics = StatusMetrics( retweet = publicMetrics?.retweetCount ?: 0, like = publicMetrics?.likeCount ?: 0, @@ -246,7 +246,7 @@ internal fun Status.toUiStatus( sensitive = possiblySensitive ?: false, rawText = fullText ?: text ?: "", htmlText = autolink.autoLink(fullText ?: text ?: ""), - timestamp = createdAt?.time ?: 0, + timestamp = createdAt?.millis ?: 0, metrics = StatusMetrics( retweet = retweetCount ?: 0, like = favoriteCount ?: 0, diff --git a/services/build.gradle.kts b/services/build.gradle.kts index c6ee6618b..cc35cf105 100644 --- a/services/build.gradle.kts +++ b/services/build.gradle.kts @@ -19,4 +19,5 @@ dependencies { retrofit() okhttp() junit5() + api("joda-time:joda-time:2.10.13") } diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Field.kt b/services/src/main/java/com/twidere/services/mastodon/model/Field.kt index aacafc993..824c53bf8 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Field.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Field.kt @@ -23,7 +23,7 @@ package com.twidere.services.mastodon.model import com.twidere.services.serializer.DateSerializerV2WithOffset import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class Field( @@ -31,5 +31,5 @@ data class Field( val value: String? = null, @SerialName("verified_at") @Serializable(with = DateSerializerV2WithOffset::class) - val verifiedAt: Date? = null, + val verifiedAt: DateTime? = null, ) diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt b/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt index 400dee31e..02ae4c10f 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Notification.kt @@ -24,7 +24,7 @@ import com.twidere.services.microblog.model.INotification import com.twidere.services.serializer.DateSerializerV2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class Notification( @@ -33,7 +33,7 @@ data class Notification( @SerialName("created_at") @Serializable(with = DateSerializerV2::class) - val createdAt: Date? = null, + val createdAt: DateTime? = null, val account: Account? = null, val status: Status? = null diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt b/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt index 569bb1a2d..13b37f401 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Poll.kt @@ -23,7 +23,7 @@ package com.twidere.services.mastodon.model import com.twidere.services.serializer.DateSerializerV2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class Poll( @@ -31,7 +31,7 @@ data class Poll( @SerialName("expires_at") @Serializable(with = DateSerializerV2::class) - val expiresAt: Date? = null, + val expiresAt: DateTime? = null, val expired: Boolean? = null, val multiple: Boolean? = null, diff --git a/services/src/main/java/com/twidere/services/mastodon/model/Status.kt b/services/src/main/java/com/twidere/services/mastodon/model/Status.kt index 0edc135d9..1b290607c 100644 --- a/services/src/main/java/com/twidere/services/mastodon/model/Status.kt +++ b/services/src/main/java/com/twidere/services/mastodon/model/Status.kt @@ -24,7 +24,7 @@ import com.twidere.services.microblog.model.IStatus import com.twidere.services.serializer.DateSerializerV2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class Status( @@ -32,7 +32,7 @@ data class Status( @SerialName("created_at") @Serializable(with = DateSerializerV2::class) - val createdAt: Date? = null, + val createdAt: DateTime? = null, @SerialName("in_reply_to_id") val inReplyToID: String? = null, diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt index 986b3594c..c3bb4a94d 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializer.kt @@ -20,6 +20,7 @@ */ package com.twidere.services.serializer +import com.twidere.services.utils.DateFormatUtils import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -28,30 +29,20 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone +import org.joda.time.DateTime @OptIn(ExperimentalSerializationApi::class) -@Serializer(forClass = Date::class) -internal object DateSerializer : KSerializer { +@Serializer(forClass = DateTime::class) +internal object DateSerializer : KSerializer { override val descriptor: SerialDescriptor - get() = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING) + get() = PrimitiveSerialDescriptor("DateTime", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): Date { + override fun deserialize(decoder: Decoder): DateTime { val str = decoder.decodeString() - return getDateFormat().parse(str) + return DateFormatUtils.parse(str) } - override fun serialize(encoder: Encoder, value: Date) { - encoder.encodeString(getDateFormat().format(value)) - } - - private fun getDateFormat(): SimpleDateFormat { - val format = SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZZZ yyyy", Locale.ENGLISH) - format.isLenient = true - format.timeZone = TimeZone.getTimeZone("UTC") - return format + override fun serialize(encoder: Encoder, value: DateTime) { + encoder.encodeString(DateFormatUtils.format(value)) } } diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt index 3bbdb2fc0..b9a2fcf21 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2.kt @@ -20,6 +20,7 @@ */ package com.twidere.services.serializer +import com.twidere.services.utils.DateFormatUtils import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -28,30 +29,20 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone +import org.joda.time.DateTime @OptIn(ExperimentalSerializationApi::class) -@Serializer(forClass = Date::class) -internal object DateSerializerV2 : KSerializer { +@Serializer(forClass = DateTime::class) +internal object DateSerializerV2 : KSerializer { override val descriptor: SerialDescriptor - get() = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING) + get() = PrimitiveSerialDescriptor("DateTime", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): Date { + override fun deserialize(decoder: Decoder): DateTime { val str = decoder.decodeString() - return getDateFormat().parse(str) + return DateFormatUtils.parse(str) } - override fun serialize(encoder: Encoder, value: Date) { - encoder.encodeString(getDateFormat().format(value)) - } - - private fun getDateFormat(): SimpleDateFormat { - val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) - format.isLenient = true - format.timeZone = TimeZone.getTimeZone("UTC") - return format + override fun serialize(encoder: Encoder, value: DateTime) { + encoder.encodeString(DateFormatUtils.format(value)) } } diff --git a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt index 50a118c5a..f6e426699 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateSerializerV2WithOffset.kt @@ -20,6 +20,7 @@ */ package com.twidere.services.serializer +import com.twidere.services.utils.DateFormatUtils import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -28,30 +29,20 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone +import org.joda.time.DateTime @OptIn(ExperimentalSerializationApi::class) -@Serializer(forClass = Date::class) -internal object DateSerializerV2WithOffset : KSerializer { +@Serializer(forClass = DateTime::class) +internal object DateSerializerV2WithOffset : KSerializer { override val descriptor: SerialDescriptor - get() = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING) + get() = PrimitiveSerialDescriptor("DateTime", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): Date { + override fun deserialize(decoder: Decoder): DateTime { val str = decoder.decodeString() - return getDateFormat().parse(str) + return DateFormatUtils.parse(str) } - override fun serialize(encoder: Encoder, value: Date) { - encoder.encodeString(getDateFormat().format(value)) - } - - private fun getDateFormat(): SimpleDateFormat { - val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH) - format.isLenient = true - format.timeZone = TimeZone.getTimeZone("UTC") - return format + override fun serialize(encoder: Encoder, value: DateTime) { + encoder.encodeString(DateFormatUtils.format(value)) } } diff --git a/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt b/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt index 846b9d2ef..7de9af7c0 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/PollV2.kt @@ -23,7 +23,7 @@ package com.twidere.services.twitter.model import com.twidere.services.serializer.DateSerializerV2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class PollV2( @@ -35,7 +35,7 @@ data class PollV2( @SerialName("end_datetime") @Serializable(with = DateSerializerV2::class) - val endDatetime: Date? = null, + val endDatetime: DateTime? = null, @SerialName("voting_status") val votingStatus: String? = null diff --git a/services/src/main/java/com/twidere/services/twitter/model/Status.kt b/services/src/main/java/com/twidere/services/twitter/model/Status.kt index e421f6871..43e5f7d6f 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/Status.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/Status.kt @@ -24,13 +24,13 @@ import com.twidere.services.microblog.model.IStatus import com.twidere.services.serializer.DateSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class Status( @SerialName("created_at") @Serializable(with = DateSerializer::class) - val createdAt: Date? = null, + val createdAt: DateTime? = null, val id: Long? = null, diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt index ce244b4bc..6f6c8cb51 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusV2.kt @@ -25,7 +25,7 @@ import com.twidere.services.serializer.DateSerializerV2 import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import java.util.Date +import org.joda.time.DateTime @Serializable data class StatusV2( @@ -52,7 +52,7 @@ data class StatusV2( @SerialName("created_at") @Serializable(with = DateSerializerV2::class) - val createdAt: Date? = null, + val createdAt: DateTime? = null, @SerialName("public_metrics") val publicMetrics: StatusV2PublicMetrics? = null, diff --git a/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt b/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt index bfc31ad30..ef8394ebd 100644 --- a/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt +++ b/services/src/main/java/com/twidere/services/twitter/model/TwitterList.kt @@ -24,13 +24,13 @@ import com.twidere.services.microblog.model.IListModel import com.twidere.services.serializer.DateSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import java.util.Date +import org.joda.time.DateTime @Serializable data class TwitterList( @SerialName("created_at") @Serializable(with = DateSerializer::class) - val createdAt: Date? = null, + val createdAt: DateTime? = null, val id: Long? = null, diff --git a/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt b/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt new file mode 100644 index 000000000..464bca848 --- /dev/null +++ b/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt @@ -0,0 +1,58 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.utils + +import org.joda.time.DateTime +import org.joda.time.format.DateTimeFormat +import org.joda.time.format.DateTimeFormatterBuilder +import java.util.Locale + +object DateFormatUtils { + + private val dateFormats = arrayOf( + "yyyy-MM-dd'T'HH:mm:ssZ", + "yyyy-MM-dd'T'HH:mm:ss.SSSZ", + "EEE MMM dd HH:mm:ss Z yyyy", + "MMM d, yyyy H:mm aaa ZZZZ", + "MMM d, yyyy · H:mm aaa ZZZZ", + ) + + private val formatter by lazy { + DateTimeFormatterBuilder() + .append( + null, + Array(dateFormats.size) { + DateTimeFormat.forPattern(dateFormats[it]).withLocale(Locale.ENGLISH).parser + } + ) + .toFormatter() + .withLocale(Locale.ENGLISH) + .withZoneUTC() + } + + fun parse(text: String): DateTime { + return DateTime.parse(text, formatter) + } + + fun format(date: DateTime): String { + return date.toString() + } +} diff --git a/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt b/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt new file mode 100644 index 000000000..16831eaa9 --- /dev/null +++ b/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt @@ -0,0 +1,49 @@ +/* + * Twidere X + * + * Copyright (C) TwidereProject and Contributors + * + * This file is part of Twidere X. + * + * Twidere X is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Twidere X is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Twidere X. If not, see . + */ +package com.twidere.services.serializer + +import com.twidere.services.utils.DateFormatUtils +import kotlin.test.Test +import kotlin.test.assertNotNull + +class DateSerializerTest { + + @Test + fun test() { + val date1 = "2019-04-12T16:38:15Z" + assertNotNull(DateFormatUtils.parse(date1)) + + val date2 = "2019-04-12T16:38:15.123Z" + assertNotNull(DateFormatUtils.parse(date2)) + + val date3 = "Thu May 05 06:29:56 +0000 2022" + assertNotNull(DateFormatUtils.parse(date3)) + + val date4 = "Dec 2, 2017 8:45 AM UTC" + assertNotNull(DateFormatUtils.parse(date4)) + + val date5 = "Dec 2, 2017 · 8:45 AM UTC" + assertNotNull(DateFormatUtils.parse(date5)) + + val date6 = "2022-05-05T06:05:28.000Z" + assertNotNull(DateFormatUtils.parse(date6)) + } +} From 40e25b9b9692ccbec1f390a57d631fcf828d1b14 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 5 May 2022 19:02:56 +0800 Subject: [PATCH 588/615] add some date formats --- .../twidere/services/utils/DateFormatUtils.kt | 3 +++ .../services/serializer/DateSerializerTest.kt | 25 ++++++------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt b/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt index 464bca848..6d7d54807 100644 --- a/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt +++ b/services/src/main/java/com/twidere/services/utils/DateFormatUtils.kt @@ -28,10 +28,13 @@ import java.util.Locale object DateFormatUtils { private val dateFormats = arrayOf( + "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ssZ", + "yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "EEE MMM dd HH:mm:ss Z yyyy", "MMM d, yyyy H:mm aaa ZZZZ", + "MMM d, yyyy·H:mm aaa ZZZZ", "MMM d, yyyy · H:mm aaa ZZZZ", ) diff --git a/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt b/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt index 16831eaa9..a52823430 100644 --- a/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt +++ b/services/src/test/java/com/twidere/services/serializer/DateSerializerTest.kt @@ -28,22 +28,13 @@ class DateSerializerTest { @Test fun test() { - val date1 = "2019-04-12T16:38:15Z" - assertNotNull(DateFormatUtils.parse(date1)) - - val date2 = "2019-04-12T16:38:15.123Z" - assertNotNull(DateFormatUtils.parse(date2)) - - val date3 = "Thu May 05 06:29:56 +0000 2022" - assertNotNull(DateFormatUtils.parse(date3)) - - val date4 = "Dec 2, 2017 8:45 AM UTC" - assertNotNull(DateFormatUtils.parse(date4)) - - val date5 = "Dec 2, 2017 · 8:45 AM UTC" - assertNotNull(DateFormatUtils.parse(date5)) - - val date6 = "2022-05-05T06:05:28.000Z" - assertNotNull(DateFormatUtils.parse(date6)) + assertNotNull(DateFormatUtils.parse("2019-04-12T16:38:15")) + assertNotNull(DateFormatUtils.parse("2019-04-12T16:38:15Z")) + assertNotNull(DateFormatUtils.parse("2019-04-12T16:38:15.123")) + assertNotNull(DateFormatUtils.parse("2022-05-05T06:05:28.000Z")) + assertNotNull(DateFormatUtils.parse("Thu May 05 06:29:56 +0000 2022")) + assertNotNull(DateFormatUtils.parse("Dec 2, 2017 8:45 AM UTC")) + assertNotNull(DateFormatUtils.parse("Dec 2, 2017·8:45 AM UTC")) + assertNotNull(DateFormatUtils.parse("Dec 2, 2017 · 8:45 AM UTC")) } } From 5d0dc0ad4e8cfa4d0050ff7011b2e943781b0e63 Mon Sep 17 00:00:00 2001 From: seiko Date: Fri, 6 May 2022 12:22:10 +0800 Subject: [PATCH 589/615] add socks proxy --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- .../http/TwidereHttpConfigProvider.kt | 1 + .../preferences/ProvidePreferences.kt | 1 + .../preferences/model/MiscPreferences.kt | 1 + .../twiderex/scenes/settings/MiscScene.kt | 2 + .../commonMain/resources/MR/base/strings.xml | 1 + .../twidere/twiderex/image/ImagePainter.kt | 40 +++++----- .../com/twidere/twiderex/kmp/ImagePainter.kt | 63 ++------------- localization | 2 +- .../http/config/HttpConfigClientFactory.kt | 79 +++++++++++++------ .../com/twidere/services/proxy/ProxyConfig.kt | 1 + .../java/com/twidere/services/utils/OkHttp.kt | 2 +- 12 files changed, 92 insertions(+), 103 deletions(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 0e17a227b..94a397d7c 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -33,7 +33,7 @@ fun DependencyHandlerScope.retrofit() { } fun DependencyHandlerScope.okhttp() { - implementation("com.squareup.okhttp3:okhttp", Versions.okhttp) + api("com.squareup.okhttp3:okhttp", Versions.okhttp) implementation("com.squareup.okhttp3:logging-interceptor", Versions.okhttp) } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt index ab11825c8..90096d152 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/http/TwidereHttpConfigProvider.kt @@ -44,6 +44,7 @@ class TwidereHttpConfigProvider( password = it.proxyPassword, type = when (it.proxyType) { MiscPreferences.ProxyType.REVERSE -> ProxyConfig.Type.REVERSE + MiscPreferences.ProxyType.SOCKS -> ProxyConfig.Type.SOCKS else -> ProxyConfig.Type.HTTP } ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt index 86eabf3a6..deb4c83c3 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/ProvidePreferences.kt @@ -64,6 +64,7 @@ fun ProvidePreferences( password = it.proxyPassword, type = when (it.proxyType) { MiscPreferences.ProxyType.REVERSE -> ProxyConfig.Type.REVERSE + MiscPreferences.ProxyType.SOCKS -> ProxyConfig.Type.SOCKS else -> ProxyConfig.Type.HTTP } ) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt index af6d52246..5fb80d3f0 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/preferences/model/MiscPreferences.kt @@ -35,6 +35,7 @@ data class MiscPreferences( @Serializable enum class ProxyType { HTTP, + SOCKS, REVERSE, } } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt index 2be478ab3..11ea3bf1b 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/scenes/settings/MiscScene.kt @@ -261,6 +261,7 @@ fun ColumnScope.ProxyPreference( fun proxyTypeValue(type: MiscPreferences.ProxyType): String { return when (type) { MiscPreferences.ProxyType.HTTP -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_http) + MiscPreferences.ProxyType.SOCKS -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_socks) MiscPreferences.ProxyType.REVERSE -> stringResource(res = com.twidere.twiderex.MR.strings.scene_settings_misc_proxy_type_reverse) } } @@ -517,6 +518,7 @@ fun ProxyTypeSelectDialog( RadioItem( options = listOf( MiscPreferences.ProxyType.HTTP, + MiscPreferences.ProxyType.SOCKS, MiscPreferences.ProxyType.REVERSE, ), value = selected, diff --git a/common/src/commonMain/resources/MR/base/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml index 1a6b203f0..9115979bb 100644 --- a/common/src/commonMain/resources/MR/base/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -343,6 +343,7 @@ Use proxy for all network requests Proxy Proxy settings + SOCKS HTTP Proxy type Reverse diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index d00c51083..0793d6db4 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.toPainter +import com.twidere.services.utils.await import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects import kotlinx.coroutines.CoroutineScope @@ -34,11 +35,14 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.Request import org.jetbrains.skia.Codec import org.jetbrains.skia.Data +import java.io.Closeable import java.io.File +import java.io.IOException import java.io.InputStream -import java.net.HttpURLConnection import java.net.URL import javax.imageio.ImageIO @@ -50,7 +54,8 @@ internal class ImagePainter( private val parentScope: CoroutineScope, private val imageCache: ImageCache, private val imageEffects: ImageEffects, - private val httpConnection: (URL) -> HttpURLConnection = { it.openConnection() as HttpURLConnection }, + // private val httpConnection: (URL) -> HttpURLConnection = { it.openConnection() as HttpURLConnection }, + private val callFactory: () -> OkHttpClient = { OkHttpClient() }, private val onImageStateChanged: (NetworkImageState) -> Unit ) : Painter(), RememberObserver { private var painter = mutableStateOf(null) @@ -111,11 +116,18 @@ internal class ImagePainter( var error: Throwable? = null try { if (mimeType == "image/gif") { - painter.value = GifPainter(Codec.makeFromData(Data.makeFromBytes(inputStream.readAllBytes())), parentScope) + painter.value = GifPainter( + Codec.makeFromData(Data.makeFromBytes(inputStream.readAllBytes())), + parentScope + ) } else { ImageIO.read(inputStream)?.let { image -> imageEffects.blur?.let { - ImageEffectsFilter.applyBlurFilter(image, it.blurRadius.toInt(), it.bitmapScale) + ImageEffectsFilter.applyBlurFilter( + image, + it.blurRadius.toInt(), + it.bitmapScale + ) } ?: image }?.let { painter.value = it.toPainter() @@ -145,32 +157,24 @@ internal class ImagePainter( } private suspend fun networkRequest(url: URL) { - var connection: HttpURLConnection? = null var error: Throwable? = null try { + val request = Request.Builder().url(url).build() var mimeType = "" val input = imageCache.fetch(url.toString())?.let { mimeType = it.toURI().toURL().openConnection().contentType it.inputStream() - } ?: httpConnection(url).let { - connection = it - it.setRequestProperty("Accept", "image/*") - it.connect() - mimeType = it.contentType - imageCache.store(url.toString(), it.inputStream)?.inputStream() ?: it.inputStream - } + } ?: callFactory().newCall(request).await().body?.use { body -> + mimeType = body.contentType().toString() + imageCache.store(url.toString(), body.byteStream())?.inputStream() + ?: body.byteStream() + } ?: throw IOException("No response body") generatePainter( inputStream = input, mimeType = mimeType ) } catch (e: Throwable) { error = e - } finally { - try { - connection?.disconnect() - } catch (e: Exception) { - e.printStackTrace() - } } if (error != null) throw error } diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt index 2834b7834..48e3874db 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/kmp/ImagePainter.kt @@ -26,21 +26,12 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.graphics.painter.Painter import com.twidere.services.http.authorization.Authorization import com.twidere.services.http.config.HttpConfig -import com.twidere.services.proxy.ProxyConfig -import com.twidere.services.proxy.ReverseProxyHandler import com.twidere.twiderex.component.foundation.NetworkImageState import com.twidere.twiderex.component.image.ImageEffects +import com.twidere.twiderex.http.TwidereServiceFactory import com.twidere.twiderex.image.ImageCacheImpl import com.twidere.twiderex.image.ImagePainter import kotlinx.coroutines.Dispatchers -import okhttp3.Credentials -import okhttp3.HttpUrl -import okhttp3.Request -import java.io.IOException -import java.net.HttpURLConnection -import java.net.InetSocketAddress -import java.net.Proxy -import java.net.URL @Composable internal actual fun rememberNetworkImagePainter( @@ -60,55 +51,13 @@ internal actual fun rememberNetworkImagePainter( cacheDir = cacheDir, ), imageEffects = effects, - httpConnection = { - generateConnection( - httpConfig = httpConfig, - authorization = authorization, - url = it - ) + callFactory = { + TwidereServiceFactory + .createHttpClientFactory() + .createHttpClientBuilder() + .build() }, onImageStateChanged = onImageStateChanged ) } } - -private fun generateConnection(httpConfig: HttpConfig, authorization: Authorization, url: URL): HttpURLConnection { - val proxyConfig = httpConfig.proxyConfig - val connection = if (proxyConfig.enable) { - when (proxyConfig.type) { - ProxyConfig.Type.HTTP -> { - val proxy = if (proxyConfig.port !in (0..65535)) { - Proxy.NO_PROXY - } else { - val address = InetSocketAddress.createUnresolved( - proxyConfig.server, - proxyConfig.port - ) - Proxy(Proxy.Type.HTTP, address) - } - url.openConnection(proxy) - } - ProxyConfig.Type.REVERSE -> { - val con = HttpUrl.get(url)?.let { - try { - URL(ReverseProxyHandler.replaceUrl(it, proxyConfig.server)) - } catch (e: Throwable) { - throw IOException("Invalid reverse proxy format") - } - }?.openConnection() ?: url.openConnection() - if (proxyConfig.userName.isNotEmpty() && proxyConfig.password.isNotEmpty()) { - val credential = Credentials.basic( - proxyConfig.userName, - proxyConfig.password - ) - con.setRequestProperty("Proxy-Authorization", credential) - } - con - } - } - } else url.openConnection() - if (authorization.hasAuthorization) { - connection.setRequestProperty("Authorization", authorization.getAuthorizationHeader(Request.Builder().url(url).build())) - } - return connection as HttpURLConnection -} diff --git a/localization b/localization index b5d016e01..972546c41 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit b5d016e0185c540928076418798457622219d835 +Subproject commit 972546c4138bfd91076ab58c2579b63e5fbb71ef diff --git a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt index c0a75369d..34811cb90 100644 --- a/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt +++ b/services/src/main/java/com/twidere/services/http/config/HttpConfigClientFactory.kt @@ -124,36 +124,41 @@ class HttpConfigClientFactory(private val configProvider: HttpConfigProvider) : return proxy(OkHttpClient.Builder(), config.proxyConfig) } - private fun proxy(builder: OkHttpClient.Builder, proxyConfig: ProxyConfig): OkHttpClient.Builder { + private fun proxy( + builder: OkHttpClient.Builder, + proxyConfig: ProxyConfig + ): OkHttpClient.Builder { return if (proxyConfig.enable) { + when (proxyConfig.type) { ProxyConfig.Type.HTTP -> { - if (proxyConfig.port !in (0..65535)) { - return builder - } - val address = InetSocketAddress.createUnresolved( - proxyConfig.server, - proxyConfig.port + proxyOkHttpBuilder( + builder, + proxyType = Proxy.Type.HTTP, + host = proxyConfig.server, + port = proxyConfig.port, + username = proxyConfig.userName, + password = proxyConfig.password, + ) + } + ProxyConfig.Type.SOCKS -> { + proxyOkHttpBuilder( + builder, + proxyType = Proxy.Type.SOCKS, + host = proxyConfig.server, + port = proxyConfig.port, + username = proxyConfig.userName, + password = proxyConfig.password, ) - builder.proxy(Proxy(Proxy.Type.HTTP, address)) - .proxyAuthenticator { _, response -> - val b = response.request.newBuilder() - if (response.code == 407) { - if (proxyConfig.userName.isNotEmpty() && - proxyConfig.password.isNotEmpty() - ) { - val credential = Credentials.basic( - proxyConfig.userName, - proxyConfig.password - ) - b.header("Proxy-Authorization", credential) - } - } - b.build() - } } ProxyConfig.Type.REVERSE -> { - builder.addInterceptor(ReverseProxyInterceptor(proxyConfig.server, proxyConfig.userName, proxyConfig.password)) + builder.addInterceptor( + ReverseProxyInterceptor( + proxyConfig.server, + proxyConfig.userName, + proxyConfig.password, + ) + ) } } } else { @@ -161,6 +166,29 @@ class HttpConfigClientFactory(private val configProvider: HttpConfigProvider) : } } + private fun proxyOkHttpBuilder( + builder: OkHttpClient.Builder, + proxyType: Proxy.Type, + host: String, + port: Int, + username: String, + password: String, + ): OkHttpClient.Builder { + if (port !in (0..65535)) return builder + val address = InetSocketAddress.createUnresolved(host, port) + return builder.proxy(Proxy(proxyType, address)) + .proxyAuthenticator { _, response -> + val b = response.request.newBuilder() + if (response.code == 407) { + if (username.isNotEmpty() && password.isNotEmpty()) { + val credential = Credentials.basic(username, password) + b.header("Proxy-Authorization", credential) + } + } + b.build() + } + } + @OptIn(ExperimentalSerializationApi::class) private fun retrofit( clazz: Class, @@ -186,7 +214,8 @@ class HttpConfigClientFactory(private val configProvider: HttpConfigProvider) : addInterceptor { it.proceed( it.request().let { request -> - request.newBuilder().url(request.url.toString().replace("%20", "+")).build() + request.newBuilder() + .url(request.url.toString().replace("%20", "+")).build() } ) } diff --git a/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt b/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt index f4730f40f..f991719c9 100644 --- a/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt +++ b/services/src/main/java/com/twidere/services/proxy/ProxyConfig.kt @@ -31,6 +31,7 @@ data class ProxyConfig( enum class Type { HTTP, + SOCKS, REVERSE } } diff --git a/services/src/main/java/com/twidere/services/utils/OkHttp.kt b/services/src/main/java/com/twidere/services/utils/OkHttp.kt index 26e877ceb..df2aa9de3 100644 --- a/services/src/main/java/com/twidere/services/utils/OkHttp.kt +++ b/services/src/main/java/com/twidere/services/utils/OkHttp.kt @@ -28,7 +28,7 @@ import java.io.IOException import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -internal suspend fun Call.await(): Response { +suspend fun Call.await(): Response { return suspendCancellableCoroutine { continuation -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { From 0c063bab5346bfba63f987684101b4c2b069ffe2 Mon Sep 17 00:00:00 2001 From: seiko Date: Fri, 6 May 2022 13:33:08 +0800 Subject: [PATCH 590/615] fix ci unit-test --- buildSrc/src/main/kotlin/Versions.kt | 1 + common/build.gradle.kts | 1 + .../com/twidere/twiderex/mock/model/MockModels.kt | 10 +++++----- services/build.gradle.kts | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 141e98d28..5b22a652c 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -44,4 +44,5 @@ object Versions { const val sqlDelight = "1.5.3" const val javafx = "0.0.10" const val kFilePicker = "1.0.4" + const val jodaTime = "2.10.13" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index ee39b7594..33ec230f2 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -73,6 +73,7 @@ kotlin { implementation("io.insert-koin:koin-test:${Versions.koin}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.Kotlin.coroutines}") implementation("io.mockk:mockk:1.12.1") + implementation("joda-time:joda-time:${Versions.jodaTime}") } } val androidMain by getting { diff --git a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt index 39978c88e..a55f27442 100644 --- a/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt +++ b/common/src/commonTest/kotlin/com/twidere/twiderex/mock/model/MockModels.kt @@ -58,7 +58,7 @@ import com.twidere.twiderex.model.ui.UiMedia import com.twidere.twiderex.model.ui.UiSearch import com.twidere.twiderex.model.ui.UiUrlEntity import org.jetbrains.annotations.TestOnly -import java.util.Date +import org.joda.time.DateTime import java.util.UUID @TestOnly @@ -141,7 +141,7 @@ fun mockIListModel( name = name, mode = mode, description = description, - createdAt = Date().apply { time = System.currentTimeMillis() } + createdAt = DateTime.now(), ) } @@ -156,7 +156,7 @@ fun mockIStatus( return StatusV2( id = id, authorID = authorId, - createdAt = Date().apply { time = System.currentTimeMillis() }, + createdAt = DateTime.now(), attachments = if (hasMedia) AttachmentsV2(mediaKeys = listOf("mediaKey")).apply { media = listOf(MediaV2(url = "mediaUrl", type = "photo")) } else null, @@ -186,7 +186,7 @@ fun mockINotification(id: String = UUID.randomUUID().toString()): INotification return Notification( id = id, type = NotificationTypes.status, - createdAt = Date().apply { time = System.currentTimeMillis() }, + createdAt = DateTime.now(), account = account, status = Status( id = id, @@ -241,7 +241,7 @@ fun UiDMEvent.toConversation() = UiDMConversation( accountKey = accountKey, conversationId = conversationKey.id, conversationKey = conversationKey, - conversationAvatar = sender.profileImage.toString(), + conversationAvatar = sender.profileImage, conversationName = sender.name, conversationSubName = sender.screenName, conversationType = UiDMConversation.Type.ONE_TO_ONE, diff --git a/services/build.gradle.kts b/services/build.gradle.kts index cc35cf105..0153cafc5 100644 --- a/services/build.gradle.kts +++ b/services/build.gradle.kts @@ -19,5 +19,5 @@ dependencies { retrofit() okhttp() junit5() - api("joda-time:joda-time:2.10.13") + api("joda-time:joda-time:${Versions.jodaTime}") } From e12689d3995582f70df3d9112165604c7568f587 Mon Sep 17 00:00:00 2001 From: seiko Date: Fri, 6 May 2022 14:46:34 +0800 Subject: [PATCH 591/615] apply spotless --- .../kotlin/com/twidere/twiderex/image/ImagePainter.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt index 0793d6db4..902491cb0 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/image/ImagePainter.kt @@ -39,7 +39,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import org.jetbrains.skia.Codec import org.jetbrains.skia.Data -import java.io.Closeable import java.io.File import java.io.IOException import java.io.InputStream From fb84646e9209f52b6440598de0b38c9c61e85c21 Mon Sep 17 00:00:00 2001 From: seiko Date: Sat, 7 May 2022 15:08:40 +0800 Subject: [PATCH 592/615] kotlin to 1.6.21 --- buildSrc/src/main/kotlin/Versions.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 141e98d28..be5a92a38 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,8 +2,8 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.6.10" - const val coroutines = "1.6.0" + const val lang = "1.6.21" + const val coroutines = "1.6.1" const val serialization = "1.3.2" } @@ -12,14 +12,14 @@ object Versions { val java = JavaVersion.VERSION_17 } - const val ksp = "${Kotlin.lang}-1.0.4" + const val ksp = "${Kotlin.lang}-1.0.5" const val agp = "7.2.0-rc01" - const val spotless = "6.4.2" + const val spotless = "6.5.0" const val ktlint = "0.45.2" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.1.1" + const val compose_jb = "1.2.0-alpha01-dev675" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" From 741d617e1d4e2dc1ef660a20ca51aa8e97cc8c35 Mon Sep 17 00:00:00 2001 From: seiko Date: Sat, 7 May 2022 15:09:28 +0800 Subject: [PATCH 593/615] output compose metrics --- build.gradle.kts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 19674a214..9d8c85f69 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,8 +22,8 @@ allprojects { kotlinOptions { jvmTarget = Versions.Java.jvmTarget allWarningsAsErrors = true - freeCompilerArgs = listOf( - "-Xopt-in=kotlin.RequiresOptIn", + freeCompilerArgs = freeCompilerArgs + listOf( + "-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all", ) } @@ -53,3 +53,22 @@ allprojects { } } } + +subprojects { + tasks.withType { + kotlinOptions { + if (project.findProperty("myapp.enableComposeCompilerReports") == "true") { + freeCompilerArgs = freeCompilerArgs + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + + project.buildDir.absolutePath + "/compose_metrics" + ) + freeCompilerArgs = freeCompilerArgs + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + + project.buildDir.absolutePath + "/compose_metrics" + ) + } + } + } +} From 861fb154f9a8bb8cc1ec1c71f1959360ec7d8f57 Mon Sep 17 00:00:00 2001 From: seiko Date: Sat, 7 May 2022 15:10:08 +0800 Subject: [PATCH 594/615] fix build error --- .../settings/AccountNotificationScene.android.kt | 4 +++- .../precompose/navigation/route/ComposeRoute.kt | 7 +------ .../tlaster/precompose/navigation/route/Route.kt | 2 -- .../precompose/navigation/route/SceneRoute.kt | 8 -------- .../src/main/kotlin/RouteDefinition.kt | 4 +++- .../serializer/DateQueryConverterFactory.kt | 16 ++++++---------- 6 files changed, 13 insertions(+), 28 deletions(-) diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt index a70d12f56..09d244354 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/scenes/settings/AccountNotificationScene.android.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import com.twidere.twiderex.BuildConfig import com.twidere.twiderex.component.stringResource +import com.twidere.twiderex.compose.LocalResLoader import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.notification.NotificationChannelSpec import com.twidere.twiderex.notification.notificationChannelId @@ -46,8 +47,9 @@ actual fun AccountNotificationChannelDetail( accountKey: MicroBlogKey, ) { val context = LocalContext.current + val resLoader = LocalResLoader.current NotificationChannelSpec.values().filter { it.grouped } - .sortedBy { stringResource(res = it.nameRes) } + .sortedBy { resLoader.getString(res = it.nameRes) } .forEach { ListItem( modifier = Modifier.clickable( diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt index c12c142be..5c1466e6e 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt @@ -22,13 +22,8 @@ package moe.tlaster.precompose.navigation.route import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.BackStackEntry -import moe.tlaster.precompose.navigation.RouteParser abstract class ComposeRoute( override val route: String, val content: @Composable (BackStackEntry) -> Unit -) : Route { - override val pathKeys by lazy { - RouteParser.pathKeys(pattern = route) - } -} +) : Route diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt index 08497c545..356cad363 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/Route.kt @@ -22,6 +22,4 @@ package moe.tlaster.precompose.navigation.route interface Route { val route: String - @Deprecated("store path key in route node in order to match different links in one route") - val pathKeys: List } diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt index 41e6abb3c..4a9044023 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt @@ -22,7 +22,6 @@ package moe.tlaster.precompose.navigation.route import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.BackStackEntry -import moe.tlaster.precompose.navigation.RouteParser import moe.tlaster.precompose.navigation.transition.NavTransition internal class SceneRoute( @@ -31,11 +30,4 @@ internal class SceneRoute( val deepLinks: List, content: @Composable (BackStackEntry) -> Unit, ) : ComposeRoute(route, content) { - override val pathKeys by lazy { - ( - deepLinks.flatMap { - RouteParser.pathKeys(pattern = it) - } + RouteParser.pathKeys(pattern = route) - ).distinct() - } } diff --git a/routeProcessor/src/main/kotlin/RouteDefinition.kt b/routeProcessor/src/main/kotlin/RouteDefinition.kt index a575a5f70..f4a0d6ebe 100644 --- a/routeProcessor/src/main/kotlin/RouteDefinition.kt +++ b/routeProcessor/src/main/kotlin/RouteDefinition.kt @@ -75,7 +75,9 @@ internal data class NestedRouteDefinition( ) : RouteDefinition { override fun generateRoute(): String { - return if (superQualifiedName.isEmpty()) generateRootRoute() else generateIRoute() + return if (superQualifiedName.isEmpty() || + superQualifiedName == "kotlin.Any" + ) generateRootRoute() else generateIRoute() } private fun generateRootRoute(): String { diff --git a/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt b/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt index 3cb6ac254..b57aa1a3c 100644 --- a/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt +++ b/services/src/main/java/com/twidere/services/serializer/DateQueryConverterFactory.kt @@ -30,9 +30,9 @@ import java.util.Locale class DateQueryConverterFactory : Converter.Factory() { override fun stringConverter( type: Type, - annotations: Array?, - retrofit: Retrofit? - ): Converter<*, String?>? { + annotations: Array, + retrofit: Retrofit + ): Converter<*, String>? { return if (type == Date::class.java) { DateQueryConverter.INSTANCE } else { @@ -40,16 +40,12 @@ class DateQueryConverterFactory : Converter.Factory() { } } - private class DateQueryConverter : Converter { + private class DateQueryConverter : Converter { val simpleDateFormat by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH) } - override fun convert(date: Date?): String? { - return if (date == null) { - null - } else { - simpleDateFormat.format(date) - } + override fun convert(date: Date): String? { + return simpleDateFormat.format(date) } companion object { val INSTANCE = DateQueryConverter() From 399315b1e326e21b9d81be3ac1cadbf78355174a Mon Sep 17 00:00:00 2001 From: seiko Date: Sat, 7 May 2022 15:40:20 +0800 Subject: [PATCH 595/615] apply spotless --- .../moe/tlaster/precompose/navigation/route/SceneRoute.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt index 4a9044023..3862c8c5c 100644 --- a/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt +++ b/common/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt @@ -29,5 +29,4 @@ internal class SceneRoute( val navTransition: NavTransition?, val deepLinks: List, content: @Composable (BackStackEntry) -> Unit, -) : ComposeRoute(route, content) { -} +) : ComposeRoute(route, content) From 9ab9a45987f1dfb0475743712ff581abd6ded206 Mon Sep 17 00:00:00 2001 From: seiko Date: Sat, 7 May 2022 16:13:14 +0800 Subject: [PATCH 596/615] jdk to 11 --- buildSrc/src/main/kotlin/Versions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index be5a92a38..103cce646 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -8,8 +8,8 @@ object Versions { } object Java { - const val jvmTarget = "17" - val java = JavaVersion.VERSION_17 + const val jvmTarget = "11" + val java = JavaVersion.VERSION_11 } const val ksp = "${Kotlin.lang}-1.0.5" From 744cc5333e98066541c86ccd085e829a5305c5e8 Mon Sep 17 00:00:00 2001 From: seiko Date: Wed, 11 May 2022 20:45:01 +0800 Subject: [PATCH 597/615] open selection in timeline --- .../twiderex/component/status/SelectionContainer.kt | 10 ++++++++++ .../component/status/TimelineStatusComponent.kt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt index 66a6337ba..118eaebb5 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/SelectionContainer.kt @@ -22,11 +22,13 @@ package com.twidere.twiderex.component.status import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalFocusManager import com.twidere.twiderex.kmp.Platform import com.twidere.twiderex.kmp.currentPlatform import kotlinx.coroutines.cancel @@ -52,6 +54,14 @@ fun SelectionContainer( content.invoke(null) return } + val focusManager = LocalFocusManager.current + DisposableEffect(Unit) { + onDispose { + // clear focus after ui hide, otherwise: + // java.lang.IllegalStateException: KeyEvent can't be processed because this key input node is not active. + focusManager.clearFocus() + } + } val positionWrapper = remember { if (currentPlatform != Platform.Android) PositionWrapper() else null } diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt index ae0915ce1..7e151d522 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/TimelineStatusComponent.kt @@ -160,7 +160,7 @@ private fun NormalStatus( } } }, - isSelectionAble = false, + isSelectionAble = true, ) } } From f11f98087323bc18fb5943b86e7ab60c6ed89866 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 12 May 2022 10:17:27 +0800 Subject: [PATCH 598/615] upgrade compose_jb version --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 103cce646..a913d4f38 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -19,7 +19,7 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose_jb = "1.2.0-alpha01-dev675" + const val compose_jb = "1.2.0-alpha01-dev683" const val paging = "3.1.0" const val activity = "1.4.0" const val datastore = "1.0.0" From ffd988d7776d80480684b27e9ed1b12b99123175 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 12 May 2022 10:40:51 +0800 Subject: [PATCH 599/615] fix warn of consume() --- .../kotlin/com/mxalbert/zoomable/Zoomable.kt | 13 +++++++------ .../twidere/twiderex/component/foundation/Pager.kt | 5 +++-- .../component/foundation/SwipeToRefreshLayout.kt | 6 +++--- .../twidere/twiderex/component/status/HtmlText.kt | 10 ++++++---- .../twidere/twiderex/navigation/ComposeDebugTool.kt | 5 +++-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt index c62114c7c..1ed83a179 100644 --- a/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt +++ b/common/src/commonMain/kotlin/com/mxalbert/zoomable/Zoomable.kt @@ -33,13 +33,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.center import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.consumePositionChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.layout.layout @@ -195,6 +195,7 @@ internal suspend fun PointerInputScope.detectTapAndDragGestures( } } +@OptIn(ExperimentalComposeUiApi::class) private suspend fun PointerInputScope.detectDragGestures( state: ZoomableState, dismissGestureEnabled: State, @@ -210,21 +211,21 @@ private suspend fun PointerInputScope.detectDragGestures( var overSlop = Offset.Zero val drag = if (state.isZooming) { awaitTouchSlopOrCancellation(down.id) { change, over -> - change.consumePositionChange() + if (change.positionChange() != Offset.Zero) change.consume() overSlop = over } } else { awaitVerticalTouchSlopOrCancellation(down.id) { change, over -> - change.consumePositionChange() + if (change.positionChange() != Offset.Zero) change.consume() overSlop = Offset(0f, over) } } if (drag != null) { onDrag(drag, overSlop) if ( - !drag(drag.id) { - onDrag(it, it.positionChange()) - it.consumePositionChange() + !drag(drag.id) { change -> + onDrag(change, change.positionChange()) + if (change.positionChange() != Offset.Zero) change.consume() } ) { onDragCancel() diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt index 67d628770..627e46d1e 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/Pager.kt @@ -40,11 +40,11 @@ import androidx.compose.runtime.saveable.listSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.consumePositionChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.input.pointer.util.VelocityTracker @@ -195,6 +195,7 @@ private data class PageData(val page: Int) : ParentDataModifier { private val Measurable.page: Int get() = (parentData as? PageData)?.page ?: error("no PageData for measurable $this") +@OptIn(ExperimentalComposeUiApi::class) @Composable fun Pager( modifier: Modifier = Modifier, @@ -235,7 +236,7 @@ fun Pager( val newPos = (pos + dragAmount).coerceIn(min.toFloat(), max.toFloat()) if (newPos != 0f) { - change.consumePositionChange() + if (change.positionChange() != Offset.Zero) change.consume() addPosition(change.uptimeMillis, change.position) coroutineScope.launch { snapToOffset(newPos / pageSize) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt index b51f82ed9..88a6c2324 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/foundation/SwipeToRefreshLayout.kt @@ -49,6 +49,7 @@ import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection @@ -56,7 +57,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.consumePositionChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.platform.LocalDensity @@ -229,7 +229,7 @@ private class SwipeToRefreshState( private fun Offset.toFloat(): Float = this.y } -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterialApi::class) @Composable fun SwipeToRefreshLayout( modifier: Modifier = Modifier, @@ -266,7 +266,7 @@ fun SwipeToRefreshLayout( detectVerticalDrag( onVerticalDrag = { change, dragAmount -> if (state.drag(dragAmount) != 0f) { - change.consumePositionChange() + if (change.positionChange() != Offset.Zero) change.consume() } }, onDragEnd = { diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt index 32ed9dca1..688be99d4 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/component/status/HtmlText.kt @@ -36,9 +36,9 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.ExperimentalComposeApi import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.consumeDownChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.AnnotatedString @@ -135,7 +135,7 @@ fun HtmlText( } } -@OptIn(ExperimentalUnitApi::class) +@OptIn(ExperimentalComposeUiApi::class, ExperimentalUnitApi::class) @Composable private fun RenderContent( modifier: Modifier = Modifier, @@ -192,9 +192,11 @@ private fun RenderContent( .firstOrNull() } if (annotation != null) { - change.consumeDownChange() + if (change.pressed != change.previousPressed) change.consume() val up = awaitPointerEventScope { - waitForUpOrCancellation()?.also { it.consumeDownChange() } + waitForUpOrCancellation()?.also { + if (it.pressed != it.previousPressed) it.consume() + } } if (up != null) { onLinkClicked.invoke(annotation.item) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt index e8fdf90c3..fbc9fdc63 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/navigation/ComposeDebugTool.kt @@ -36,8 +36,8 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.consumeAllChanges import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -46,6 +46,7 @@ import moe.tlaster.precompose.navigation.NavController import moe.tlaster.precompose.navigation.currentBackStackEntryAsState import kotlin.math.roundToInt +@OptIn(ExperimentalComposeUiApi::class) @Composable fun ComposeDebugTool( rootNavController: NavController, @@ -76,7 +77,7 @@ fun ComposeDebugTool( .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) } .pointerInput(Unit) { detectDragGestures { change, dragAmount -> - change.consumeAllChanges() + change.consume() offsetX += dragAmount.x offsetY += dragAmount.y } From 7d9eac9d9b602a8e156a00ca5be68163a02f38b4 Mon Sep 17 00:00:00 2001 From: seiko Date: Thu, 12 May 2022 11:18:01 +0800 Subject: [PATCH 600/615] fix unit test --- .../kotlin/moe/tlaster/precompose/navigation/TestRoute.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt index b2abd4989..74b0e8d23 100644 --- a/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt +++ b/common/src/commonTest/kotlin/moe/tlaster/precompose/navigation/TestRoute.kt @@ -25,7 +25,6 @@ import moe.tlaster.precompose.navigation.route.Route class TestRoute( override val route: String, val id: String, - override val pathKeys: List = emptyList(), ) : Route fun RouteBuilder.testRoute( From ee7b7fcadbda81421bc14196bfd127a0a59d6d26 Mon Sep 17 00:00:00 2001 From: seiko Date: Fri, 13 May 2022 10:15:15 +0800 Subject: [PATCH 601/615] use compose window insets --- android/build.gradle.kts | 1 - .../com/twidere/twiderex/TwidereXActivity.kt | 33 +-- buildSrc/src/main/kotlin/Versions.kt | 2 +- common/build.gradle.kts | 16 +- .../twiderex/component/PlatformInsets.kt | 216 ++++++++---------- .../lifecycle/PreComposeActivity.kt | 17 -- 6 files changed, 120 insertions(+), 165 deletions(-) diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 7f3dda6cb..eab26b786 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -136,7 +136,6 @@ android { dependencies { implementation("androidx.activity:activity-ktx:${Versions.activity}") implementation(projects.common) - implementation("com.google.accompanist:accompanist-insets:${Versions.accompanist}") implementation("androidx.startup:startup-runtime:${Versions.startup}") if (enableGoogleVariant) { diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 7e8607aa1..87a118303 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -31,11 +31,9 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.Composable @@ -54,7 +52,6 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope -import com.google.accompanist.insets.ProvideWindowInsets import com.twidere.twiderex.action.LocalStatusActions import com.twidere.twiderex.action.StatusActions import com.twidere.twiderex.component.LocalWindowInsetsController @@ -146,17 +143,11 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { lightColors() } ) { - Scaffold { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Image( - painter = painterResource(id = R.drawable.ic_login_logo), - contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twidere) - ) - } + Box(Modifier.fillMaxSize(), Alignment.Center) { + Image( + painter = painterResource(id = R.drawable.ic_login_logo), + contentDescription = stringResource(id = com.twidere.common.R.string.accessibility_common_logo_twidere) + ) } } } @@ -184,14 +175,10 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { ProvidePreferences( preferencesHolder, ) { - ProvideWindowInsets( - windowInsetsAnimationsEnabled = true - ) { - Router( - navController = navController, - isDebug = moe.tlaster.kfilepicker.BuildConfig.DEBUG - ) - } + Router( + navController = navController, + isDebug = moe.tlaster.kfilepicker.BuildConfig.DEBUG + ) } } } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index a913d4f38..c7fb029c0 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -19,6 +19,7 @@ object Versions { const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" + const val compose = "1.2.0-beta01" const val compose_jb = "1.2.0-alpha01-dev683" const val paging = "3.1.0" const val activity = "1.4.0" @@ -30,7 +31,6 @@ object Versions { const val work = "2.7.1" const val startup = "1.1.0" const val coil = "2.0.0-rc03" - const val accompanist = "0.23.0" const val accompanist_jb = "0.20.1" const val androidx_exifinterface = "1.3.3" const val exoplayer = "2.17.1" diff --git a/common/build.gradle.kts b/common/build.gradle.kts index ee39b7594..73f1f92e0 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,5 +1,4 @@ -import org.jetbrains.compose.compose import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.konan.properties.loadProperties @@ -42,10 +41,10 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - api(compose.runtime) - api(compose.foundation) - api(compose.material) - api(compose.materialIconsExtended) + api("org.jetbrains.compose.runtime:runtime:${Versions.compose_jb}") + api("org.jetbrains.compose.foundation:foundation:${Versions.compose_jb}") + api("org.jetbrains.compose.material:material:${Versions.compose_jb}") + api("org.jetbrains.compose.material:material-icons-extended:${Versions.compose_jb}") implementation(projects.services) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:${Versions.Kotlin.coroutines}") @@ -77,8 +76,12 @@ kotlin { } val androidMain by getting { dependencies { + api("androidx.compose.runtime:runtime:${Versions.compose}") + api("androidx.compose.foundation:foundation:${Versions.compose}") + api("androidx.compose.material:material:${Versions.compose}") + api("androidx.compose.material:material-icons-extended:${Versions.compose}") implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") - implementation("androidx.savedstate:savedstate-ktx:1.1.0") + implementation("androidx.savedstate:savedstate-ktx:1.2.0-beta01") implementation("androidx.core:core-ktx:1.8.0-alpha02") implementation("io.insert-koin:koin-android:${Versions.koin}") implementation("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") @@ -97,7 +100,6 @@ kotlin { implementation("androidx.datastore:datastore-preferences:${Versions.datastore}") implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") implementation("androidx.startup:startup-runtime:${Versions.startup}") - implementation("com.google.accompanist:accompanist-insets:${Versions.accompanist}") implementation("androidx.browser:browser:${Versions.browser}") implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02") implementation("androidx.activity:activity-compose:${Versions.activity}") diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 2acd33eee..0c77bb651 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -22,33 +22,33 @@ package com.twidere.twiderex.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.windowInsetsEndWidth +import androidx.compose.foundation.layout.windowInsetsStartWidth +import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.key import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.composed import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.zIndex import androidx.core.view.WindowInsetsControllerCompat -import com.google.accompanist.insets.HorizontalSide -import com.google.accompanist.insets.Insets -import com.google.accompanist.insets.LocalWindowInsets -import com.google.accompanist.insets.navigationBarsHeight -import com.google.accompanist.insets.navigationBarsPadding -import com.google.accompanist.insets.navigationBarsWidth -import com.google.accompanist.insets.statusBarsHeight -import com.google.accompanist.insets.statusBarsPadding -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -60,10 +60,10 @@ actual fun Modifier.bottomInsetsPadding(): Modifier = this.navigationBarsPadding actual fun Modifier.startInsetsPadding(): Modifier = this actual fun Modifier.endInsetsPadding(): Modifier = this -actual fun Modifier.topInsetsHeight(): Modifier = this.statusBarsHeight() -actual fun Modifier.bottomInsetsHeight(): Modifier = this.navigationBarsHeight() -actual fun Modifier.startInsetsWidth(): Modifier = this.navigationBarsWidth(HorizontalSide.Left) -actual fun Modifier.endInsetsWidth(): Modifier = this.navigationBarsWidth(HorizontalSide.Right) +actual fun Modifier.topInsetsHeight(): Modifier = composed { this.windowInsetsTopHeight(WindowInsets.statusBars) } +actual fun Modifier.bottomInsetsHeight(): Modifier = composed { this.windowInsetsBottomHeight(WindowInsets.navigationBars) } +actual fun Modifier.startInsetsWidth(): Modifier = composed { this.windowInsetsStartWidth(WindowInsets.navigationBars) } +actual fun Modifier.endInsetsWidth(): Modifier = composed { this.windowInsetsEndWidth(WindowInsets.navigationBars) } @Composable actual fun PlatformInsets( @@ -84,74 +84,51 @@ actual fun PlatformInsets( ) Box( modifier = Modifier - .padding( - actual.let { - with(LocalDensity.current) { - val layoutDirection = LocalLayoutDirection.current - PaddingValues( - top = it.top.toDp(), - bottom = it.bottom.toDp(), - start = when (layoutDirection) { - LayoutDirection.Ltr -> it.left.toDp() - LayoutDirection.Rtl -> it.right.toDp() - }, - end = when (layoutDirection) { - LayoutDirection.Ltr -> it.right.toDp() - LayoutDirection.Rtl -> it.left.toDp() - }, - ) - } - } - ) + .padding(actual.asPaddingValues()) .align(Alignment.Center) ) { content() } - Spacer( - modifier = if (!control.extendToTop) { - Modifier - .statusBarsHeight() - .navigationBarsPadding(bottom = false) + if (!control.extendToTop) { + Spacer( + modifier = Modifier + .topInsetsHeight() .zIndex(999F) .fillMaxWidth() .background(color.top) - } else { - Modifier - }.align(Alignment.TopCenter) - ) - Spacer( - modifier = if (!control.extendToStart) { - Modifier - .navigationBarsWidth(HorizontalSide.Left) + .align(Alignment.TopCenter) + ) + } + if (!control.extendToStart) { + Spacer( + modifier = Modifier + .startInsetsWidth() .zIndex(999F) .fillMaxHeight() .background(color.start) - } else { - Modifier - }.align(Alignment.CenterStart) - ) - Spacer( - modifier = if (!control.extendToEnd) { - Modifier - .navigationBarsWidth(HorizontalSide.Right) - .fillMaxHeight() + .align(Alignment.CenterStart) + ) + } + if (!control.extendToEnd) { + Spacer( + modifier = Modifier + .endInsetsWidth() .zIndex(999F) + .fillMaxHeight() .background(color.end) - } else { - Modifier - }.align(Alignment.CenterEnd) - ) - Spacer( - modifier = if (!control.extendToBottom) { - Modifier - .navigationBarsHeight() + .align(Alignment.CenterEnd) + ) + } + if (!control.extendToBottom) { + Spacer( + modifier = Modifier + .bottomInsetsHeight() .zIndex(999F) .fillMaxWidth() .background(color.bottom) - } else { - Modifier - }.align(Alignment.BottomCenter) - ) + .align(Alignment.BottomCenter) + ) + } } } @@ -160,9 +137,11 @@ actual fun ImeVisibleWithInsets( filter: ((Boolean) -> Boolean)?, collectIme: ((Boolean) -> Unit)? ) { - val ime = LocalWindowInsets.current.ime + val density = LocalDensity.current + val ime = WindowInsets.ime + val navigation = WindowInsets.navigationBars LaunchedEffect(ime) { - snapshotFlow { ime.isVisible } + snapshotFlow { ime.getBottom(density) > navigation.getBottom(density) } .distinctUntilChanged() .filter { filter?.invoke(it) ?: false } .collect { @@ -176,10 +155,10 @@ actual fun ImeHeightWithInsets( filter: ((Int) -> Boolean)?, collectIme: ((Int) -> Unit)? ) { - val ime = LocalWindowInsets.current.ime - + val density = LocalDensity.current + val ime = WindowInsets.ime LaunchedEffect(ime) { - snapshotFlow { ime.bottom } + snapshotFlow { ime.getBottom(density) } .distinctUntilChanged() .filter { filter?.invoke(it) ?: false } .collect { @@ -190,53 +169,58 @@ actual fun ImeHeightWithInsets( @Composable actual fun ImeBottomInsets(): Dp { - val navigation = LocalWindowInsets.current.navigationBars - val ime = LocalWindowInsets.current.ime - ime.bottom.coerceAtLeast(navigation.bottom) + val density = LocalDensity.current + val ime = WindowInsets.ime + val navigation = WindowInsets.navigationBars + ime.getBottom(density).coerceAtLeast(navigation.getBottom(density)) return with(LocalDensity.current) { - ime.bottom.coerceAtLeast(navigation.bottom).toDp() + ime.getBottom(density).coerceAtLeast(navigation.getBottom(density)).toDp() } } @Composable private fun provideSystemInsets( extendViewIntoNavigationBar: Boolean, - extendViewIntoStatusBar: Boolean -): Insets { - val ime = LocalWindowInsets.current.ime - val navigation = LocalWindowInsets.current.navigationBars - val status = LocalWindowInsets.current.statusBars - return key( - ime, - ime.isVisible, - navigation, - status - ) { - ime.copy( - left = if (extendViewIntoNavigationBar) { - 0 - } else { - ime.left.coerceAtLeast(navigation.left) - }, - right = if (extendViewIntoNavigationBar) { - 0 - } else { - ime.right.coerceAtLeast(navigation.right) - }, - bottom = if (extendViewIntoNavigationBar) { - 0 - } else { - ime.bottom.coerceAtLeast(navigation.bottom) - }, - top = if (extendViewIntoNavigationBar) { - 0 - } else { - ime.top.coerceAtLeast(navigation.top) - } + if (extendViewIntoStatusBar) { - 0 - } else { - status.top - }, - ) - } + extendViewIntoStatusBar: Boolean, +): WindowInsets { + val density = LocalDensity.current + val layoutDirection = LocalLayoutDirection.current + + val ime = WindowInsets.ime + val navigation = WindowInsets.navigationBars + val status = WindowInsets.statusBars + return WindowInsets( + left = if (extendViewIntoNavigationBar) { + 0 + } else { + ime.getLeft(density, layoutDirection).coerceAtLeast( + navigation.getLeft(density, layoutDirection) + ) + }, + right = if (extendViewIntoNavigationBar) { + 0 + } else { + ime.getRight(density, layoutDirection).coerceAtLeast( + navigation.getRight(density, layoutDirection) + ) + }, + bottom = if (extendViewIntoNavigationBar) { + 0 + } else { + ime.getBottom(density).coerceAtLeast( + navigation.getBottom(density) + ) + }, + top = if (extendViewIntoNavigationBar) { + 0 + } else { + ime.getTop(density).coerceAtLeast( + navigation.getTop(density) + ) + } + if (extendViewIntoStatusBar) { + 0 + } else { + status.getTop(density) + }, + ) } diff --git a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt index 27955481b..e90b1590c 100644 --- a/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt +++ b/common/src/androidMain/kotlin/moe/tlaster/precompose/lifecycle/PreComposeActivity.kt @@ -26,9 +26,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionContext import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.ComposeView -import androidx.lifecycle.ViewTreeLifecycleOwner -import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.ViewTreeSavedStateRegistryOwner import moe.tlaster.precompose.ui.BackDispatcher import moe.tlaster.precompose.ui.BackDispatcherOwner import moe.tlaster.precompose.ui.LocalBackDispatcherOwner @@ -42,7 +39,6 @@ open class PreComposeActivity : LifecycleOwner, ViewModelStoreOwner, androidx.lifecycle.LifecycleOwner, - SavedStateRegistryOwner, BackDispatcherOwner, androidx.lifecycle.LifecycleObserver { override val lifecycle by lazy { @@ -99,23 +95,10 @@ fun PreComposeActivity.setContent( setContent { ContentInternal(content) } - // Set the view tree owners before setting the content view so that the inflation process - // and attach listeners will see them already present - setOwners() setContentView(this, DefaultActivityContentLayoutParams) } } -private fun PreComposeActivity.setOwners() { - val decorView = window.decorView - if (ViewTreeLifecycleOwner.get(decorView) == null) { - ViewTreeLifecycleOwner.set(decorView, this) - } - if (ViewTreeSavedStateRegistryOwner.get(decorView) == null) { - ViewTreeSavedStateRegistryOwner.set(decorView, this) - } -} - @Composable private fun PreComposeActivity.ContentInternal(content: @Composable () -> Unit) { ProvideAndroidCompositionLocals { From 2e2e82766ba5b6073dc28abc5586dff158cba943 Mon Sep 17 00:00:00 2001 From: Yama <4602400+nuzar@users.noreply.github.com> Date: Thu, 19 May 2022 10:11:55 +0800 Subject: [PATCH 602/615] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aedadba47..43f1b5f4a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Next generation of Twidere for Android 5.0+. **Still in early stage.** - [Mastodon] Add federated timeline and local timeline ### What is being planned for 1.6.0 - Aug 2021 Update -For 1.6.0, we're planning to build an experimatal desktop version, this is a big step, you can check out our [milestore](https://github.com/TwidereProject/TwidereX-Android/milestone/4) for detail. Here is a shortlist: +For 1.6.0, we're planning to build an experimatal desktop version, this is a big step, you can check out our [milestone](https://github.com/TwidereProject/TwidereX-Android/milestone/4) for detail. Here is a shortlist: - Experimatal desktop version. - Mute and block support. From c4990bacf90c9bb9aa7de6f21b6846257b1481d7 Mon Sep 17 00:00:00 2001 From: Terence Eden Date: Thu, 26 May 2022 22:02:15 +0100 Subject: [PATCH 603/615] Include extended mode and alt text in lists and mentions Fixes #447 --- .../com/twidere/services/twitter/api/TimelineResources.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt b/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt index 326ffc6af..1e8a65355 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/TimelineResources.kt @@ -45,6 +45,8 @@ interface TimelineResources { @Query("trim_user") trim_user: Boolean? = null, @Query("exclude_replies") exclude_replies: Boolean? = null, @Query("include_entities") include_entities: Boolean? = null, + @Query("tweet_mode") tweet_mode: String = "extended", + @Query("include_ext_alt_text") include_ext_alt_text: Boolean = true, ): List @GET("/1.1/statuses/user_timeline.json") @@ -83,5 +85,7 @@ interface TimelineResources { @Query("max_id") max_id: String? = null, @Query("include_entities") include_entities: Boolean? = null, @Query("include_rts") include_rts: Boolean? = null, + @Query("tweet_mode") tweet_mode: String = "extended", + @Query("include_ext_alt_text") include_ext_alt_text: Boolean = true, ): List } From c91cf7382d0da399fad970a20fb6f6b3e9c6f162 Mon Sep 17 00:00:00 2001 From: seiko Date: Mon, 13 Jun 2022 09:49:04 +0800 Subject: [PATCH 604/615] share retweeted tweet link --- .../commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt index aee06a373..4e155727a 100644 --- a/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt +++ b/common/src/commonMain/kotlin/com/twidere/twiderex/model/ui/UiStatus.kt @@ -88,7 +88,7 @@ data class UiStatus( } fun generateShareLink() = "https://${statusKey.host}" + when (platformType) { - PlatformType.Twitter -> "/${user.screenName}/status/$statusId" + PlatformType.Twitter -> (retweet ?: this).let { "/${it.user.screenName}/status/${it.statusId}" } PlatformType.StatusNet -> TODO() PlatformType.Fanfou -> TODO() PlatformType.Mastodon -> "/web/statuses/$statusId" From 6b5ba0f4ac7974b51f6bdca7f5e92da421aed056 Mon Sep 17 00:00:00 2001 From: seiko Date: Tue, 28 Jun 2022 12:15:42 +0800 Subject: [PATCH 605/615] upgrade dependencies --- build.gradle.kts | 1 - buildSrc/src/main/kotlin/AndroidSdk.kt | 4 +- buildSrc/src/main/kotlin/Versions.kt | 47 +++++++++---------- common/build.gradle.kts | 15 +++--- .../twiderex/kmp/OverScrollConfiguration.kt | 6 +-- .../LimitOffsetTransformPagingSource.kt | 4 +- .../paging/compose/LazyPagingItems.kt | 2 +- 7 files changed, 38 insertions(+), 41 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9d8c85f69..145e80e7f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,6 @@ buildscript { dependencies { classpath(kotlin("gradle-plugin", version = Versions.Kotlin.lang)) classpath("com.android.tools.build:gradle:${Versions.agp}") - classpath("com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}") } } diff --git a/buildSrc/src/main/kotlin/AndroidSdk.kt b/buildSrc/src/main/kotlin/AndroidSdk.kt index 6bd1461cc..89c12700d 100644 --- a/buildSrc/src/main/kotlin/AndroidSdk.kt +++ b/buildSrc/src/main/kotlin/AndroidSdk.kt @@ -1,6 +1,6 @@ object AndroidSdk { const val min = 21 - const val compile = 31 + const val compile = 32 const val target = compile - const val buildTools = "31.0.0" + const val buildTools = "32.0.0" } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index f7e57af9f..9efd70f8d 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -3,46 +3,45 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { const val lang = "1.6.21" - const val coroutines = "1.6.1" - const val serialization = "1.3.2" + const val coroutines = "1.6.3" + const val serialization = "1.3.3" } object Java { - const val jvmTarget = "11" - val java = JavaVersion.VERSION_11 + const val jvmTarget = "17" + val java = JavaVersion.VERSION_17 } const val ksp = "${Kotlin.lang}-1.0.5" - const val agp = "7.2.0-rc01" - const val spotless = "6.5.0" - const val ktlint = "0.45.2" - const val okhttp = "4.9.1" + const val agp = "7.2.1" + const val spotless = "6.7.2" + const val ktlint = "0.46.1" + const val okhttp = "4.10.0" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose = "1.2.0-beta01" - const val compose_jb = "1.2.0-alpha01-dev683" - const val paging = "3.1.0" - const val activity = "1.4.0" + const val compose = "1.2.0-rc01" + const val compose_jb = "1.2.0-alpha01-dev725" + const val paging = "3.2.0-alpha01" + const val activity = "1.6.0-alpha05" const val datastore = "1.0.0" - const val androidx_hilt = "1.0.0" - const val room = "2.4.2" - const val lifecycle = "2.4.1" - const val lifecycle_compose = "2.4.1" - const val work = "2.7.1" - const val startup = "1.1.0" + const val room = "2.5.0-alpha02" + const val lifecycle = "2.5.0-rc02" + const val work = "2.8.0-alpha02" + const val startup = "1.2.0-alpha01" const val coil = "2.0.0-rc03" + const val accompanist = "0.24.12-rc" const val accompanist_jb = "0.20.1" const val androidx_exifinterface = "1.3.3" - const val exoplayer = "2.17.1" + const val exoplayer = "2.18.0" const val browser = "1.4.0" const val protobuf = "3.20.0" - const val androidx_test = "1.4.1-alpha03" - const val extJUnitVersion = "1.1.4-alpha03" - const val espressoVersion = "3.5.0-alpha03" + const val androidx_test = "1.5.0-alpha01" + const val extJUnitVersion = "1.1.4-alpha07" + const val espressoVersion = "3.5.0-alpha07" const val koin = "3.2.0-beta-1" const val moko = "0.19.0" const val sqlDelight = "1.5.3" - const val javafx = "0.0.10" + const val javafx = "0.0.13" const val kFilePicker = "1.0.4" - const val jodaTime = "2.10.13" + const val jodaTime = "2.10.14" } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 5b071c9c9..83a081efd 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -9,7 +9,7 @@ plugins { id("com.android.library") id("com.google.devtools.ksp").version(Versions.ksp) id("dev.icerock.mobile.multiplatform-resources") version Versions.moko - id("com.squareup.sqldelight") + id("com.squareup.sqldelight") version Versions.sqlDelight id("com.codingfeline.buildkonfig") version "0.11.0" } @@ -41,10 +41,10 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - api("org.jetbrains.compose.runtime:runtime:${Versions.compose_jb}") - api("org.jetbrains.compose.foundation:foundation:${Versions.compose_jb}") - api("org.jetbrains.compose.material:material:${Versions.compose_jb}") - api("org.jetbrains.compose.material:material-icons-extended:${Versions.compose_jb}") + api(compose.runtime) + api(compose.foundation) + api(compose.material) + api(compose.materialIconsExtended) implementation(projects.services) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.Kotlin.coroutines}") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:${Versions.Kotlin.coroutines}") @@ -82,8 +82,7 @@ kotlin { api("androidx.compose.material:material:${Versions.compose}") api("androidx.compose.material:material-icons-extended:${Versions.compose}") implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}") - implementation("androidx.savedstate:savedstate-ktx:1.2.0-beta01") - implementation("androidx.core:core-ktx:1.8.0-alpha02") + implementation("androidx.core:core-ktx:1.9.0-alpha05") implementation("io.insert-koin:koin-android:${Versions.koin}") implementation("io.insert-koin:koin-androidx-workmanager:${Versions.koin}") implementation("androidx.work:work-runtime-ktx:${Versions.work}") @@ -102,7 +101,7 @@ kotlin { implementation("androidx.exifinterface:exifinterface:${Versions.androidx_exifinterface}") implementation("androidx.startup:startup-runtime:${Versions.startup}") implementation("androidx.browser:browser:${Versions.browser}") - implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02") + implementation("androidx.vectordrawable:vectordrawable:1.2.0-beta01") implementation("androidx.activity:activity-compose:${Versions.activity}") implementation("com.github.android:renderscript-intrinsics-replacement-toolkit:b6363490c3") } diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt index 103e1ee33..ecc758bfc 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/kmp/OverScrollConfiguration.kt @@ -21,8 +21,8 @@ package com.twidere.twiderex.kmp import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.gestures.LocalOverScrollConfiguration -import androidx.compose.foundation.gestures.OverScrollConfiguration +import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.OverscrollConfiguration import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -33,7 +33,7 @@ actual fun ProvideOverScrollConfiguration( content: @Composable () -> Unit ) { CompositionLocalProvider( - LocalOverScrollConfiguration provides OverScrollConfiguration( + LocalOverscrollConfiguration provides OverscrollConfiguration( glowColor = MaterialTheme.colors.primary, ) ) { diff --git a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt index 334ea3fa4..228c1ef47 100644 --- a/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt +++ b/common/src/androidMain/kotlin/com/twidere/twiderex/room/db/paging/LimitOffsetTransformPagingSource.kt @@ -37,11 +37,11 @@ internal class LimitOffsetTransformPagingSource( */ private val queryItemCount: suspend () -> Int, private val db: RoomDatabase, - vararg tables: String + tables: Array, ) : LimitOffsetPagingSource(Dispatchers.IO) { private val observer = object : InvalidationTracker.Observer(tables) { - override fun onInvalidated(tables: MutableSet) { + override fun onInvalidated(tables: Set) { invalidate() } } diff --git a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt index 770214dfe..fbc9ac82b 100644 --- a/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt +++ b/common/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt @@ -96,7 +96,7 @@ public class LazyPagingItems internal constructor( private val pagingDataDiffer = object : PagingDataDiffer( differCallback = differCallback, - mainDispatcher = mainDispatcher + mainContext = mainDispatcher ) { override suspend fun presentNewList( previousList: NullPaddedList, From a376332c201d228a8ac1a74ae91e6df5f4b3200b Mon Sep 17 00:00:00 2001 From: seiko Date: Tue, 28 Jun 2022 13:06:05 +0800 Subject: [PATCH 606/615] ktlint to 0.45.2 --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 9efd70f8d..2d944e2cf 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -15,7 +15,7 @@ object Versions { const val ksp = "${Kotlin.lang}-1.0.5" const val agp = "7.2.1" const val spotless = "6.7.2" - const val ktlint = "0.46.1" + const val ktlint = "0.45.2" const val okhttp = "4.10.0" const val retrofit2 = "2.9.0" const val hson = "0.1.4" From 5969360fc2201f31394f46609388062b488193d6 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Mon, 11 Jul 2022 12:05:47 +0800 Subject: [PATCH 607/615] add native window support --- common/build.gradle.kts | 1 + .../twiderex/component/NativeWindow.kt | 312 +----------------- .../twiderex/component/PlatformInsets.kt | 6 + .../kotlin/com/twidere/twiderex/main.kt | 2 - 4 files changed, 24 insertions(+), 297 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 33ec230f2..e41bca80b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -128,6 +128,7 @@ kotlin { implementation("de.huxhorn.lilith:de.huxhorn.lilith.3rdparty.junique:1.0.4") implementation("org.javassist:javassist:3.28.0-GA") implementation("org.ocpsoft.prettytime:prettytime:5.0.2.Final") + implementation("com.mayakapps.compose:window-styler:0.3.2") } } val desktopTest by getting diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt index 338f94c91..fff1af9e7 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/NativeWindow.kt @@ -20,41 +20,10 @@ */ package com.twidere.twiderex.component -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.window.WindowDraggableArea -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.contentColorFor -import androidx.compose.material.darkColors -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Fullscreen -import androidx.compose.material.icons.filled.FullscreenExit -import androidx.compose.material.icons.filled.Minimize -import androidx.compose.material.lightColors import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -63,20 +32,13 @@ import androidx.compose.runtime.saveable.listSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.key.KeyEvent -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.FrameWindowScope -import androidx.compose.ui.window.WindowPlacement -import androidx.compose.ui.window.WindowScope import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberWindowState -import androidx.compose.ui.zIndex +import com.mayakapps.compose.windowstyler.WindowBackdrop +import com.mayakapps.compose.windowstyler.WindowStyle import com.twidere.twiderex.kmp.LocalPlatformWindow import com.twidere.twiderex.kmp.PlatformWindow import com.twidere.twiderex.utils.OperatingSystem @@ -143,8 +105,8 @@ fun NativeWindow( visible = visible, title = title, icon = icon, - undecorated = true, - transparent = true, + undecorated = false, + transparent = false, resizable = resizable, enabled = enabled, focusable = focusable, @@ -153,261 +115,21 @@ fun NativeWindow( onKeyEvent = onKeyEvent, onCloseRequest = onCloseRequest, content = { - Box( - modifier = Modifier.clip(RoundedCornerShape(8.dp)), - ) { - Box( - modifier = Modifier.fillMaxSize(), - ) { - content.invoke(this@PreComposeWindow) - } - Box( - modifier = Modifier.fillMaxWidth().zIndex(Float.MAX_VALUE), - contentAlignment = Alignment.TopCenter, - ) { - PlatformTitleBar( - title = title, - icon = icon, - isAppearanceLightTitleBar = nativeWindowController.isAppearanceLightTitleBar, - operatingSystem = currentOperatingSystem, - onCloseRequest = onCloseRequest, - onMinimizeRequest = { state.isMinimized = true }, - onMaximizeRequest = { state.placement = WindowPlacement.Maximized }, - onUndoMaximizeRequest = { state.placement = WindowPlacement.Floating }, - isMaximized = state.placement == WindowPlacement.Maximized, - ) + if (currentOperatingSystem == OperatingSystem.MacOS) { + LaunchedEffect(Unit) { + window.rootPane.apply { + putClientProperty("apple.awt.fullWindowContent", true) + putClientProperty("apple.awt.transparentTitleBar", true) + } } + } else if (currentOperatingSystem == OperatingSystem.Windows) { + WindowStyle( + isDarkTheme = !nativeWindowController.isAppearanceLightTitleBar, + backdropType = WindowBackdrop.Mica, + ) } + content.invoke(this) }, ) } } - -@Composable -private fun WindowScope.PlatformTitleBar( - title: String, - icon: Painter?, - isAppearanceLightTitleBar: Boolean, - operatingSystem: OperatingSystem, - onCloseRequest: () -> Unit, - onMinimizeRequest: () -> Unit, - onMaximizeRequest: () -> Unit, - onUndoMaximizeRequest: () -> Unit, - isMaximized: Boolean = false, -) { - val platformWindow = LocalPlatformWindow.current - val windowBarVisibility by platformWindow.windowBarVisibility.collectAsState(initial = true) - WindowDraggableArea { - MaterialTheme( - colors = if (isAppearanceLightTitleBar) { - lightColors() - } else { - darkColors() - } - ) { - Surface( - color = Color.Transparent, - contentColor = contentColorFor(MaterialTheme.colors.background), - ) { - AnimatedVisibility( - windowBarVisibility, - enter = expandVertically(), - exit = shrinkVertically(), - ) { - when (operatingSystem) { - OperatingSystem.MacOS -> OSXTitleBar( - title = title, - icon = icon, - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) - else -> WindowsTitleBar( - title = title, - icon = icon, - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) - } - } - } - } - } -} - -@Composable -private fun WindowsTitleBar( - title: String, - icon: Painter?, - onCloseRequest: () -> Unit, - onMinimizeRequest: () -> Unit, - onMaximizeRequest: () -> Unit, - onUndoMaximizeRequest: () -> Unit, - isMaximized: Boolean = false, -) { - Row( - modifier = Modifier - .padding( - start = 16.dp, - top = 8.dp, - end = 8.dp, - bottom = 8.dp - ) - ) { - WindowTitle( - title = title, - icon = icon, - ) - Spacer(modifier = Modifier.weight(1f)) - WindowsWindowButtons( - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) - } -} - -@Composable -private fun WindowsWindowButtons( - onCloseRequest: () -> Unit, - onMinimizeRequest: () -> Unit, - onMaximizeRequest: () -> Unit, - onUndoMaximizeRequest: () -> Unit, - isMaximized: Boolean = false, -) { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - WindowButton( - icon = Icons.Filled.Minimize, - onClick = onMinimizeRequest - ) - if (isMaximized) { - WindowButton( - icon = Icons.Filled.FullscreenExit, - onClick = onUndoMaximizeRequest - ) - } else { - WindowButton( - icon = Icons.Filled.Fullscreen, - onClick = onMaximizeRequest - ) - } - WindowButton( - icon = Icons.Filled.Close, - onClick = onCloseRequest - ) - } -} - -@Composable -private fun WindowButton(icon: ImageVector, onClick: () -> Unit) { - IconButton( - onClick = onClick, - modifier = Modifier.size(24.dp) - ) { - Icon(icon, contentDescription = null) - } -} - -@Composable -private fun WindowTitle( - modifier: Modifier = Modifier, - title: String, - icon: Painter?, -) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically, - ) { - if (icon != null) { - Image(painter = icon, contentDescription = null, modifier = Modifier.size(24.dp)) - Spacer(modifier = Modifier.width(8.dp)) - } - Text(text = title) - } -} - -@Composable -private fun OSXTitleBar( - title: String, - icon: Painter?, - onCloseRequest: () -> Unit, - onMinimizeRequest: () -> Unit, - onMaximizeRequest: () -> Unit, - onUndoMaximizeRequest: () -> Unit, - isMaximized: Boolean = false, -) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp, horizontal = 16.dp), - ) { - OSXWindowButtons( - modifier = Modifier.align(Alignment.CenterStart), - onCloseRequest = onCloseRequest, - onMinimizeRequest = onMinimizeRequest, - onMaximizeRequest = onMaximizeRequest, - onUndoMaximizeRequest = onUndoMaximizeRequest, - isMaximized = isMaximized - ) - Box( - modifier = Modifier.align(Alignment.Center), - ) { - WindowTitle( - title = title, - icon = icon, - ) - } - } -} - -@Composable -private fun OSXWindowButtons( - modifier: Modifier = Modifier, - onCloseRequest: () -> Unit, - onMinimizeRequest: () -> Unit, - onMaximizeRequest: () -> Unit, - onUndoMaximizeRequest: () -> Unit, - isMaximized: Boolean = false, -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Box( - modifier = Modifier - .size(12.dp) - .background(Color(0xFFff5150), shape = CircleShape) - .clickable(onClick = onCloseRequest) - .clip(CircleShape) - ) - Box( - modifier = Modifier - .size(12.dp) - .background(Color(0xFFffbc00), shape = CircleShape) - .clickable(onClick = onMinimizeRequest) - .clip(CircleShape) - ) - Box( - modifier = Modifier - .size(12.dp) - .background(Color(0xFF00cd1d), shape = CircleShape) - .clickable(onClick = { - if (isMaximized) { - onUndoMaximizeRequest() - } else { - onMaximizeRequest() - } - }) - .clip(CircleShape) - ) - } -} - -val titleBarHeight = 8.dp * 2 + 24.dp diff --git a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt index 888e1accf..ba4bcc98c 100644 --- a/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt +++ b/common/src/desktopMain/kotlin/com/twidere/twiderex/component/PlatformInsets.kt @@ -33,7 +33,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex +import com.twidere.twiderex.utils.OperatingSystem +import com.twidere.twiderex.utils.currentOperatingSystem +private val titleBarHeight = when (currentOperatingSystem) { + OperatingSystem.MacOS -> 24.dp + else -> 0.dp +} actual fun Modifier.topInsetsPadding(): Modifier = this.padding(top = titleBarHeight) actual fun Modifier.bottomInsetsPadding(): Modifier = this actual fun Modifier.startInsetsPadding(): Modifier = this diff --git a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt index 9caa4976c..c615ddd20 100644 --- a/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt +++ b/desktop/src/jvmMain/kotlin/com/twidere/twiderex/main.kt @@ -23,11 +23,9 @@ package com.twidere.twiderex import androidx.compose.ui.ExperimentalComposeUiApi import com.twidere.twiderex.component.foundation.DesktopMediaPlayerHelper import com.twidere.twiderex.media.DesktopMediaPlayerFactoryImpl -import kotlinx.coroutines.Dispatchers @ExperimentalComposeUiApi fun main(args: Array) { DesktopMediaPlayerHelper.register(DesktopMediaPlayerFactoryImpl()) - Dispatchers runDesktopApp(args) } From 6c1f87f3c01ca28cb16bbb90d7de953df638177c Mon Sep 17 00:00:00 2001 From: seiko Date: Mon, 11 Jul 2022 12:22:54 +0800 Subject: [PATCH 608/615] kotlin to 1.7.0 --- buildSrc/src/main/kotlin/Versions.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 2d944e2cf..b42aa884b 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { object Kotlin { - const val lang = "1.6.21" + const val lang = "1.7.0" const val coroutines = "1.6.3" const val serialization = "1.3.3" } @@ -12,15 +12,15 @@ object Versions { val java = JavaVersion.VERSION_17 } - const val ksp = "${Kotlin.lang}-1.0.5" + const val ksp = "${Kotlin.lang}-1.0.6" const val agp = "7.2.1" const val spotless = "6.7.2" const val ktlint = "0.45.2" const val okhttp = "4.10.0" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose = "1.2.0-rc01" - const val compose_jb = "1.2.0-alpha01-dev725" + const val compose = "1.3.0-alpha01" + const val compose_jb = "1.2.0-alpha01-dev741" const val paging = "3.2.0-alpha01" const val activity = "1.6.0-alpha05" const val datastore = "1.0.0" From 17b6ad1faef00efa38a745b6972cde8691088427 Mon Sep 17 00:00:00 2001 From: seiko Date: Mon, 11 Jul 2022 14:00:04 +0800 Subject: [PATCH 609/615] fix MissingSuperCall --- android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index 87a118303..ab315fc60 100644 --- a/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/android/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -194,6 +194,7 @@ class TwidereXActivity : PreComposeActivity(), KoinComponent { } override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) intent?.data?.let { onDeeplink(it) } From ced6722af7aa7cf0539ec19e3fff572f628d1720 Mon Sep 17 00:00:00 2001 From: seiko Date: Mon, 11 Jul 2022 14:06:15 +0800 Subject: [PATCH 610/615] compileSdk to 33 --- buildSrc/src/main/kotlin/AndroidSdk.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/AndroidSdk.kt b/buildSrc/src/main/kotlin/AndroidSdk.kt index 89c12700d..c033f86a8 100644 --- a/buildSrc/src/main/kotlin/AndroidSdk.kt +++ b/buildSrc/src/main/kotlin/AndroidSdk.kt @@ -1,6 +1,6 @@ object AndroidSdk { const val min = 21 - const val compile = 32 + const val compile = 33 const val target = compile - const val buildTools = "32.0.0" + const val buildTools = "33.0.0" } From 1fe918318b7783797016ccd7b0a8630c67b26777 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 12 Jul 2022 16:16:33 +0800 Subject: [PATCH 611/615] version 1.6.0 --- buildSrc/src/main/kotlin/Package.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index 3c5ccd4f5..56baeb2b0 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -11,7 +11,7 @@ object Package { const val main = "1" const val mirror = "6" const val patch = "0" - const val revision = "dev03" - const val build = 58 + const val revision = "" + const val build = 59 } } From 2b02e3deed66004da7950e3fbb7861510167f098 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 12 Jul 2022 16:16:48 +0800 Subject: [PATCH 612/615] Update README.md --- README.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 43f1b5f4a..bbfb54b79 100644 --- a/README.md +++ b/README.md @@ -23,26 +23,22 @@ Next generation of Twidere for Android 5.0+. **Still in early stage.** ## What's Happening -### What's new in 1.5.0 - Aug 2021 Update - -- Proxy support, you can set a proxy for all the network request in settings. -- Tabs column editing support, you can now modify the order and count of the home tab. -- "Tweet sent" notification will be dismissed after showing once. -- Screen will keep on when playing media in media scene. -- Better RTL support for tweets. -- Fix certain crashes when requesting network. -- [Mastodon] Add support for gif avatar support and custom emoji in user name. -- [Mastodon] Add federated timeline and local timeline - -### What is being planned for 1.6.0 - Aug 2021 Update -For 1.6.0, we're planning to build an experimatal desktop version, this is a big step, you can check out our [milestone](https://github.com/TwidereProject/TwidereX-Android/milestone/4) for detail. Here is a shortlist: +### What's new in 1.6.0 - July 2022 Update - Experimatal desktop version. - Mute and block support. +- Gif insertion support - Optimizing video play for timeline. -- Bug fixes. -- UI/UX tweaking. -- Stability. +- Enable sensitive content blur for Twitter (by @enaix ) +- Fix startup crash in Android 12 +- Fix Clicking on the searchbar already with text (#304) +- Fix DMs tab is killed by block/deleted account (#384) +- Fix App crashes when viewing list members (#310) +- Add Share button for Tweet (#344) +- [Mastodon] Mastodon OAuth URL with spaces breaks when using DuckDuckGo as a browser (#326) + +### What is being planned for 1.7.0 - July 2022 Update +For 1.7.0, we're forcusing on bug fix and performance enhancemant, there will be lots of internal code changes. ### Roadmap for 2.0 - Jun 2021 Update For 2.0, we're considering these options, but it is still an early thought and might change over time. From 35e2336d7fc64ddbda35f272ce2c379e0df9a23e Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 12 Jul 2022 17:24:51 +0800 Subject: [PATCH 613/615] fix LimitOffsetTransformPagingSourceTest --- .../twiderex/paging/LimitOffsetTransformPagingSourceTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt index 4c8be6dfd..0662cfaeb 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt @@ -44,7 +44,8 @@ internal class LimitOffsetTransformPagingSourceTest { queryItemCount = { count }, - db = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java).build() + db = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java).build(), + tables = arrayOf("model"), ) @Test fun refreshReturnsResultPageWithCorrectParams() = runBlocking { From 8d0a1d42d8db47e11870f635e7351dec22be41df Mon Sep 17 00:00:00 2001 From: Tlaster Date: Wed, 13 Jul 2022 12:04:36 +0800 Subject: [PATCH 614/615] fix test --- .../twiderex/paging/LimitOffsetTransformPagingSourceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt index 0662cfaeb..74ec59ab4 100644 --- a/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt +++ b/common/src/androidAndroidTest/kotlin/com/twidere/twiderex/paging/LimitOffsetTransformPagingSourceTest.kt @@ -45,7 +45,7 @@ internal class LimitOffsetTransformPagingSourceTest { count }, db = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), RoomCacheDatabase::class.java).build(), - tables = arrayOf("model"), + tables = arrayOf(), ) @Test fun refreshReturnsResultPageWithCorrectParams() = runBlocking { From 78d7f5068c047a6041b055320e95f1e5a98fdde0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 14 Jul 2022 12:54:27 +0800 Subject: [PATCH 615/615] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbfb54b79..5a915a01d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Next generation of Twidere for Android 5.0+. **Still in early stage.** - [Mastodon] Mastodon OAuth URL with spaces breaks when using DuckDuckGo as a browser (#326) ### What is being planned for 1.7.0 - July 2022 Update -For 1.7.0, we're forcusing on bug fix and performance enhancemant, there will be lots of internal code changes. +For 1.7.0, we're forcusing on bug fix and performance enhancemant, there will be lots of internal code changes, you can check out our [milestone](https://github.com/TwidereProject/TwidereX-Android/milestone/6) for detail. ### Roadmap for 2.0 - Jun 2021 Update For 2.0, we're considering these options, but it is still an early thought and might change over time.

    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 diff --git a/android/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xxhdpi/ic_profile_image_twidere.png deleted file mode 100644 index 9abc85cc80e90d905828307ed10371c1530f7bec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21605 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 diff --git a/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png b/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_blue.png deleted file mode 100644 index afa6a10034218e8f8187e208deddf89cc6a2e6ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3719 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 ZcAEeBr9t)U^09aXOfcq#_4=-H{{w*Jy66A^ diff --git a/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png b/android/src/main/res/drawable-xxxhdpi-v4/ic_mastodon_logo_white.png deleted file mode 100644 index e21bddb54572dc13ddc9225f78af93a88e649f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2925 zcmV-z3zGDSP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA3j#?*K~#8N?VStE z9Yqz#d#^36AGH;vh}BX{r3yi46_5sP8bc)kiI5PH`iK(2ghZ^wpwak9Fs(%ssxi@6 zh=vGJc@z_g3PB#VB2q;Kks?q(S|}(~uwP5D?XAE6-2*mwd+$DG?w$Sa`6VZ3XCG%~ zXU@*<&d$zEa2&^R9LI4S$8j9TaU92S9LI4S$8j8I+|e*yTUxEwD^Z6dZ$M@uhngIM zyxQbdCI=!@kjcma$fQQ2@e1^Wy?gim2eliKejeFjvJH6-c^28q{!g>-KUjVe*(_n* zlSqr@+EE(^prB`v$;XlT#}PaVc{4H>IgIrKJrC0K0%{}j5VC>fJb?Zq@)wqOp_3(9 z2cW>`kaiOk;k&N{b!&Kz!iTa>IyPI z)%;LmwZaPWU^$|P(7rM-2cYoJMSYKxo|>&0Zg2!WCwze0g5E6pM$>d(;bgypy4nl> ze~=d^qweCuSdgvh4}GrzIKgjnvKLv3I33+iA!H=0A2ZB_}w9Y4o2NV zUcDt-6PLc#09^S?Ik~GX==1;wc!c~q0r{7$ib7u;fWp^1{d!JjBwOR;0Vnt=d8Rgz zy|yYi(Qg5q#6;8&z3_`eo~eE0;}(oUKOEp9PULMCbc(Z*8uYcc9Gs{QK;i3)+?TVE zQwA=cgHX}(Fm?^_8BSoDEj#5opIYEewj7jb4}o0$2O*E)0Kpr3u&aln`;j}5zamc{ z`i8G>`}+Pr2hnY35%IqUU5PI7=>pOf`eJ;G?TQT!N%&5)*}Tw#>4V2EQ^th^n`6i~ z@?H%bG7&kG{qJfUCq;!1Uz~&fA&Rq{k@O@d*!+^ath`mgdS}YY%dHWe!D^Qi6kFYZn zJL}s<1Ax6>nd!74p1!Ei;)WB=96lYb9jIOh$nyMu=nITfygSQ#6S0 z28*jS*tpxwr3nfAgr&7K*to^aMTL0Iw)i%n^q)Xfh-ZP~s{%HjM+Li0#b^l(@g4iS zrlZnqHm@Tyf63w}8;yp32#E^u-AjDGwO}bo=M-$rwqRvqIDlHkO2f(R;3RLdU@o{S z&Ooj~?njk7L!*mqlYWH~tg z(`K$RF>V3+39~eupFXzqg#Glak*kr2Tgt)gzsiESF#Dg)Lfyf>em1={3w4Km^N8(&TR2$NmM_pJc&2oW?PEzXVqL6Nq$G&EuDd z;Q&=_K4ZEUh1v>ffM9?&QM>1(%@*p`f()Z0Aym`ffmZ#2lG13Y^yN}LBUM7#i)?9V7-J>tN~(@ z_a0rUR}Tf#9&Myf#BhKj4^I6iS931~ne>H0O|!=qD---g>v$E^4e~hHurtGU3m6wr z187X5Ah-k66Ada61b2XX;x)jKqw$4;;0{nv@-~+B#2uiXq+0{%0uen95Ubb zPY%+ixeC&y)oMLTBCBwind%DhY_a`QBG=zVq2&1prh^dU77(nJ%IV18Ob5Xophi%; z?&22vumyt0uo5P{{s(B8CGzZQ2Qs<{}JA@R34Wt=74C=`|KC4AtC?s61^6 zXA+NQ0Zj(d{wGwPA3qn6c1?U)2<%_bY&KV0FbKgNfJ81v-Ij%%P>`=aarL>mR@mQTV^l)`TaP0|afY8b&C5z3-pLQ~&kZnmELAfU0BQ3^f$K z9`??l@UP0PCWP-W?T*eL;pug0DeDSQoJ8J#E&z)83R6ofq6jVx#JBnrQ8x6t6P zfiKc(we;$e?ueENLBZdOT1LTZUZTDd>lr{)8WVRIpy1abr&92z45IKwOa}>A0vw>^mig%$0?3Y%v-=bZUGxC7!)#Z3u-F%D~(FpFG#Om>Fz+TBhA+%$$T-5 z<^TdG*vQGg(SpGsLodVb=h5>B9*?LW|67rxS^s+UKp|b~6Y*!#qPc?Bv3v(tdaxfl zLlWBoE+-R~Suhx6>V79tWZQH^pISB0qaH#vHlU`0(J-;EBR!g*zckq1fqDwrg0061 zZ)RDd;a}BZbCk5D{qx2?#C8D9+^MQBC}jGLWb#=SECk0MfN16|&1{Qgxc%P3}NEDEoZ*M0K&18BX`VsUdUP}uNV8JnmAo`|oB%;{)EI~Z2r})X$dzi;u~mJ6IJcoR(4u~0SG&GG?r7LGwf*Z~ z!^xy+8vE&c!J#aVpu57pU&jG1v7nO|YJ!g;yU4&=!#RB0PN{Yb^vRj4=KHAA$-`hn zpAG_QiC#05C(FQJop6Gi$-v`rg4*^WB<%;n+BtFPX^>fx*2y$>P}u)iKqeI6voaU92S9LI4S$8j9TaU92S9LI4S$8l=HgbDuz XbTi7@_-UXe00000NkvXXu0mjfvM6l# diff --git a/android/src/main/res/drawable-xxxhdpi/featured_graphics.png b/android/src/main/res/drawable-xxxhdpi/featured_graphics.png deleted file mode 100644 index eca726e3a52673624a9480b54ed3de5eeabfbd03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159469 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≫vBTExfk5UP}`%Y^b5gZjkz?ZF;+1;n4}%X+W{o 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%0DBR9w6O1AlpoPR4U8#@k(Oz1o!qbIlBI}0*- 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 diff --git a/android/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png b/android/src/main/res/drawable-xxxhdpi/ic_profile_image_twidere.png deleted file mode 100644 index 8c6076f05b7d64cfffba107460682af1b2665adb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35500 zcmV*XKv=(tP)v7?2(byoCMhbF7%>ElfrLR~3Ivxdi9y|JG`70?cHizh z_09h8eST;E_da`{v!C~UZ{N%AKF?Zvt^b<#^qgmSpL^;bKk~`z(?;7=nw@Z`!quV% z>$2TA4Fw|Nb)A|58xvJ*8_||lrlEU108fR4ME9B?%H7+ZncM7PYrlt#liP~V^=xNw zl$?k)V!It3PivaAmw8Z0q8?YqY;)TDYS7ecKavYkp>1@Mx()mm-gWYSh>aE- zSN0Z)Q|_n@c=;mk;uj~-pb#sZ zD12Zy8#rtP7M~hXL9O%1SZvyKWO8cDb@Uoh_Q`TICs|`tdg}L72b%?&(DH=0oB=gJ z`<}A~>qBkU(X-E*BX`l!xlum4$glQU_rXh^Yk|WHv>ZK2LDqh~@A*@c>uVmL@}DEu zsN7#202s*;e1^9<$H9|G+oJR>8|)T*!$B=t*(}DH!KS7uqI*a@65-*qw>GDV@ri1q zN~wJ7BCnBHU`tP9y-{20SZ^A6XnUz^*~>^?u-U_+??(qwQqP~7N<{AY*u%Fg?b~3N zR}EEWo7!i#$(Wj>4rr~f`}nM5)j0ag0pN!40*yHGQo*|qf;n{@yz3(`C$qBl*R?Sx zZUYZZj`0Fb(u`Tco7Xk#ljHmvnI(#-OR;kFtgkuY4$TYSwQcR&`^sM%X5IQ;$e(y? z+fo7!J7|e2dqfu4qk6&`3-;3oq__a4lN-h4{QnZPZvnYItSoh@DbKi@z z8~_-~7+&t(nfkqk%_v@eh}HCoa&}l3G7^WJbRBG^G^@g`y8j82rCwWGJg zC>O8cWa{t43W3&jc=WX$+R?n^0W5UwHB0ah&6+;Pob(Y{BfrXHBp0lN#jebY9$G9i z9f?E!qPooaDzuGC($f?@l2FSL_AT9>j0Bh13o~2R{UAm(kH=n!MUUMghzuE& zQhIG0w27`aN~z!Ljl_@-aL_~Q->x!exLu1PyQh?aq+HokpF0HWUh3$sYw=#o$fj{0 zqhsr%q5R6$t`yvGj2TEh&@xuLN36CsN#h-WVsJ@};YgBhH-b-La}4^?76(ee9Ro5k z5L0#8HkK+eBY4WK6CUtFeQ`Oz06I)Nrw%xX$&S40?E99vo-K@Y%3lkNMON0M$R0KE z*!@}?H>6++o-jade)j!Qs+-?2}RU0bEKm1rPp9vtU< zf|Kc5vzD|2u-V!8ny9_W!goJOX}@{NDSt3<4O;AFRMc+1+%jgOn>@BBJmDe+jPrtB?u0Ht`WN037n0)wUq zve&i9U_{|db`hI8AhsJ=XhxSApcjRw#%#0Z*znLbB1`ZqNf|w3Y7QIEtHzA<@zcZl z#xHBZvdn<7krck?8#!1!BHyBi+9>xj|1IxXN1QC#=6wFj`sGR2x)GzTUE?KTUiNCj zrzm`x*V_N`DZpB_Eg=A?x{ti3Bm^EBaVSOMOLhf|f{lm5e}O{-%^^K}))vM^hi4lJ z_fBcb!Y{ONSaq<0R_e_z{zq&*cjwEIX@?_DTbrexhw~Xxa_)BoFKgw_wYOd*_NLx1i5n0-P^kjRsbTQVGQw71f^IIO=u4{wSh9gup zt)X=s>9I*zN0injuGF=9oX*l|f_%bSc**+FdlpH4aBjg&W`{%AHF&QQKC2ilJ-7KYsdHX}E+=6*%5&!e3391LY47t+Dip3Ps+d!v_Ef*Hn&? z^@2DCEOfC^#w+2XT~vbjKxV|66Sj{>D?9N#ChFR#Y&}4P8F-Gi^JJ-JVgcu!D_eUuB;V@RJ3f8U|(w1y+dojLq9r*OuGjFNs8(sapZD=PW)(d`@)8?BG9Y^kGing z=wP>oNP{==35mqR_kEOJIhENsrMB#~qn8-(kpp?rcT9_~an#qsN=ELY)RQQl`X2TX zIjXRD!Y*sq9GHV_f{}8kjvpBAQ;5jjV% z5xKuQ05Bp+sO0d_>}H$8C#b?6r6?D`K!pW@9n@e8yN|^hx|lBE*P4uRuAZEc1&42_ z`)l!zgNL^8$iP4u7xXPkJ*7?_u*O@WlIlVVsC1+sZz=}FwfEt}cu$X&2$Wl~BrS!^p2|HSg zdpe3u&a2JU^Dw$`Mslemx=rTAmE(k+q^?E2Mg2hA!l85jWPz?l;{hNbU})_~{i8JO zpiLj1Koc;wz+@^{ilvKM0z0=li-d3-e2v5d)EAa`?}O=4awAp`Cpmf4bj!x=z+q3{ zspk%#Hxia5_1wjK-+eoe2V{rX)VBDjspk)0W9mNnkH(O_+rTHk>dDjE+gg$=MjrEg zY@_?p+jjA<0Z9yFhq}(?BfR{niNZ&LKh!}9INPYZY45%Q4<2!LS7(qlZiBQ&;}6P& zXpzCAYf<+no;WQmvf4&+mx1e1kF#Z`{MjtjlKO51pD6jO2j9!1B_FZzi1006F7sL> zryj@DyyxP4jq|>6y4Lk01)V$!=QSiLm^Ni?i84QJWR}*^v!>0R#(;!KvZihGZK?al ze_^2@D4+zMRn{#`dmUV22hG!kW!6YSuB8-goB9N`5m`!kNJiq2lWeeu;-^RD-|c%i zn`o0zQUkQwu{Z@bVCg2u$Pe)NoM7;MG+oo{QMh1JNqlL+}u;2-m{Lm8xS>_2M;kO^_#ns+4 zYabqcBp&sQV8O_SIO`1h5^AVxOCyT+WX~Amt<)l2Q}U|q*T^iPo-Ensl)q}Bo-MTJ z4ZoF}W8kxYBxFp#jmXk%j(WCHY0edy{MXUf9**?40Bbj8Pz5H50?W8S3uRtrUHb}O zcipXcUAfUKy<>wm~)Bo=kJeaWxAxc?Hn-y5yR^Abien?#cPQ zz)GoSQSy#+zC@Kh$2ni3);;Q4YqllTl5B#>11$X#TuRj|B_+DoV@{;st4aQqldjQN z$2tHA!a#UnWFeQlQY1tHjeztZQSH;zT^3h;do&=ED~|==4Kgcyb7(?*f_kzqd~3h(+JPfgG@4sXze(3t5erG0B@z(UvPwFE45jYtym7lq&3G_sW6;rUOY z;^Vr0G8Bc+Tq!k2gDf@m;POu$IeHJBHu}W8J6r&TEDh873eM%wzkh;*Yv$Q zD%-9(78H{KT_dxkA2nlo+-P31Cypq5&#%k<5Tm}VZ)boMn(=F{&V@d7ZILDTAHkBl zDE!F$DB5To@&FG_8KYG%^Kj6F9eW(j-y{?3Ln_m3L83PL*KxN zNSZzrXpe%c?Gs*F*MQF6Kx9duV=_dw!07iaO3gZYsiy~y+*HA6lMUC1jn|NC+RlKy zK#jx^rncL&2e10~u;ie6xR7m8VhA%D2b*?INTE6UT9S3nF(*(CGspCJ#o+2;T)rFt zt>g|_cId#oZ+ZM)u5@j{ph*0?Sybt_J>9i@q3L)sOg>^dSXD4 zwz-<0j@H^zvK_s|dT%)ZJTV3V>IQY7MYrWZI%{^BPe8qxk*_U7t zSR}1_(r}eF?H(J04bw)QjFhaQW%lhD)C3g@`?f{U&j7Ef+&FkBGoU9eh+YhrgU8-= zEgs(^cs2@D{;03zJL+eJ;t&JcvyogFY9YrgMJr_1s)uGIMzo&I%Q=x|-Q0H>b&YjN zJOJv1)aMcy=kTJd=20;gty=91Xa(Es5(-Vulf4b!%klvv{1C`C^VJ$cbb-=m{A?8&`sgRI@ zN`p2fK@Xawhdu61hI=E<=&*S9?q`IT?IHGBWV)y)Ejm1qIW%QJ54x9^J`@&9TIg8aHvTye3`GjG8Co8?WH*+ ze%>IZ&(U5TyJzRwkLKq%_`P_(u*7>mDe_zJS>_M@+$E5ULASTz|`d-lF6+NsLn&MN?> zIoLKQd-t)nM=K3{_PKALv2aX)sPpLesA7`MI-|BV__nq!#fEr7?Zsn*s4n>I-L2() z?Jr5QMT>JU@X!{%mI+IoEj#7U{7?)1+9!SY3y*GumNx2-7~M?o84CU(tv0r15K!X`_h|wOok{E0bQfl z5;9tA`VwV*6g%oGtM12TMzVzk z)1s`aF@&Yrm)>?ko(eb#Z=?*{^T=IxlJkfXihRoscGHF2F(2`kd)p>>n9rPje3x`C=sQDFQd1tvMl`` zKy4vUlGXCrPGS!RTx#*&m+ZK0+aK9!>A%+8lyso|qr%qF?7iUopHvZ{wOY+qREn)+qSRKxz8l9pt5F+Tj#oO zN3Q!-C0~rszUMZAMV4zM1L4aG)N_gRZSP4simQE$+L61cF4aU`wa>cGI1k4%2iXQr zRF}Ns`DkONFw`Y}q_v~(Tc!hG&X=^In0sG5WqMHY$8E$Bg?FDx=9+c}*gH~w>c0DS z{*Xmf*v%c{yKajoFVREB5$qv$s*R08PI<%NNmN&Aa4tm#W{Zwu!Fil5zv##xio8XM zA)T+D78aS?z(W&;9u&(FZN`$nDE!DlKvYrIME1JPEuR9ou{*(e&kcHL4>uwwE)yfV z_6rJ4P4mRI?e{a$pmQ-1`&N1I6_+eW?Wke|-@=UA`+{;l`Sh$a4+m|<@5x5zvil;1<7o?@fc8}ae;i;=_k)(=vNF4WdR)%sw z+O?yW9Ge>Jjz@WXfp7WD1%h^pWun{mCAQTY8Ss`JvQ~U_ElMS=aciP9BEqQI1az!eOR5cQ)Gzlwi zyB)gzJO*RF9ih|~cL1KGoZ+yR%Z1BImxuKfLrwm4(U*+Yj#WzOkvCL6(BZE|8F)@! zQTP_VmMMm=R{k~C`{1#6U602G+QZdyuZMawh78tTrJ=30Xfg#Oir1`dbKtOTqqe2% zn%a0`)U=I0M4Cpb_Uq%f=Z$sebG2uK{*XA>!~?($NnSFSW6+YJJCx!}l(>B#pr9pE z_U=zS{Sw*kX~2B}iT8fnDogkC&saF}6Ft;kHg1p49;Fu1_TuViAk9IsW;`~1&pF!> zOhzY;TA-4)Ys;48BWaIXG7GR-!_?DiFav-Y#1+MkmsJA>u>l0KvJHp^GXIx zN=)T|K-(@6sZh?VKhm^tSUk`XJ1~k#TqwE`icML6-x3_7gMHs3Eh_0*T=3ZjFVL)s z*zkac_$8w32|I!-t#Xf8S<>eYoj>wAX#7HWYI6IOl{93WD7`hdUk4iM89B&gvyP6Z zae89^G#DXOID>#pR5AtydG5Pa%?q88YulCJoY_(OJ0xb zh!1KMrRH_L+n%HytrM*B-}ag-1UDKZ`o<``+2xKjezR9{gQn{6xK0MmZc+ijya>zO zFkd};MI^>Lde3StKe0OilE?#;he$f~&@vUba$cxZrrU{^G-)ywdbF`IeDFtYJwE)X zt#H^JlD}+p(Xgf8qAVs+=SwWvlv4Fv|08pr$YY5aQA*a%}ThD{g=n^f5 zcpa*2;16a@V;M!%;4_Ca>OZb!#S?d4A%v*|BO`^{26>{bDAFJopFP0UF_8;>NYjSkv}JP~WFNrf;u(+nkPY@D zHq=?$Am8($4WnyaYwaOdBMo`(`FA}z)}9?x^Tf%~Yed(V_qtvg3Ma?;z+7l7-0hJ|Wu}y`S*V^-z6)~V& zSagSOe%Z-CF`)R{qpGL0QLnDD{0LBp(xx4Sc@d?qk%M3zz3qQby1D}(X^Mu*QID^K z;G>q8gXZ4WNJ-?!QO>t5wxvF>&Tm^KtzzquM0#;$*+`d_u4j$4i%)nV?k_t4{K@$it18LqjiL|Zb~_jnCxJ-DNT>~TG>Lvj(euD+LROHM3k z3*$0sV{y5zoC|5lERV0^jK-1&u%hxd+Jq(gsGZN{XdLn)&r!QJ4lj76f^uX*wSAOQ z<_lVC%bxr7@hef|z2YT6%gh6|^rehxBsFQ>1NzbmlxyMLdTq5Y@j-2Gh;1JppPaTW zO6^J69}?T^^!ig~?n!*k5RE8&SxviFdTC?!q&bUiq}S&BwpG#|QWO5xdDUJr!Zpt0 zYl|{Zdo|&gsL(CnTe{*UfPl#%ZlSdcmphLK$+#p_+TNEeHLOpvFa{Hqjc8^-59jQsUespOMYjyxooE%Ux6sKTnx%ASZ2M?~6qtCQuowbK? zEO^n;L2X6w?BQFuS_T%X+Td$!s}B~sKYFDX>Si3+I9pjT{=_Uj?)R|YN9HVu1>Liy z9^rCLeJlL>_#^9Et@F#{k2N%Vq5icdWA?a_n&jg+G3p%btH5&0Q00=70Xp?s7{}Mx z-crPJv-!Ht7J#9$JZhD+x2@^>x|YC&!Ek3du-bv z^)YrHeNDZ#Ll1SYZ75b1-0j=z|L{3h2(PIM+3ot6cR>dzsyhHm73HeEfswq0*&gEK zS=YJbz7HnP6wr(s4m)h$TCG zpxdK|ZvH)gEj@XW8}%lvq@_f1YFcZORqK)U32k9&nMWtj8gocL+yT@ndk9>4d5(7; zY@%)U2Bp8ePDg6==-YYLQQCdG0}wzi-7RII z6V1($lGfIe;Xyih$xotbf$L)6=pWTL`I9H~5Px3hq4vj(->zR-$g>(5L6Zgq@e_9J z(d#A3mL-v)TUd0eOHK3aH1)P0iQ{>0SvU^cHh!&@?VM`cq>?gU)J2^Ytl3>hZ^Cwk z)d5iB%W}$IUQSpkDP4|h@h-#mC{zQ-;6ab#2b((oqvYxJA8LOR@u?+KWkFVC1kH0O z#GwtF>}}fvI8j~LZ&_(mXl~go!j5bdk3}40Tk(Vi2hDg%K&-VHH^#AMN9vKsIy!ZY zMQfwt0pO)LUz>P9;lS3s>=-6|xGl2dP!@#Kqxz!`*~1@_FE%&LJ|v)VU_q7RKqV!u zbM}5|-|h*2s0|t5am1dQYKo5@^;nAI{hp2FVB7e%R5sXc`F0)a)@6Hq9bGYx+m?S9 zfC3Z*9;zS&b3Jlo;6=d;qgMBmbo@~Rrly79i#^H1_pkvZ*zhC1p0E5Ln(y< zWYUmR+paCeCt7?UZ_)YOUuqkv2mkmRi34_v#;*d)S<4|Z5AHHKrVrf;b{ySm@~=NX zz4s+wHvQ6z{_25lJyR-^%;gQ2%o4N#X^Rv?4gb(-`j;>IYtyek_}8cN7apI!`d@tS zbnhn~P0+zd-}KvQkweac5IGwp+f?{NZRAlljJEnYBlY$h^+ES6j+$pbueySi0dYh} zTAhF0@A;^s_w(#|Xv>#30o@_RAfxXtS~Axkhxb7duZzW={H9y}>({PLzwyAAPQQ5D zmmCnTR~7Q$$`g=h5*Uu+qa$9I`47EcIQJ#f|8w7$#LfS;Kl6{LhaUa?>FeJ8J>d{| zWV-&mUQleeIz^sUl9k7oB=^Z;ozZqYw6fPVYENWr@PnNG7Cm(M$&FgKe7lYu%iQRp zb&E6>(u~Gpo(`?a{kJnI^BV0f2LNTtkYFwwb$A>ci@MzG$s;=T^m_k#c+sx^o)3q> zuif*-(=VL;;+@c{hKitWVzRvi+^osBrYQUWxieop{pwwRdAj|HPfTC;bN|Ei@=raY zqNe+w`BXRr(w)H8A)v8fAraokC(U4vw_s*wtpTx3v8(B}RnlU^+8VI|)0z%+Ez0N< zvxTW;!s?Q3JIA&+?AqEg3UgG`64mYzhuYex!==js;HD$RIR2f`{K)itwTT_8NYg5QZ(Y{*q+H+n{hp^w(sFwUfUe9wp1U};66uq zk2{KIOv)VHlk@4oknd93kLL5_eBYwfkY6c1Uaxro9M9#pHH%g#1_7B*I5PKE$b#|m zPWsIM%K5iXKl}U_xXH{jrJ~MPPy&d;SCH+l`-aaJ@4ou>>6gyEeY)eBC#P@zjek15 z>d8N=R(P_x|Cz_bA@IG^J>flp`VdfB8aL8&zO0S-f)-`Nr^0bUKf0cX!nHmJWCXsZD|`-+SEOkSS|Oy`#q#4_p!y_&;y{JJo@Z4 zQFtZk3mR-Mc|H4|I&(T60%yV@aOT{(a2T8k*SYDJmqS3qr(t7?eK=>&1Y$PmLwgBy z<%+?TKtFx?^TRX$+rs95X8P9O`sdRtpQ@kv-5IuePvG%z2;@5frNkIP^U}pfwnqY$ zhLMm={k_Jb!|NX2^~59QNIcoAPA>JC8u5FLJcnb=4wWN$<{0v_)*vNT#&2`&`Rm&d zhrHB3>2_iVfQqXd6W7w!SoKG7!s^*yhd|u)XVL+1_O{!nGq;_a&fa$06z>GWvu;}E zjpXFSn}Q#bfZIl1F?4CVa?|FI&-|A!PT%^wKRLbf!bg&F6{~Hp_XL(hAbj*m%jU#u z+k-z2^{R+A;t#Cs4{2IAT(Zlx_PrdpdhE*x?Fx|Aw6tCK*bmZ-)P(<|j{iD?wd7a_ zfES_S=!^PQ>%2cY80*yMLm+&ChC|?VxC@Afz`5I|)8YE%+uuIDJ8ZyaBf&Zu^@p3L zM$M-vN%ZKO|L^?X|2n&QaGPHOZbhWp3{Ic>HM+66KOh zB@6|VBv~#_I(^j-S!Q9!&CI6c27phG}+0jonmNqBO{N=jKUJ=crd z{`kzV+kQ14s3ehazOsFi1u-J%946ig=#9bfav&W7W&Fi{j!~J<(ms(lHSD0sleYip z*~k|AzD3q-t)Gl4IpT?Om5A*g_Cks0F=gJb;Ly6{nI#96tk>`lO}X{ZN0%>m03;>q z6T!NzWylrUc~3?1lWe$y`rwGyA&@>A44($fhrsD@02GIS-Wa49iXKjq$j~x_)ib}| z@bf`%KE^EVi!=x!>afr)4*`IHk{6^6^CC>$kr&yxeT~`bV=s*R*|I^W>F_C84HrP8 zR1G!NE{hGdCId;y9GW!hxfc7RZ9l4p)}CskvsAIvh?#mP{}>MdCFzMO3`vVeC*GUn zH0P0>H1z>jXbZpa^&t>9e|;yw_XG}3nhbmyq$+><^5-q@_Ak%;?%!8-H0e}8QG^m! zGVh13-WdFb@N(eZkAEo4kKPkVhHehuFc0{4151T%R9{NWD&4+8&}f(#D(GssDi6ew zSiJWuXtUe5kw)3CE2t$SF{uOEOr|09SY*_I9a|I+0Fu+-J1_5b|JV@^aNq+h-upg4 zwafYth~E*=O95UEgsgpLmO6fMJLPh36pXnG;8G;d|Ixj!oN6 zQmT< zjkNA&G2390$I`AFzVbsX@X!j@?P}v|sFvaCBC|pJZS!gS&SE zt*KBpSzf>9kKgd)crRud8@G4fszU50rcSIt0JAdQ3H)sMve3u$WufFis6!fF2|r>> zHo_9`5c_Bs3>mtSn8F+y1Ha@ed-6q>$2EJ06Bb^0=d*pA({dQ8k9_2tJlh^mPI^-g z03SSfQFsQMzRGp}(DFG1^hZPS(}DP2zW&*?SK%SPg@SPfWLYV0LnW4RnsZ4CsnEQr~-7bDw><(>+)R{bCR9DEcE#F4dFe3dp}6`iJ2F@F8d=P|NDR(n1D3`Bc{)$r}xQ1{3)f&4<9%a0pzRu7yM3YB&U* zzZ$M^Fz7Y(9nW5vzV&zhS^AS+IqN=sZDio8?jSj+leK268y_3_@pfve4}lut=q-LA z!=|~Xy_=T37@{J5S8ZW;b2)`wu-xLV%1LzZiv$vn0|D5>T^fmAM`2#nGaV%?oTe6=^_9bNa!ONx~LG- z$A0O1<_MvHn6-$Tr(wxb5!gr{46H)1{}Mj$*VKVKFvX;mH&q8}LQN#&4#( zpw184Y~|nb?J^Dl{km|x6VQ)|gii+E@ZsC1E7$Iue)vuQ{`Aoo-RCBOC*np1Qq?fv z(VLVsfb?fNx{_9blD#(nyWaY>(_{C(V!Ha=v(x2^&y26mDwU&jN#8fgs2?9H=lATv zLVc7=TWLV0siG$eU)r>Ls#6zrwo#?jqlFo_<-Mq*+sXX!W%5{SKgosbypUt@Ss$f20H8Mkdu8@YVqJu!rVXOmsW| z(hrMpl;3~p>v___L?$XI?VB^P#T{hk#xV6jpOTlhsqlssPWv_GK0DbW&4;l)2#d z)TGV-{h$3+(_?qPEF5fSqJ<7Q9U#{(U!JZE90H^bEy50THq#!k2Xbh9;TZ{hWQul; zkVM);;^~mnr3R($BwasxWbxi#qi>w#mN@_z0H#JIOGY0(^f*J}K8WG*TE6eUbl3Ea zkGy%B&b)Mb=j%BH8q0))-Jybb(4{g5HKMYbS}SaUw<~z++?~@q-uRW%pM~50_z$Oq zI~pBm>EBb(KcaFve$;v8*=H9|HABjtKC&9y>bKHw;d(ME^Y}fi%RR@&3g07d^#EYdRTcx!fH*zrvRi}iGR2Gk?qHZv7O@h%|G7J- zZ~E|Crt4>4GJXH+zhe5x`FqRMlysfA`m5&A#A8$ewVFtPl~q98F8kCo|2y9B6=Czg zGQNyC6aKN3bj|;Y()|3U8snWn=MeDB@D44~=yZ0|@7N(GKK)UO9kgv%P7GK^wA8je zxRmK(x?I=ej~c7z?~e0q9t?vQTW=Y`dpb< zYn}?cKH1>S&S)L{D*9Eu9OxVZ!l_x7NwD)s>Vg&NZ`ct-5b@sMFk4hLzV0U=(%p$# z{BAK!lh?b2cOA3mvKN{^(F1@XWr$S_JUY?3aAXvZ4gKTL+82394qyA%Gh@6$_WCat zVPFn{_@2PS;Se}Ke>xzH923-RatsLpRseq*5%2xQ0X76dF~8>5ZU23*Oq)M^lA&Kh z(&j&-zkSdr9P@htwYA>~)H?NvO0+Gi%|PnHjcyKN5Rd*=EZ2c2fBKF|yfmP)7W`V1 zF*V0DdA&8J)juRg%l@QfM>+sJ`4-LLH__pT+NpJ433Au zV{eUb3@]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