Skip to content

Commit 6038145

Browse files
authored
Merge pull request #1253 from DimensionDev/feature/ios_rework
Initial rework for iOS
2 parents e2a70ce + 71a8b47 commit 6038145

File tree

744 files changed

+14305
-309133
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

744 files changed

+14305
-309133
lines changed

.github/workflows/ios.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ on:
1919

2020
jobs:
2121
build:
22-
runs-on: [macos-15]
22+
runs-on: [macos-26]
2323

2424
steps:
2525
- name: Checkout
@@ -51,4 +51,4 @@ jobs:
5151
if [ $scheme = default ]; then scheme=$(cat default); fi
5252
if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
5353
file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
54-
xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=iPhone 16"
54+
xcodebuild build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=iPhone 17" -skipPackagePluginValidation -skipMacroValidation

.gitignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,4 @@ shared/shared.podspec
2929
.lh
3030
iosApp/K_D/
3131
*.sh
32-
.*
33-
/package_source
34-
/example_app
35-
36-
/iosApp/iosApp/todo
32+
.env

app/src/main/java/dev/dimension/flare/ui/route/Route.kt

Lines changed: 104 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import androidx.navigation3.runtime.NavKey
44
import dev.dimension.flare.data.model.TimelineTabItem
55
import dev.dimension.flare.model.AccountType
66
import dev.dimension.flare.model.MicroBlogKey
7-
import io.ktor.http.Url
87
import kotlinx.serialization.Serializable
98

