diff --git a/app/src/main/java/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt b/app/src/main/java/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt index 5bd299112..b3a79322d 100644 --- a/app/src/main/java/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt +++ b/app/src/main/java/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt @@ -528,7 +528,7 @@ private fun StatusActions( text = action.displayItem.iconText, color = statusActionItemColor(item = action.displayItem), withTextMinWidth = index != items.lastIndex, - ) { + ) { closeMenu -> action.actions.forEach { subActions -> if (subActions is StatusAction.Item) { val color = statusActionItemColor(subActions) @@ -550,6 +550,7 @@ private fun StatusActions( ) }, onClick = { + closeMenu.invoke() if (subActions is StatusAction.Item.Clickable) { subActions.onClicked.invoke( ClickContext( diff --git a/app/src/main/java/dev/dimension/flare/ui/component/status/StatusRetweetHeaderComponent.kt b/app/src/main/java/dev/dimension/flare/ui/component/status/StatusRetweetHeaderComponent.kt index 94487ef96..4ef20bf76 100644 --- a/app/src/main/java/dev/dimension/flare/ui/component/status/StatusRetweetHeaderComponent.kt +++ b/app/src/main/java/dev/dimension/flare/ui/component/status/StatusRetweetHeaderComponent.kt @@ -36,7 +36,7 @@ internal fun StatusRetweetHeaderComponent( contentDescription = null, modifier = Modifier - .size(16.dp), + .size(12.dp), ) if (user != null) { Spacer(modifier = Modifier.width(8.dp)) diff --git a/app/src/main/java/dev/dimension/flare/ui/component/status/UiTimelineComponent.kt b/app/src/main/java/dev/dimension/flare/ui/component/status/UiTimelineComponent.kt index 435f1c131..e199dc9c1 100644 --- a/app/src/main/java/dev/dimension/flare/ui/component/status/UiTimelineComponent.kt +++ b/app/src/main/java/dev/dimension/flare/ui/component/status/UiTimelineComponent.kt @@ -20,6 +20,7 @@ import compose.icons.fontawesomeicons.solid.At import compose.icons.fontawesomeicons.solid.CircleInfo import compose.icons.fontawesomeicons.solid.Heart import compose.icons.fontawesomeicons.solid.Pen +import compose.icons.fontawesomeicons.solid.QuoteLeft import compose.icons.fontawesomeicons.solid.Reply import compose.icons.fontawesomeicons.solid.Retweet import compose.icons.fontawesomeicons.solid.SquarePollHorizontal @@ -28,6 +29,7 @@ import dev.dimension.flare.R import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickContext import dev.dimension.flare.ui.model.UiTimeline +import dev.dimension.flare.ui.model.mapper.MisskeyAchievement @Composable internal fun UiTimelineComponent( @@ -144,6 +146,7 @@ private fun TopMessageComponent( UiTimeline.TopMessage.Icon.Edit -> FontAwesomeIcons.Solid.Pen UiTimeline.TopMessage.Icon.Info -> FontAwesomeIcons.Solid.CircleInfo UiTimeline.TopMessage.Icon.Reply -> FontAwesomeIcons.Solid.Reply + UiTimeline.TopMessage.Icon.Quote -> FontAwesomeIcons.Solid.QuoteLeft } val text: String = when (val type = data.type) { @@ -230,53 +233,55 @@ private fun TopMessageComponent( is UiTimeline.TopMessage.MessageType.Misskey -> when (type) { - UiTimeline.TopMessage.MessageType.Misskey.AchievementEarned -> + is UiTimeline.TopMessage.MessageType.Misskey.AchievementEarned -> stringResource( id = R.string.misskey_notification_item_achievement_earned, + type.achievement?.titleResId?.let { stringResource(it) } ?: "", + type.achievement?.descriptionResId?.let { stringResource(it) } ?: "", ) - UiTimeline.TopMessage.MessageType.Misskey.App -> + is UiTimeline.TopMessage.MessageType.Misskey.App -> stringResource(id = R.string.misskey_notification_item_app) - UiTimeline.TopMessage.MessageType.Misskey.Follow -> + is UiTimeline.TopMessage.MessageType.Misskey.Follow -> stringResource(id = R.string.misskey_notification_item_followed_you) - UiTimeline.TopMessage.MessageType.Misskey.FollowRequestAccepted -> + is UiTimeline.TopMessage.MessageType.Misskey.FollowRequestAccepted -> stringResource( id = R.string.misskey_notification_item_follow_request_accepted, ) - UiTimeline.TopMessage.MessageType.Misskey.Mention -> + is UiTimeline.TopMessage.MessageType.Misskey.Mention -> stringResource( id = R.string.misskey_notification_item_mentioned_you, ) - UiTimeline.TopMessage.MessageType.Misskey.PollEnded -> + is UiTimeline.TopMessage.MessageType.Misskey.PollEnded -> stringResource( id = R.string.misskey_notification_item_poll_ended, ) - UiTimeline.TopMessage.MessageType.Misskey.Quote -> + is UiTimeline.TopMessage.MessageType.Misskey.Quote -> stringResource( id = R.string.misskey_notification_item_quoted_your_status, ) - UiTimeline.TopMessage.MessageType.Misskey.Reaction -> + is UiTimeline.TopMessage.MessageType.Misskey.Reaction -> stringResource( id = R.string.misskey_notification_item_reacted_to_your_status, ) - UiTimeline.TopMessage.MessageType.Misskey.ReceiveFollowRequest -> + is UiTimeline.TopMessage.MessageType.Misskey.ReceiveFollowRequest -> stringResource( id = R.string.misskey_notification_item_requested_follow, ) - UiTimeline.TopMessage.MessageType.Misskey.Renote -> + is UiTimeline.TopMessage.MessageType.Misskey.Renote -> stringResource( id = R.string.misskey_notification_item_reposted_your_status, ) - UiTimeline.TopMessage.MessageType.Misskey.Reply -> + is UiTimeline.TopMessage.MessageType.Misskey.Reply -> stringResource( id = R.string.misskey_notification_item_replied_to_you, ) @@ -317,3 +322,170 @@ private fun TopMessageComponent( }, ) } + +val MisskeyAchievement.titleResId: Int + get() = + when (this) { + MisskeyAchievement.NOTES1 -> R.string.misskey_achievement_notes1_title + MisskeyAchievement.NOTES10 -> R.string.misskey_achievement_notes10_title + MisskeyAchievement.NOTES100 -> R.string.misskey_achievement_notes100_title + MisskeyAchievement.NOTES500 -> R.string.misskey_achievement_notes500_title + MisskeyAchievement.NOTES1000 -> R.string.misskey_achievement_notes1000_title + MisskeyAchievement.NOTES5000 -> R.string.misskey_achievement_notes5000_title + MisskeyAchievement.NOTES10000 -> R.string.misskey_achievement_notes10000_title + MisskeyAchievement.NOTES20000 -> R.string.misskey_achievement_notes20000_title + MisskeyAchievement.NOTES30000 -> R.string.misskey_achievement_notes30000_title + MisskeyAchievement.NOTES40000 -> R.string.misskey_achievement_notes40000_title + MisskeyAchievement.NOTES50000 -> R.string.misskey_achievement_notes50000_title + MisskeyAchievement.NOTES60000 -> R.string.misskey_achievement_notes60000_title + MisskeyAchievement.NOTES70000 -> R.string.misskey_achievement_notes70000_title + MisskeyAchievement.NOTES80000 -> R.string.misskey_achievement_notes80000_title + MisskeyAchievement.NOTES90000 -> R.string.misskey_achievement_notes90000_title + MisskeyAchievement.NOTES100000 -> R.string.misskey_achievement_notes100000_title + MisskeyAchievement.LOGIN3 -> R.string.misskey_achievement_login3_title + MisskeyAchievement.LOGIN7 -> R.string.misskey_achievement_login7_title + MisskeyAchievement.LOGIN15 -> R.string.misskey_achievement_login15_title + MisskeyAchievement.LOGIN30 -> R.string.misskey_achievement_login30_title + MisskeyAchievement.LOGIN60 -> R.string.misskey_achievement_login60_title + MisskeyAchievement.LOGIN100 -> R.string.misskey_achievement_login100_title + MisskeyAchievement.LOGIN200 -> R.string.misskey_achievement_login200_title + MisskeyAchievement.LOGIN300 -> R.string.misskey_achievement_login300_title + MisskeyAchievement.LOGIN400 -> R.string.misskey_achievement_login400_title + MisskeyAchievement.LOGIN500 -> R.string.misskey_achievement_login500_title + MisskeyAchievement.LOGIN600 -> R.string.misskey_achievement_login600_title + MisskeyAchievement.LOGIN700 -> R.string.misskey_achievement_login700_title + MisskeyAchievement.LOGIN800 -> R.string.misskey_achievement_login800_title + MisskeyAchievement.LOGIN900 -> R.string.misskey_achievement_login900_title + MisskeyAchievement.LOGIN1000 -> R.string.misskey_achievement_login1000_title + MisskeyAchievement.NOTE_CLIPPED1 -> R.string.misskey_achievement_note_clipped1_title + MisskeyAchievement.NOTE_FAVORITED1 -> R.string.misskey_achievement_note_favorited1_title + MisskeyAchievement.MY_NOTE_FAVORITED1 -> R.string.misskey_achievement_my_note_favorited1_title + MisskeyAchievement.PROFILE_FILLED -> R.string.misskey_achievement_profile_filled_title + MisskeyAchievement.MARKED_AS_CAT -> R.string.misskey_achievement_marked_as_cat_title + MisskeyAchievement.FOLLOWING1 -> R.string.misskey_achievement_following1_title + MisskeyAchievement.FOLLOWING10 -> R.string.misskey_achievement_following10_title + MisskeyAchievement.FOLLOWING50 -> R.string.misskey_achievement_following50_title + MisskeyAchievement.FOLLOWING100 -> R.string.misskey_achievement_following100_title + MisskeyAchievement.FOLLOWING300 -> R.string.misskey_achievement_following300_title + MisskeyAchievement.FOLLOWERS1 -> R.string.misskey_achievement_followers1_title + MisskeyAchievement.FOLLOWERS10 -> R.string.misskey_achievement_followers10_title + MisskeyAchievement.FOLLOWERS50 -> R.string.misskey_achievement_followers50_title + MisskeyAchievement.FOLLOWERS100 -> R.string.misskey_achievement_followers100_title + MisskeyAchievement.FOLLOWERS300 -> R.string.misskey_achievement_followers300_title + MisskeyAchievement.FOLLOWERS500 -> R.string.misskey_achievement_followers500_title + MisskeyAchievement.FOLLOWERS1000 -> R.string.misskey_achievement_followers1000_title + MisskeyAchievement.COLLECT_ACHIEVEMENTS30 -> R.string.misskey_achievement_collect_achievements30_title + MisskeyAchievement.VIEW_ACHIEVEMENTS3MIN -> R.string.misskey_achievement_view_achievements3min_title + MisskeyAchievement.I_LOVE_MISSKEY -> R.string.misskey_achievement_i_love_misskey_title + MisskeyAchievement.FOUND_TREASURE -> R.string.misskey_achievement_found_treasure_title + MisskeyAchievement.CLIENT30MIN -> R.string.misskey_achievement_client30min_title + MisskeyAchievement.CLIENT60MIN -> R.string.misskey_achievement_client60min_title + MisskeyAchievement.NOTE_DELETED_WITHIN1MIN -> R.string.misskey_achievement_note_deleted_within1min_title + MisskeyAchievement.POSTED_AT_LATE_NIGHT -> R.string.misskey_achievement_posted_at_late_night_title + MisskeyAchievement.POSTED_AT_0MIN0SEC -> R.string.misskey_achievement_posted_at_0min0sec_title + MisskeyAchievement.SELF_QUOTE -> R.string.misskey_achievement_self_quote_title + MisskeyAchievement.HTL20NPM -> R.string.misskey_achievement_htl20npm_title + MisskeyAchievement.VIEW_INSTANCE_CHART -> R.string.misskey_achievement_view_instance_chart_title + MisskeyAchievement.OUTPUT_HELLO_WORLD_ON_SCRATCHPAD -> R.string.misskey_achievement_output_hello_world_on_scratchpad_title + MisskeyAchievement.OPEN3WINDOWS -> R.string.misskey_achievement_open3windows_title + MisskeyAchievement.DRIVE_FOLDER_CIRCULAR_REFERENCE -> R.string.misskey_achievement_drive_folder_circular_reference_title + MisskeyAchievement.REACT_WITHOUT_READ -> R.string.misskey_achievement_react_without_read_title + MisskeyAchievement.CLICKED_CLICK_HERE -> R.string.misskey_achievement_clicked_click_here_title + MisskeyAchievement.JUST_PLAIN_LUCKY -> R.string.misskey_achievement_just_plain_lucky_title + MisskeyAchievement.SET_NAME_TO_SYUILO -> R.string.misskey_achievement_set_name_to_syuilo_title + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED1 -> R.string.misskey_achievement_passed_since_account_created1_title + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED2 -> R.string.misskey_achievement_passed_since_account_created2_title + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED3 -> R.string.misskey_achievement_passed_since_account_created3_title + MisskeyAchievement.LOGGED_IN_ON_BIRTHDAY -> R.string.misskey_achievement_logged_in_on_birthday_title + MisskeyAchievement.LOGGED_IN_ON_NEW_YEARS_DAY -> R.string.misskey_achievement_logged_in_on_new_years_day_title + MisskeyAchievement.COOKIE_CLICKED -> R.string.misskey_achievement_cookie_clicked_title + MisskeyAchievement.BRAIN_DIVER -> R.string.misskey_achievement_brain_diver_title + MisskeyAchievement.SMASH_TEST_NOTIFICATION_BUTTON -> R.string.misskey_achievement_smash_test_notification_button_title + MisskeyAchievement.TUTORIAL_COMPLETED -> R.string.misskey_achievement_tutorial_completed_title + MisskeyAchievement.BUBBLE_GAME_EXPLODING_HEAD -> R.string.misskey_achievement_bubble_game_exploding_head_title + MisskeyAchievement.BUBBLE_GAME_DOUBLE_EXPLODING_HEAD -> R.string.misskey_achievement_bubble_game_double_exploding_head_title + } + +val MisskeyAchievement.descriptionResId: Int + get() = + when (this) { + MisskeyAchievement.NOTES1 -> R.string.misskey_achievement_notes1_description + MisskeyAchievement.NOTES10 -> R.string.misskey_achievement_notes10_description + MisskeyAchievement.NOTES100 -> R.string.misskey_achievement_notes100_description + MisskeyAchievement.NOTES500 -> R.string.misskey_achievement_notes500_description + MisskeyAchievement.NOTES1000 -> R.string.misskey_achievement_notes1000_description + MisskeyAchievement.NOTES5000 -> R.string.misskey_achievement_notes5000_description + MisskeyAchievement.NOTES10000 -> R.string.misskey_achievement_notes10000_description + MisskeyAchievement.NOTES20000 -> R.string.misskey_achievement_notes20000_description + MisskeyAchievement.NOTES30000 -> R.string.misskey_achievement_notes30000_description + MisskeyAchievement.NOTES40000 -> R.string.misskey_achievement_notes40000_description + MisskeyAchievement.NOTES50000 -> R.string.misskey_achievement_notes50000_description + MisskeyAchievement.NOTES60000 -> R.string.misskey_achievement_notes60000_description + MisskeyAchievement.NOTES70000 -> R.string.misskey_achievement_notes70000_description + MisskeyAchievement.NOTES80000 -> R.string.misskey_achievement_notes80000_description + MisskeyAchievement.NOTES90000 -> R.string.misskey_achievement_notes90000_description + MisskeyAchievement.NOTES100000 -> R.string.misskey_achievement_notes100000_description + MisskeyAchievement.LOGIN3 -> R.string.misskey_achievement_login3_description + MisskeyAchievement.LOGIN7 -> R.string.misskey_achievement_login7_description + MisskeyAchievement.LOGIN15 -> R.string.misskey_achievement_login15_description + MisskeyAchievement.LOGIN30 -> R.string.misskey_achievement_login30_description + MisskeyAchievement.LOGIN60 -> R.string.misskey_achievement_login60_description + MisskeyAchievement.LOGIN100 -> R.string.misskey_achievement_login100_description + MisskeyAchievement.LOGIN200 -> R.string.misskey_achievement_login200_description + MisskeyAchievement.LOGIN300 -> R.string.misskey_achievement_login300_description + MisskeyAchievement.LOGIN400 -> R.string.misskey_achievement_login400_description + MisskeyAchievement.LOGIN500 -> R.string.misskey_achievement_login500_description + MisskeyAchievement.LOGIN600 -> R.string.misskey_achievement_login600_description + MisskeyAchievement.LOGIN700 -> R.string.misskey_achievement_login700_description + MisskeyAchievement.LOGIN800 -> R.string.misskey_achievement_login800_description + MisskeyAchievement.LOGIN900 -> R.string.misskey_achievement_login900_description + MisskeyAchievement.LOGIN1000 -> R.string.misskey_achievement_login1000_description + MisskeyAchievement.NOTE_CLIPPED1 -> R.string.misskey_achievement_note_clipped1_description + MisskeyAchievement.NOTE_FAVORITED1 -> R.string.misskey_achievement_note_favorited1_description + MisskeyAchievement.MY_NOTE_FAVORITED1 -> R.string.misskey_achievement_my_note_favorited1_description + MisskeyAchievement.PROFILE_FILLED -> R.string.misskey_achievement_profile_filled_description + MisskeyAchievement.MARKED_AS_CAT -> R.string.misskey_achievement_marked_as_cat_description + MisskeyAchievement.FOLLOWING1 -> R.string.misskey_achievement_following1_description + MisskeyAchievement.FOLLOWING10 -> R.string.misskey_achievement_following10_description + MisskeyAchievement.FOLLOWING50 -> R.string.misskey_achievement_following50_description + MisskeyAchievement.FOLLOWING100 -> R.string.misskey_achievement_following100_description + MisskeyAchievement.FOLLOWING300 -> R.string.misskey_achievement_following300_description + MisskeyAchievement.FOLLOWERS1 -> R.string.misskey_achievement_followers1_description + MisskeyAchievement.FOLLOWERS10 -> R.string.misskey_achievement_followers10_description + MisskeyAchievement.FOLLOWERS50 -> R.string.misskey_achievement_followers50_description + MisskeyAchievement.FOLLOWERS100 -> R.string.misskey_achievement_followers100_description + MisskeyAchievement.FOLLOWERS300 -> R.string.misskey_achievement_followers300_description + MisskeyAchievement.FOLLOWERS500 -> R.string.misskey_achievement_followers500_description + MisskeyAchievement.FOLLOWERS1000 -> R.string.misskey_achievement_followers1000_description + MisskeyAchievement.COLLECT_ACHIEVEMENTS30 -> R.string.misskey_achievement_collect_achievements30_description + MisskeyAchievement.VIEW_ACHIEVEMENTS3MIN -> R.string.misskey_achievement_view_achievements3min_description + MisskeyAchievement.I_LOVE_MISSKEY -> R.string.misskey_achievement_i_love_misskey_description + MisskeyAchievement.FOUND_TREASURE -> R.string.misskey_achievement_found_treasure_description + MisskeyAchievement.CLIENT30MIN -> R.string.misskey_achievement_client30min_description + MisskeyAchievement.CLIENT60MIN -> R.string.misskey_achievement_client60min_description + MisskeyAchievement.NOTE_DELETED_WITHIN1MIN -> R.string.misskey_achievement_note_deleted_within1min_description + MisskeyAchievement.POSTED_AT_LATE_NIGHT -> R.string.misskey_achievement_posted_at_late_night_description + MisskeyAchievement.POSTED_AT_0MIN0SEC -> R.string.misskey_achievement_posted_at_0min0sec_description + MisskeyAchievement.SELF_QUOTE -> R.string.misskey_achievement_self_quote_description + MisskeyAchievement.HTL20NPM -> R.string.misskey_achievement_htl20npm_description + MisskeyAchievement.VIEW_INSTANCE_CHART -> R.string.misskey_achievement_view_instance_chart_description + MisskeyAchievement.OUTPUT_HELLO_WORLD_ON_SCRATCHPAD -> R.string.misskey_achievement_output_hello_world_on_scratchpad_description + MisskeyAchievement.OPEN3WINDOWS -> R.string.misskey_achievement_open3windows_description + MisskeyAchievement.DRIVE_FOLDER_CIRCULAR_REFERENCE -> R.string.misskey_achievement_drive_folder_circular_reference_description + MisskeyAchievement.REACT_WITHOUT_READ -> R.string.misskey_achievement_react_without_read_description + MisskeyAchievement.CLICKED_CLICK_HERE -> R.string.misskey_achievement_clicked_click_here_description + MisskeyAchievement.JUST_PLAIN_LUCKY -> R.string.misskey_achievement_just_plain_lucky_description + MisskeyAchievement.SET_NAME_TO_SYUILO -> R.string.misskey_achievement_set_name_to_syuilo_description + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED1 -> R.string.misskey_achievement_passed_since_account_created1_description + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED2 -> R.string.misskey_achievement_passed_since_account_created2_description + MisskeyAchievement.PASSED_SINCE_ACCOUNT_CREATED3 -> R.string.misskey_achievement_passed_since_account_created3_description + MisskeyAchievement.LOGGED_IN_ON_BIRTHDAY -> R.string.misskey_achievement_logged_in_on_birthday_description + MisskeyAchievement.LOGGED_IN_ON_NEW_YEARS_DAY -> R.string.misskey_achievement_logged_in_on_new_years_day_description + MisskeyAchievement.COOKIE_CLICKED -> R.string.misskey_achievement_cookie_clicked_description + MisskeyAchievement.BRAIN_DIVER -> R.string.misskey_achievement_brain_diver_description + MisskeyAchievement.SMASH_TEST_NOTIFICATION_BUTTON -> R.string.misskey_achievement_smash_test_notification_button_description + MisskeyAchievement.TUTORIAL_COMPLETED -> R.string.misskey_achievement_tutorial_completed_description + MisskeyAchievement.BUBBLE_GAME_EXPLODING_HEAD -> R.string.misskey_achievement_bubble_game_exploding_head_description + MisskeyAchievement.BUBBLE_GAME_DOUBLE_EXPLODING_HEAD -> + R.string.misskey_achievement_bubble_game_double_exploding_head_description + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8b7d0331..488b1722c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,8 +55,8 @@ Settings followed you - favourited your toot - reblogged your toot + favourited + reblogged A poll you were participating in has ended mentioned you request to follow you @@ -67,30 +67,30 @@ Show less followed you - favourited your status - reblogged your status + favourited + reblogged A poll you were participating in has ended mentioned you boosted a status replied to you - quoted your status + quoted Starterpack Joined UnKnown mentioned you replied to you - reposted your status - quoted your status + reposted + quoted reacted to your status A poll you were participating in has ended accepted your follow request - You\'ve earned an achievement + You\'ve earned an achievement: %1$s\n%2$s App followed you requested to follow you reposted a status - retweeted your tweet + retweeted mentions you Public @@ -413,4 +413,256 @@ Media Tab settings + + + just setting up my msky + Post your first note + Have a good time with Misskey! + + Some notes + Post 10 notes + + A lot of notes + Post 100 notes + + Covered in notes + Post 500 notes + + A mountain of notes + Post 1,000 notes + + Overflowing notes + Post 5,000 notes + + Supernote + Post 10,000 notes + + Need... more... notes... + Post 20,000 notes + + Notes notes notes! + Post 30,000 notes + + Note factory + Post 40,000 notes + + Planet of notes + Post 50,000 notes + + Note quasar + Post 60,000 notes + + Note black hole + Post 70,000 notes + + Note galaxy + Post 80,000 notes + + Note universe + Post 90,000 notes + + ALL YOUR NOTE ARE BELONG TO US + Post 100,000 notes + You sure have a lot to say. + + Beginner I + Log in for a total of 3 days + Starting today, just call me Misskist + + Beginner II + Log in for a total of 7 days + Feel like you\'ve gotten the hang of things yet? + + Beginner III + Log in for a total of 15 days + + Misskist I + Log in for a total of 30 days + + Misskist II + Log in for a total of 60 days + + Misskist III + Log in for a total of 100 days + Violent Misskist + + Regular I + Log in for a total of 200 days + + Regular II + Log in for a total of 300 days + + Regular III + Log in for a total of 400 days + + Expert I + Log in for a total of 500 days + My friends, it has often been said that I like notes + + Expert II + Log in for a total of 600 days + + Expert III + Log in for a total of 700 days + + Master of Notes I + Log in for a total of 800 days + + Master of Notes II + Log in for a total of 900 days + + Master of Notes III + Log in for a total of 1,000 days + Thank you for using Misskey! + + Must... clip... + Clip your first note + + Stargazer + Favorite your first note + + Seeking Stars + Have somebody else favorite one of your notes + + Well-prepared + Set up your profile + + I Am a Cat + Mark your account as a cat + I\'ll give you a name later. + + Following your first user + Follow a user + + Keep up... keep up... + Follow 10 users + + Lots of friends + Follow 50 accounts + + 100 Friends + Follow 100 accounts + + Friend overload + Follow 300 accounts + + First follower + Gain 1 follower + + Follow me! + Gain 10 followers + + Coming in crowds + Gain 50 followers + + Popular + Gain 100 followers + + Please form a single line + Gain 300 followers + + Radio Tower + Gain 500 followers + + Influencer + Gain 1,000 followers + + Achievement Collector + Earn 30 achievements + + Likes Achievements + Look at your list of achievements for at least 3 minutes + + I Love Misskey + Post \"I ❤ #Misskey\" + Misskey\'s development team greatly appreciates your support! + + Treasure Hunt + You\'ve found the hidden treasure + + Short break + Keep Misskey opened for at least 30 minutes + + No \"Miss\" in Misskey + Keep Misskey opened for at least 60 minutes + + Nevermind + Delete a note within a minute of posting it + + Nocturnal + Post a note late at night + It\'s about time to go to bed. + + Speaking Clock + Post a note at 00:00 + Click Click Click Claaang + + Self-Reference + Quote your own note + + Flowing Timeline + Have the speed of your home timeline exceed 20 npm (notes per minute) + + Analyst + View your instance\'s charts + + Hello, world! + Output \"hello world\" in the Scratchpad + + Multi-Window + Have at least 3 windows open at the same time + + Circular Reference + Attempt to create a recursively nested folder in Drive + + Did you really read that? + React on a note that\'s over 100 characters long within 3 seconds of it being posted + + Click here + You\'ve clicked here + + Just Plain Lucky + Has a chance to be obtained with a probability of 0.005\% every 10 seconds + + God Complex + Set your name to \"syuilo\" + + One Year Anniversary + One year has passed since your account was created + + Two Year Anniversary + Two years have passed since your account was created + + Three Year Anniversary + Three years have passed since your account was created + + Happy Birthday + Log in on your birthday + + Happy New Year! + Logged in on the first day of the year + To another great year on this instance + + A game in which you click cookies + Clicked the cookie + Wait, are you on the correct website? + + Brain Diver + Post the link to Brain Diver + Misskey-Misskey La-Tu-Ma + + Test overflow + Trigger the notification test repeatedly within an extremely short time + + Misskey Elementary Course Diploma + Tutorial completed + + 🤯 + The biggest object in the bubble game + + Double🤯 + Two of the biggest objects in the bubble game at the same time + You can fill a lunch box like this 🤯 🤯 a bit. + + \ No newline at end of file diff --git a/iosApp/iosApp/UI/Component/Status/StatusTimelineBuilder.swift b/iosApp/iosApp/UI/Component/Status/StatusTimelineBuilder.swift index 69fd70f56..aa6ba4cd3 100644 --- a/iosApp/iosApp/UI/Component/Status/StatusTimelineBuilder.swift +++ b/iosApp/iosApp/UI/Component/Status/StatusTimelineBuilder.swift @@ -57,6 +57,7 @@ struct StatusItemView: View { case .edit: "pencil" case .info: "app" case .reply: "arrowshape.turn.up.left" + case .quote: "quote.bubble.fill" } let text = switch onEnum(of: topMessage.type) { case .bluesky(let data): diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/mapper/Bluesky.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/mapper/Bluesky.kt index 478b9dd1a..e9682a98d 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/mapper/Bluesky.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/mapper/Bluesky.kt @@ -213,25 +213,18 @@ internal fun List.toDbPagingTimeline( } } val references = - when (val data = it.reason) { - is FeedViewPostReasonUnion.ReasonRepost -> - listOfNotNull( - if (reply != null) { - ReferenceType.Reply to reply - } else { - null - }, - ReferenceType.Retweet to status, - ).toMap() - else -> - listOfNotNull( - if (reply != null) { - ReferenceType.Reply to reply - } else { - null - }, - ).toMap() - } + listOfNotNull( + if (reply != null) { + ReferenceType.Reply to reply + } else { + null + }, + if (it.reason is FeedViewPostReasonUnion.ReasonRepost) { + ReferenceType.Retweet to it.post.toDbStatusWithUser(accountKey) + } else { + null + }, + ).toMap() createDbPagingTimelineWithStatus( accountKey = accountKey, pagingKey = pagingKey, diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/mastodon/MastodonDataSource.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/mastodon/MastodonDataSource.kt index 63b1cc304..13940cea3 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/mastodon/MastodonDataSource.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/mastodon/MastodonDataSource.kt @@ -549,7 +549,7 @@ class MastodonDataSource( accountKey = account.accountKey, cacheDatabase = database, update = { - it.copy(data = result) + result.reblog?.let { StatusContent.Mastodon(it) } ?: it }, ) } diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/misskey/MisskeyDataSource.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/misskey/MisskeyDataSource.kt index 77d93995d..210772e37 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/misskey/MisskeyDataSource.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/misskey/MisskeyDataSource.kt @@ -27,7 +27,6 @@ import dev.dimension.flare.data.datasource.microblog.timelinePager import dev.dimension.flare.data.network.misskey.api.model.AdminAccountsDeleteRequest import dev.dimension.flare.data.network.misskey.api.model.IPinRequest import dev.dimension.flare.data.network.misskey.api.model.MuteCreateRequest -import dev.dimension.flare.data.network.misskey.api.model.NotesChildrenRequest import dev.dimension.flare.data.network.misskey.api.model.NotesCreateRequest import dev.dimension.flare.data.network.misskey.api.model.NotesCreateRequestPoll import dev.dimension.flare.data.network.misskey.api.model.NotesReactionsCreateRequest @@ -380,11 +379,40 @@ class MisskeyDataSource( override fun renote(statusKey: MicroBlogKey) { coroutineScope.launch { - service.notesRenotes( - NotesChildrenRequest( - noteId = statusKey.id, - ), + updateStatusUseCase( + statusKey = statusKey, + accountKey = account.accountKey, + cacheDatabase = database, + update = { + it.copy( + data = + it.data.copy( + renoteCount = it.data.renoteCount + 1, + ), + ) + }, ) + runCatching { + service.notesCreate( + NotesCreateRequest( + renoteId = statusKey.id, + ), + ) + }.onFailure { + updateStatusUseCase( + statusKey = statusKey, + accountKey = account.accountKey, + cacheDatabase = database, + update = { + it.copy( + data = + it.data.copy( + renoteCount = it.data.renoteCount - 1, + ), + ) + }, + ) + } } } diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiTimeline.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiTimeline.kt index 54c72c06b..f6c552775 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiTimeline.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiTimeline.kt @@ -5,11 +5,11 @@ import dev.dimension.flare.data.datasource.microblog.StatusAction import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.model.PlatformType import dev.dimension.flare.ui.humanizer.humanize +import dev.dimension.flare.ui.model.mapper.MisskeyAchievement import dev.dimension.flare.ui.render.UiDateTime import dev.dimension.flare.ui.render.UiRichText import kotlinx.collections.immutable.ImmutableList -// TODO: Handling item click event internally @Immutable data class UiTimeline internal constructor( val topMessage: TopMessage?, @@ -169,6 +169,7 @@ data class UiTimeline internal constructor( Edit, Info, Reply, + Quote, } sealed interface MessageType { @@ -191,27 +192,50 @@ data class UiTimeline internal constructor( } sealed interface Misskey : MessageType { - data object Follow : Misskey + data class Follow( + val id: String, + ) : Misskey - data object Mention : Misskey + data class Mention( + val id: String, + ) : Misskey - data object Reply : Misskey + data class Reply( + val id: String, + ) : Misskey - data object Renote : Misskey + data class Renote( + val id: String, + ) : Misskey - data object Quote : Misskey + data class Quote( + val id: String, + ) : Misskey - data object Reaction : Misskey + data class Reaction( + val id: String, + ) : Misskey - data object PollEnded : Misskey + data class PollEnded( + val id: String, + ) : Misskey - data object ReceiveFollowRequest : Misskey + data class ReceiveFollowRequest( + val id: String, + ) : Misskey - data object FollowRequestAccepted : Misskey + data class FollowRequestAccepted( + val id: String, + ) : Misskey - data object AchievementEarned : Misskey + data class AchievementEarned( + val id: String, + val achievement: MisskeyAchievement?, + ) : Misskey - data object App : Misskey + data class App( + val id: String, + ) : Misskey } sealed interface Bluesky : MessageType { diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt index 5fdb4b676..8543526c0 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt @@ -17,11 +17,13 @@ import app.bsky.richtext.FacetFeatureUnion import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.TextNode import dev.dimension.flare.common.AppDeepLink +import dev.dimension.flare.data.database.cache.model.StatusContent import dev.dimension.flare.data.datasource.bluesky.bskyJson import dev.dimension.flare.data.datasource.microblog.StatusAction import dev.dimension.flare.data.datasource.microblog.StatusEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.model.PlatformType +import dev.dimension.flare.model.ReferenceType import dev.dimension.flare.ui.model.UiCard import dev.dimension.flare.ui.model.UiList import dev.dimension.flare.ui.model.UiMedia @@ -138,10 +140,11 @@ private fun parseBluesky( internal fun FeedViewPostReasonUnion.render( accountKey: MicroBlogKey, - data: PostView, event: StatusEvent.Bluesky, -): UiTimeline = - UiTimeline( + references: Map, +): UiTimeline { + val data = (references[ReferenceType.Retweet] as? StatusContent.Bluesky)?.data + return UiTimeline( topMessage = (this as? FeedViewPostReasonUnion.ReasonRepost)?.value?.by?.let { val user = it.render(accountKey) @@ -159,9 +162,10 @@ internal fun FeedViewPostReasonUnion.render( }, ) }, - content = data.renderStatus(accountKey, event), + content = data?.renderStatus(accountKey, event), platformType = PlatformType.Bluesky, ) +} internal fun ListNotificationsNotification.render(accountKey: MicroBlogKey): UiTimeline { val user = author.render(accountKey) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt index 040e2b601..ea48e4a80 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt @@ -5,6 +5,7 @@ import com.fleeksoft.ksoup.nodes.Node import dev.dimension.flare.common.AppDeepLink import dev.dimension.flare.data.database.cache.model.DbEmoji import dev.dimension.flare.data.database.cache.model.EmojiContent +import dev.dimension.flare.data.database.cache.model.StatusContent import dev.dimension.flare.data.datasource.guest.GuestDataSource import dev.dimension.flare.data.datasource.microblog.StatusAction import dev.dimension.flare.data.datasource.microblog.StatusEvent @@ -19,6 +20,7 @@ import dev.dimension.flare.data.network.mastodon.api.model.Status import dev.dimension.flare.data.network.mastodon.api.model.Visibility import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.model.PlatformType +import dev.dimension.flare.model.ReferenceType import dev.dimension.flare.ui.model.UiCard import dev.dimension.flare.ui.model.UiEmoji import dev.dimension.flare.ui.model.UiMedia @@ -38,10 +40,14 @@ import kotlinx.datetime.Instant internal fun Notification.render( accountKey: MicroBlogKey, event: StatusEvent.Mastodon, + references: Map, ): UiTimeline { requireNotNull(account) { "account is null" } val user = account.render(accountKey) - val status = status?.renderStatus(accountKey, event) + val status = + (references[ReferenceType.Notification] as? StatusContent.Mastodon) + ?.data + ?.renderStatus(accountKey, event) val topMessageType = when (type) { NotificationTypes.Follow -> UiTimeline.TopMessage.MessageType.Mastodon.Follow @@ -97,6 +103,7 @@ internal fun Notification.render( internal fun Status.render( accountKey: MicroBlogKey, event: StatusEvent.Mastodon, + references: Map = mapOf(), ): UiTimeline { requireNotNull(account) { "account is null" } val user = account.render(accountKey) @@ -119,7 +126,7 @@ internal fun Status.render( ) } val currentStatus = this.renderStatus(accountKey, event) - val actualStatus = reblog ?: this + val actualStatus = (references[ReferenceType.Retweet] as? StatusContent.Mastodon)?.data ?: this return UiTimeline( topMessage = topMessage, content = diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt index d286e8d35..1037c964f 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.ui.model.mapper import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.TextNode import dev.dimension.flare.common.AppDeepLink +import dev.dimension.flare.data.database.cache.model.StatusContent import dev.dimension.flare.data.datasource.microblog.StatusAction import dev.dimension.flare.data.datasource.microblog.StatusEvent import dev.dimension.flare.data.network.misskey.api.model.DriveFile @@ -15,6 +16,7 @@ import dev.dimension.flare.data.network.misskey.api.model.UserLite import dev.dimension.flare.data.network.misskey.api.model.Visibility import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.model.PlatformType +import dev.dimension.flare.model.ReferenceType import dev.dimension.flare.ui.model.UiEmoji import dev.dimension.flare.ui.model.UiMedia import dev.dimension.flare.ui.model.UiPoll @@ -50,27 +52,72 @@ import moe.tlaster.mfm.parser.tree.UrlNode internal fun Notification.render( accountKey: MicroBlogKey, event: StatusEvent.Misskey, + references: Map, ): UiTimeline { val user = user?.render(accountKey) - val status = note?.renderStatus(accountKey, event) + val status = + (references[ReferenceType.Notification] as? StatusContent.Misskey) + ?.data + ?.renderStatus(accountKey, event) val topMessageType = when (this.type) { - NotificationType.Follow -> UiTimeline.TopMessage.MessageType.Misskey.Follow - NotificationType.Mention -> UiTimeline.TopMessage.MessageType.Misskey.Mention - NotificationType.Reply -> UiTimeline.TopMessage.MessageType.Misskey.Reply - NotificationType.Renote -> UiTimeline.TopMessage.MessageType.Misskey.Renote - NotificationType.Quote -> UiTimeline.TopMessage.MessageType.Misskey.Quote - NotificationType.Reaction -> UiTimeline.TopMessage.MessageType.Misskey.Reaction - NotificationType.PollEnded -> UiTimeline.TopMessage.MessageType.Misskey.PollEnded - NotificationType.ReceiveFollowRequest -> UiTimeline.TopMessage.MessageType.Misskey.ReceiveFollowRequest - NotificationType.FollowRequestAccepted -> UiTimeline.TopMessage.MessageType.Misskey.FollowRequestAccepted - NotificationType.AchievementEarned -> UiTimeline.TopMessage.MessageType.Misskey.AchievementEarned - NotificationType.App -> UiTimeline.TopMessage.MessageType.Misskey.App + NotificationType.Follow -> + UiTimeline.TopMessage.MessageType.Misskey + .Follow(id = id) + NotificationType.Mention -> + UiTimeline.TopMessage.MessageType.Misskey + .Mention(id = id) + NotificationType.Reply -> + UiTimeline.TopMessage.MessageType.Misskey + .Reply(id = id) + NotificationType.Renote -> + UiTimeline.TopMessage.MessageType.Misskey + .Renote(id = id) + NotificationType.Quote -> + UiTimeline.TopMessage.MessageType.Misskey + .Quote(id = id) + NotificationType.Reaction -> + UiTimeline.TopMessage.MessageType.Misskey + .Reaction(id = id) + NotificationType.PollEnded -> + UiTimeline.TopMessage.MessageType.Misskey + .PollEnded(id = id) + NotificationType.ReceiveFollowRequest -> + UiTimeline.TopMessage.MessageType.Misskey + .ReceiveFollowRequest(id = id) + NotificationType.FollowRequestAccepted -> + UiTimeline.TopMessage.MessageType.Misskey + .FollowRequestAccepted(id = id) + NotificationType.AchievementEarned -> + UiTimeline.TopMessage.MessageType.Misskey.AchievementEarned( + id = id, + achievement = + achievement?.let { + MisskeyAchievement.fromString(it) + }, + ) + NotificationType.App -> + UiTimeline.TopMessage.MessageType.Misskey + .App(id = id) + } + val icon = + when (this.type) { + NotificationType.Follow -> UiTimeline.TopMessage.Icon.Follow + NotificationType.Mention -> UiTimeline.TopMessage.Icon.Mention + NotificationType.Reply -> UiTimeline.TopMessage.Icon.Reply + NotificationType.Renote -> UiTimeline.TopMessage.Icon.Retweet + NotificationType.Quote -> UiTimeline.TopMessage.Icon.Quote + NotificationType.Reaction -> UiTimeline.TopMessage.Icon.Favourite + NotificationType.PollEnded -> UiTimeline.TopMessage.Icon.Poll + NotificationType.ReceiveFollowRequest -> UiTimeline.TopMessage.Icon.Follow + NotificationType.FollowRequestAccepted -> UiTimeline.TopMessage.Icon.Follow + NotificationType.AchievementEarned -> UiTimeline.TopMessage.Icon.Info + NotificationType.App -> UiTimeline.TopMessage.Icon.Info } val topMessage = UiTimeline.TopMessage( user = user, - icon = UiTimeline.TopMessage.Icon.Retweet, + icon = icon, type = topMessageType, onClicked = { if (user != null) { @@ -99,11 +146,101 @@ internal fun Notification.render( ) } +enum class MisskeyAchievement( + val id: String, +) { + NOTES1("notes1"), + NOTES10("notes10"), + NOTES100("notes100"), + NOTES500("notes500"), + NOTES1000("notes1000"), + NOTES5000("notes5000"), + NOTES10000("notes10000"), + NOTES20000("notes20000"), + NOTES30000("notes30000"), + NOTES40000("notes40000"), + NOTES50000("notes50000"), + NOTES60000("notes60000"), + NOTES70000("notes70000"), + NOTES80000("notes80000"), + NOTES90000("notes90000"), + NOTES100000("notes100000"), + LOGIN3("login3"), + LOGIN7("login7"), + LOGIN15("login15"), + LOGIN30("login30"), + LOGIN60("login60"), + LOGIN100("login100"), + LOGIN200("login200"), + LOGIN300("login300"), + LOGIN400("login400"), + LOGIN500("login500"), + LOGIN600("login600"), + LOGIN700("login700"), + LOGIN800("login800"), + LOGIN900("login900"), + LOGIN1000("login1000"), + NOTE_CLIPPED1("noteClipped1"), + NOTE_FAVORITED1("noteFavorited1"), + MY_NOTE_FAVORITED1("myNoteFavorited1"), + PROFILE_FILLED("profileFilled"), + MARKED_AS_CAT("markedAsCat"), + FOLLOWING1("following1"), + FOLLOWING10("following10"), + FOLLOWING50("following50"), + FOLLOWING100("following100"), + FOLLOWING300("following300"), + FOLLOWERS1("followers1"), + FOLLOWERS10("followers10"), + FOLLOWERS50("followers50"), + FOLLOWERS100("followers100"), + FOLLOWERS300("followers300"), + FOLLOWERS500("followers500"), + FOLLOWERS1000("followers1000"), + COLLECT_ACHIEVEMENTS30("collectAchievements30"), + VIEW_ACHIEVEMENTS3MIN("viewAchievements3min"), + I_LOVE_MISSKEY("iLoveMisskey"), + FOUND_TREASURE("foundTreasure"), + CLIENT30MIN("client30min"), + CLIENT60MIN("client60min"), + NOTE_DELETED_WITHIN1MIN("noteDeletedWithin1min"), + POSTED_AT_LATE_NIGHT("postedAtLateNight"), + POSTED_AT_0MIN0SEC("postedAt0min0sec"), + SELF_QUOTE("selfQuote"), + HTL20NPM("htl20npm"), + VIEW_INSTANCE_CHART("viewInstanceChart"), + OUTPUT_HELLO_WORLD_ON_SCRATCHPAD("outputHelloWorldOnScratchpad"), + OPEN3WINDOWS("open3windows"), + DRIVE_FOLDER_CIRCULAR_REFERENCE("driveFolderCircularReference"), + REACT_WITHOUT_READ("reactWithoutRead"), + CLICKED_CLICK_HERE("clickedClickHere"), + JUST_PLAIN_LUCKY("justPlainLucky"), + SET_NAME_TO_SYUILO("setNameToSyuilo"), + PASSED_SINCE_ACCOUNT_CREATED1("passedSinceAccountCreated1"), + PASSED_SINCE_ACCOUNT_CREATED2("passedSinceAccountCreated2"), + PASSED_SINCE_ACCOUNT_CREATED3("passedSinceAccountCreated3"), + LOGGED_IN_ON_BIRTHDAY("loggedInOnBirthday"), + LOGGED_IN_ON_NEW_YEARS_DAY("loggedInOnNewYearsDay"), + COOKIE_CLICKED("cookieClicked"), + BRAIN_DIVER("brainDiver"), + SMASH_TEST_NOTIFICATION_BUTTON("smashTestNotificationButton"), + TUTORIAL_COMPLETED("tutorialCompleted"), + BUBBLE_GAME_EXPLODING_HEAD("bubbleGameExplodingHead"), + BUBBLE_GAME_DOUBLE_EXPLODING_HEAD("bubbleGameDoubleExplodingHead"), + ; + + companion object { + fun fromString(id: String): MisskeyAchievement? = entries.find { it.id == id } + } +} + internal fun Note.render( accountKey: MicroBlogKey, event: StatusEvent.Misskey, + references: Map, ): UiTimeline { val user = user.render(accountKey) + val renote = (references[ReferenceType.Retweet] as? StatusContent.Misskey)?.data val topMessage = if (renote == null || !text.isNullOrEmpty()) { null diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Render.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Render.kt index c37a113c8..39af265a3 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Render.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Render.kt @@ -6,47 +6,60 @@ import dev.dimension.flare.data.database.cache.model.StatusContent import dev.dimension.flare.data.database.cache.model.UserContent import dev.dimension.flare.data.datasource.microblog.StatusEvent import dev.dimension.flare.model.MicroBlogKey +import dev.dimension.flare.model.ReferenceType import dev.dimension.flare.ui.model.UiTimeline internal fun DbPagingTimelineView.render(event: StatusEvent): UiTimeline = status_content.render( timeline.accountKey, event, + references = + listOfNotNull( + retweet_status_content?.let { ReferenceType.Retweet to it }, + quote_status_content?.let { ReferenceType.Quote to it }, + reply_status_content?.let { ReferenceType.Reply to it }, + notification_status_content?.let { ReferenceType.Notification to it }, + ).toMap(), ) internal fun StatusContent.render( accountKey: MicroBlogKey, event: StatusEvent, + references: Map = emptyMap(), ) = when (this) { is StatusContent.Mastodon -> data.render( accountKey = accountKey, event = event as StatusEvent.Mastodon, + references = references, ) is StatusContent.MastodonNotification -> data.render( accountKey = accountKey, event = event as StatusEvent.Mastodon, + references = references, ) is StatusContent.Misskey -> data.render( accountKey = accountKey, event = event as StatusEvent.Misskey, + references = references, ) is StatusContent.MisskeyNotification -> data.render( accountKey = accountKey, event = event as StatusEvent.Misskey, + references = references, ) is StatusContent.BlueskyReason -> reason.render( accountKey = accountKey, - data = data, event = event as StatusEvent.Bluesky, + references = references, ) is StatusContent.Bluesky -> @@ -64,6 +77,7 @@ internal fun StatusContent.render( data.render( accountKey = accountKey, event = event as StatusEvent.XQT, + references = references, ) is StatusContent.VVO -> diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt index aa7e1d087..e392a19a2 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt @@ -4,6 +4,7 @@ import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.TextNode import de.cketti.codepoints.deluxe.codePointSequence import dev.dimension.flare.common.AppDeepLink +import dev.dimension.flare.data.database.cache.model.StatusContent import dev.dimension.flare.data.datasource.microblog.StatusAction import dev.dimension.flare.data.datasource.microblog.StatusEvent import dev.dimension.flare.data.network.xqt.model.GetProfileSpotlightsQuery200Response @@ -11,8 +12,6 @@ import dev.dimension.flare.data.network.xqt.model.Media import dev.dimension.flare.data.network.xqt.model.Tweet import dev.dimension.flare.data.network.xqt.model.TweetCardLegacy import dev.dimension.flare.data.network.xqt.model.TweetCardLegacyBindingValueData -import dev.dimension.flare.data.network.xqt.model.TweetTombstone -import dev.dimension.flare.data.network.xqt.model.TweetWithVisibilityResults import dev.dimension.flare.data.network.xqt.model.User import dev.dimension.flare.data.network.xqt.model.UserResultCore import dev.dimension.flare.data.network.xqt.model.UserResults @@ -20,6 +19,7 @@ import dev.dimension.flare.data.network.xqt.model.UserUnavailable import dev.dimension.flare.data.network.xqt.model.legacy.TopLevel import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.model.PlatformType +import dev.dimension.flare.model.ReferenceType import dev.dimension.flare.model.xqtHost import dev.dimension.flare.ui.model.UiCard import dev.dimension.flare.ui.model.UiMedia @@ -110,7 +110,7 @@ internal fun TopLevel.renderNotifications( ), ), legacy = it, - ).renderStatus(accountKey, event) + ).renderStatus(accountKey, event, emptyMap()) } val itemContent = when { @@ -178,7 +178,7 @@ internal fun TopLevel.renderNotifications( ), ), legacy = tweet, - ).renderStatus(accountKey, event) + ).renderStatus(accountKey, event, emptyMap()) UiTimeline( topMessage = UiTimeline.TopMessage( @@ -202,20 +202,15 @@ internal fun TopLevel.renderNotifications( internal fun Tweet.render( accountKey: MicroBlogKey, event: StatusEvent.XQT, + references: Map = emptyMap(), ): UiTimeline { val retweet = - legacy - ?.retweetedStatusResult - ?.result - ?.let { - when (it) { - is Tweet -> it - is TweetTombstone -> null - is TweetWithVisibilityResults -> it.tweet - } - } - val actualTweet = retweet ?: this - val user = renderStatus(accountKey, event).user + (references[ReferenceType.Retweet] as? StatusContent.XQT) + ?.data + ?.renderStatus(accountKey = accountKey, event = event, references = emptyMap()) + val currentTweet = renderStatus(accountKey, event, references) + val actualTweet = retweet ?: currentTweet + val user = currentTweet.user val topMessage = if (retweet != null && user != null) { UiTimeline.TopMessage( @@ -229,10 +224,9 @@ internal fun Tweet.render( } else { null } - val currentTweet = this.renderStatus(accountKey, event) return UiTimeline( content = - actualTweet.renderStatus(accountKey = accountKey, event = event).copy( + actualTweet.copy( onClicked = { launcher.launch( AppDeepLink.StatusDetail( @@ -251,17 +245,12 @@ internal fun Tweet.render( internal fun Tweet.renderStatus( accountKey: MicroBlogKey, event: StatusEvent.XQT, + references: Map, ): UiTimeline.ItemContent.Status { val quote = - quotedStatusResult - ?.result - ?.let { - when (it) { - is Tweet -> it - is TweetTombstone -> null - is TweetWithVisibilityResults -> it.tweet - } - }?.renderStatus(accountKey = accountKey, event = event) + (references[ReferenceType.Quote] as? StatusContent.XQT) + ?.data + ?.renderStatus(accountKey = accountKey, event = event, references = emptyMap()) val user = core ?.userResults