109
@Serializable
@@ -407,182 +406,110 @@ internal sealed interface Route : NavKey {
407406

408407
companion object {
409408
public fun parse(url: String): Route? {
410-
val data = Url(url)
411-
return when (data.host) {
412-
"Callback" ->
413-
when (data.segments.getOrNull(0)) {
414-
"SignIn" ->
415-
when (data.segments.getOrNull(1)) {
416-
// "Mastodon" -> Route.Callback.Mastodon
417-
// "Misskey" -> Route.Callback.Misskey
418-
else -> null
419-
}
420-
421-
else -> null
422-
}
423-
424-
"Search" -> {
425-
val accountKey = data.parameters["accountKey"]?.let { MicroBlogKey.valueOf(it) }
426-
val keyword = data.segments.getOrNull(0) ?: return null
427-
val accountType = accountKey?.let { AccountType.Specific(it) } ?: AccountType.Guest
428-
Route.Search(accountType, keyword)
429-
}
430-
431-
"Profile" -> {
432-
val accountKey = data.parameters["accountKey"]?.let { MicroBlogKey.valueOf(it) }
433-
val userKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
434-
val accountType = accountKey?.let { AccountType.Specific(it) } ?: AccountType.Guest
435-
Route.Profile.User(accountType, userKey)
436-
}
437-
438-
"ProfileWithNameAndHost" -> {
439-
val accountKey = data.parameters["accountKey"]?.let { MicroBlogKey.valueOf(it) }
440-
val userName = data.segments.getOrNull(0) ?: return null
441-
val host = data.segments.getOrNull(1) ?: return null
442-
val accountType = accountKey?.let { AccountType.Specific(it) } ?: AccountType.Guest
443-
Route.Profile.UserNameWithHost(accountType, userName, host)
444-
}
445-
446-
"StatusDetail" -> {
447-
val accountKey = data.parameters["accountKey"]?.let { MicroBlogKey.valueOf(it) }
448-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
449-
val accountType = accountKey?.let { AccountType.Specific(it) } ?: AccountType.Guest
450-
Route.Status.Detail(statusKey, accountType)
451-
}
452-
453-
"Compose" ->
454-
when (data.segments.getOrNull(0)) {
455-
"Reply" -> {
456-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
457-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
458-
Route.Compose.Reply(accountKey, statusKey)
459-
}
460-
461-
"Quote" -> {
462-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
463-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
464-
Route.Compose.Quote(accountKey, statusKey)
465-
}
466-
467-
"New" -> {
468-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
469-
Route.Compose.New(AccountType.Specific(accountKey))
470-
}
471-
472-
else -> null
473-
}
474-
475-
"RawImage" -> {
476-
val rawImage = data.segments.getOrNull(0) ?: return null
477-
Route.Media.Image(rawImage, previewUrl = null)
478-
}
479-
480-
"VVO" ->
481-
when (data.segments.getOrNull(0)) {
482-
"StatusDetail" -> {
483-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
484-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
485-
Route.Status.VVOStatus(statusKey, AccountType.Specific(accountKey))
486-
}
487-
488-
"CommentDetail" -> {
489-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
490-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
491-
Route.Status.VVOComment(statusKey, AccountType.Specific(accountKey))
492-
}
493-
494-
"ReplyToComment" -> {
495-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
496-
val replyTo = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
497-
val rootId = data.segments.getOrNull(3) ?: return null
498-
Route.Compose.VVOReplyComment(accountKey, replyTo, rootId)
499-
}
500-
501-
else -> null
502-
}
503-
504-
"DeleteStatus" -> {
505-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
506-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
507-
Route.Status.DeleteConfirm(statusKey, AccountType.Specific(accountKey))
508-
}
509-
510-
"AddReaction" -> {
511-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
512-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
513-
Route.Status.AddReaction(statusKey, AccountType.Specific(accountKey))
514-
}
515-
516-
"Bluesky" ->
517-
when (data.segments.getOrNull(0)) {
518-
"ReportStatus" -> {
519-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
520-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
521-
Route.Status.BlueskyReport(statusKey, AccountType.Specific(accountKey))
522-
}
523-
524-
else -> null
525-
}
526-
527-
"Mastodon" ->
528-
when (data.segments.getOrNull(0)) {
529-
"ReportStatus" -> {
530-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
531-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
532-
val userKey = MicroBlogKey.valueOf(data.segments.getOrNull(3) ?: return null)
533-
Route.Status.MastodonReport(
534-
statusKey = statusKey,
535-
userKey = userKey,
536-
accountType = AccountType.Specific(accountKey),
537-
)
538-
}
539-
540-
else -> null
541-
}
542-
543-
"Misskey" ->
544-
when (data.segments.getOrNull(0)) {
545-
"ReportStatus" -> {
546-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(1) ?: return null)
547-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(2) ?: return null)
548-
val userKey = MicroBlogKey.valueOf(data.segments.getOrNull(3) ?: return null)
549-
Route.Status.MisskeyReport(
550-
accountType = AccountType.Specific(accountKey),
551-
statusKey = statusKey,
552-
userKey = userKey,
553-
)
554-
}
555-
556-
else -> null
557-
}
558-
559-
"StatusMedia" -> {
560-
val accountKey = data.parameters["accountKey"]?.let { MicroBlogKey.valueOf(it) }
561-
val statusKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
562-
val index = data.segments.getOrNull(1)?.toIntOrNull() ?: return null
563-
val accountType = accountKey?.let { AccountType.Specific(it) } ?: AccountType.Guest
564-
val preview = data.parameters["preview"]
565-
Route.Media.StatusMedia(accountType = accountType, statusKey = statusKey, index = index, preview = preview)
566-
}
567-
568-
"Podcast" -> {
569-
val accountKey = MicroBlogKey.valueOf(data.segments.getOrNull(0) ?: return null)
570-
val id = data.segments.getOrNull(1) ?: return null
571-
val accountType = accountKey.let { AccountType.Specific(it) }
572-
Route.Media.Podcast(accountType = accountType, id = id)
573-
}
574-
575-
"AltText" -> {
576-
val text = data.segments.getOrNull(0) ?: return null
577-
Route.Status.AltText(text)
578-
}
579-
580-
"RSS" -> {
581-
val feedUrl = data.segments.getOrNull(0) ?: return null
582-
Route.Rss.Detail(feedUrl)
583-
}
584-
585-
else -> null
409+
val deeplinkRoute = DeeplinkRoute.parse(url) ?: return null
410+
return when (deeplinkRoute) {
411+
is DeeplinkRoute.Callback -> null
412+
is DeeplinkRoute.Compose.New ->
413+
Route.Compose.New(accountType = deeplinkRoute.accountType)
414+
is DeeplinkRoute.Compose.Quote ->
415+
Route.Compose.Quote(
416+
accountKey = deeplinkRoute.accountKey,
417+
statusKey = deeplinkRoute.statusKey,
418+
)
419+
is DeeplinkRoute.Compose.Reply ->
420+
Route.Compose.Reply(
421+
accountKey = deeplinkRoute.accountKey,
422+
statusKey = deeplinkRoute.statusKey,
423+
)
424+
is DeeplinkRoute.Compose.VVOReplyComment ->
425+
Route.Compose.VVOReplyComment(
426+
accountKey = deeplinkRoute.accountKey,
427+
replyTo = deeplinkRoute.replyTo,
428+
rootId = deeplinkRoute.rootId,
429+
)
430+
is DeeplinkRoute.Media.Image ->
431+
Route.Media.Image(
432+
uri = deeplinkRoute.uri,
433+
previewUrl = deeplinkRoute.previewUrl,
434+
)
435+
is DeeplinkRoute.Media.Podcast ->
436+
Route.Media.Podcast(
437+
accountType = deeplinkRoute.accountType,
438+
id = deeplinkRoute.id,
439+
)
440+
is DeeplinkRoute.Media.StatusMedia ->
441+
Route.Media.StatusMedia(
442+
statusKey = deeplinkRoute.statusKey,
443+
accountType = deeplinkRoute.accountType,
444+
index = deeplinkRoute.index,
445+
preview = deeplinkRoute.preview,
446+
)
447+
is DeeplinkRoute.Profile.User ->
448+
Route.Profile.User(
449+
accountType = deeplinkRoute.accountType,
450+
userKey = deeplinkRoute.userKey,
451+
)
452+
is DeeplinkRoute.Profile.UserNameWithHost ->
453+
Route.Profile.UserNameWithHost(
454+
accountType = deeplinkRoute.accountType,
455+
name = deeplinkRoute.userName,
456+
host = deeplinkRoute.host,
457+
)
458+
is DeeplinkRoute.Rss.Detail ->
459+
Route.Rss.Detail(
460+
url = deeplinkRoute.url,
461+
)
462+
is DeeplinkRoute.Search ->
463+
Route.Search(
464+
accountType = deeplinkRoute.accountType,
465+
query = deeplinkRoute.query,
466+
)
467+
is DeeplinkRoute.Status.AddReaction ->
468+
Route.Status.AddReaction(
469+
statusKey = deeplinkRoute.statusKey,
470+
accountType = deeplinkRoute.accountType,
471+
)
472+
is DeeplinkRoute.Status.AltText ->
473+
Route.Status.AltText(
474+
text = deeplinkRoute.text,
475+
)
476+
is DeeplinkRoute.Status.BlueskyReport ->
477+
Route.Status.BlueskyReport(
478+
statusKey = deeplinkRoute.statusKey,
479+
accountType = deeplinkRoute.accountType,
480+
)
481+
is DeeplinkRoute.Status.DeleteConfirm ->
482+
Route.Status.DeleteConfirm(
483+
statusKey = deeplinkRoute.statusKey,
484+
accountType = deeplinkRoute.accountType,
485+
)
486+
is DeeplinkRoute.Status.Detail ->
487+
Route.Status.Detail(
488+
statusKey = deeplinkRoute.statusKey,
489+
accountType = deeplinkRoute.accountType,
490+
)
491+
is DeeplinkRoute.Status.MastodonReport ->
492+
Route.Status.MastodonReport(
493+
userKey = deeplinkRoute.userKey,
494+
statusKey = deeplinkRoute.statusKey,
495+
accountType = deeplinkRoute.accountType,
496+
)
497+
is DeeplinkRoute.Status.MisskeyReport ->
498+
Route.Status.MisskeyReport(
499+
userKey = deeplinkRoute.userKey,
500+
statusKey = deeplinkRoute.statusKey,
501+
accountType = deeplinkRoute.accountType,
502+
)
503+
is DeeplinkRoute.Status.VVOComment ->
504+
Route.Status.VVOComment(
505+
commentKey = deeplinkRoute.commentKey,
506+
accountType = deeplinkRoute.accountType,
507+
)
508+
is DeeplinkRoute.Status.VVOStatus ->
509+
Route.Status.VVOStatus(
510+
statusKey = deeplinkRoute.statusKey,
511+
accountType = deeplinkRoute.accountType,
512+
)
586513
}
587514
}
588515
}

app/src/main/java/dev/dimension/flare/ui/screen/home/NotificationScreen.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import dev.dimension.flare.ui.component.status.LazyStatusVerticalStaggeredGrid
3535
import dev.dimension.flare.ui.component.status.status
3636
import dev.dimension.flare.ui.model.onSuccess
3737
import dev.dimension.flare.ui.presenter.home.NotificationPresenter
38-
import dev.dimension.flare.ui.presenter.home.NotificationState
3938
import dev.dimension.flare.ui.presenter.home.UserPresenter
4039
import dev.dimension.flare.ui.presenter.home.UserState
4140
import dev.dimension.flare.ui.presenter.invoke
@@ -143,7 +142,7 @@ internal fun NotificationScreen(
143142
@Composable
144143
private fun NotificationFilterSelector(
145144
filters: ImmutableList<NotificationFilter>,
146-
notificationState: NotificationState,
145+
notificationState: NotificationPresenter.State,
147146
modifier: Modifier = Modifier,
148147
) {
149148
val titles = filters.map { stringResource(id = it.title) }

app/src/main/java/dev/dimension/flare/ui/screen/rss/RssDetailScreen.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ import dev.dimension.flare.common.encodeJson
5555
import dev.dimension.flare.data.network.rss.DocumentData
5656
import dev.dimension.flare.data.repository.SettingsRepository
5757
import dev.dimension.flare.ui.component.BackButton
58+
import dev.dimension.flare.ui.component.DateTimeText
5859
import dev.dimension.flare.ui.component.FAIcon
5960
import dev.dimension.flare.ui.component.FlareScaffold
6061
import dev.dimension.flare.ui.component.FlareTopAppBar
6162
import dev.dimension.flare.ui.component.listCard
6263
import dev.dimension.flare.ui.model.collectAsUiState
6364
import dev.dimension.flare.ui.model.flatMap
64-
import dev.dimension.flare.ui.model.localizedFullTime
6565
import dev.dimension.flare.ui.model.onError
6666
import dev.dimension.flare.ui.model.onLoading
6767
import dev.dimension.flare.ui.model.onSuccess
@@ -157,9 +157,10 @@ internal fun RssDetailScreen(
157157
.padding(horizontal = screenHorizontalPadding),
158158
) {
159159
it.publishDateTime?.let {
160-
Text(
161-
text = it.localizedFullTime,
160+
DateTimeText(
161+
it,
162162
style = MaterialTheme.typography.bodySmall,
163+
fullTime = true,
163164
)
164165
}
165166
Spacer(modifier = Modifier.weight(1f))

0 commit comments

Comments
 (0)