Skip to content

Commit 65f3e24

Browse files
authored
Merge pull request #1654 from DimensionDev/bugfix/bsky_deeplink
fix bluesky post deeplink
2 parents ee63444 + c84d417 commit 65f3e24

File tree

11 files changed

+198
-81
lines changed

11 files changed

+198
-81
lines changed

app/src/main/java/dev/dimension/flare/ui/AppContainer.kt

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ fun FlareApp(content: @Composable () -> Unit) {
4949
object : UriHandler {
5050
override fun openUri(uri: String) {
5151
if (uri.startsWith("http://") || uri.startsWith("https://")) {
52-
openInBrowser(context, uri, appearanceSettings.inAppBrowser)
52+
openInBrowser(context, uri, appearanceSettings.inAppBrowser) {
53+
originalUriHandler.openUri(uri)
54+
}
5355
} else {
5456
originalUriHandler.openUri(uri)
5557
}
@@ -120,20 +122,27 @@ private fun openInBrowser(
120122
context: Context,
121123
url: String,
122124
inAppBrowser: Boolean,
125+
fallbackOpenUrl: (String) -> Unit,
123126
) {
124127
val targetPackage = getNonSelfBrowserPackageName(context, url)
125128
if (targetPackage != null) {
126-
if (inAppBrowser) {
127-
val builder = CustomTabsIntent.Builder()
128-
val customTabsIntent = builder.build()
129-
customTabsIntent.intent.setPackage(targetPackage)
130-
customTabsIntent.launchUrl(context, url.toUri())
131-
} else {
132-
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
133-
intent.addCategory(Intent.CATEGORY_BROWSABLE)
134-
intent.setPackage(targetPackage)
135-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
136-
context.startActivity(intent)
129+
runCatching {
130+
if (inAppBrowser) {
131+
val builder = CustomTabsIntent.Builder()
132+
val customTabsIntent = builder.build()
133+
customTabsIntent.intent.setPackage(targetPackage)
134+
customTabsIntent.launchUrl(context, url.toUri())
135+
} else {
136+
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
137+
intent.addCategory(Intent.CATEGORY_BROWSABLE)
138+
intent.setPackage(targetPackage)
139+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
140+
context.startActivity(intent)
141+
}
142+
}.onFailure {
143+
fallbackOpenUrl.invoke(url)
137144
}
145+
} else {
146+
fallbackOpenUrl.invoke(url)
138147
}
139148
}

app/src/main/java/dev/dimension/flare/ui/screen/status/StatusScreen.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import dev.dimension.flare.ui.component.RefreshContainer
3030
import dev.dimension.flare.ui.component.platform.isBigScreen
3131
import dev.dimension.flare.ui.component.status.LazyStatusVerticalStaggeredGrid
3232
import dev.dimension.flare.ui.component.status.status
33+
import dev.dimension.flare.ui.model.takeSuccess
3334
import dev.dimension.flare.ui.presenter.invoke
3435
import dev.dimension.flare.ui.presenter.status.StatusContextPresenter
3536
import kotlinx.coroutines.launch
@@ -90,7 +91,10 @@ internal fun StatusScreen(
9091
) {
9192
status(
9293
state.state.listState,
93-
detailStatusKey = statusKey,
94+
detailStatusKey =
95+
state.state.current
96+
.takeSuccess()
97+
?.statusKey,
9498
)
9599
}
96100
},

app/src/main/java/dev/dimension/flare/ui/theme/Theme.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private fun ColorScheme.withPureColorLightMode(): ColorScheme =
5151
surfaceContainerHighest = Color.White,
5252
onSurfaceVariant = MoreColors.Gray800,
5353
outlineVariant = MoreColors.Gray400,
54+
outline = MoreColors.Gray600,
5455
)
5556

5657
private fun ColorScheme.withPureColorDarkMode(): ColorScheme =
@@ -66,6 +67,7 @@ private fun ColorScheme.withPureColorDarkMode(): ColorScheme =
6667
surfaceContainerHighest = MoreColors.Gray900,
6768
onSurfaceVariant = MoreColors.Gray400,
6869
outlineVariant = MoreColors.Gray800,
70+
outline = MoreColors.Gray500,
6971
)
7072

7173
@OptIn(ExperimentalMaterial3ExpressiveApi::class)

compose-ui/src/androidMain/kotlin/dev/dimension/flare/ui/component/platform/PlatformCard.android.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ internal actual fun PlatformCard(
4545
if (onClick == null) {
4646
Card(
4747
modifier = modifier,
48-
shape = shape ?: CardDefaults.shape,
48+
shape =
49+
shape ?: if (elevated) {
50+
CardDefaults.elevatedShape
51+
} else {
52+
CardDefaults.shape
53+
},
4954
colors = colors,
5055
elevation =
5156
if (elevated) {
@@ -60,7 +65,12 @@ internal actual fun PlatformCard(
6065
} else {
6166
Card(
6267
modifier = modifier,
63-
shape = shape ?: CardDefaults.shape,
68+
shape =
69+
shape ?: if (elevated) {
70+
CardDefaults.elevatedShape
71+
} else {
72+
CardDefaults.shape
73+
},
6474
colors = colors,
6575
elevation =
6676
if (elevated) {

desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/status/StatusScreen.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import dev.dimension.flare.LocalWindowPadding
1919
import dev.dimension.flare.RegisterTabCallback
2020
import dev.dimension.flare.model.AccountType
2121
import dev.dimension.flare.model.MicroBlogKey
22-
import dev.dimension.flare.ui.common.plus
2322
import dev.dimension.flare.ui.component.status.LazyStatusVerticalStaggeredGrid
2423
import dev.dimension.flare.ui.component.status.status
24+
import dev.dimension.flare.ui.model.takeSuccess
2525
import dev.dimension.flare.ui.presenter.invoke
2626
import dev.dimension.flare.ui.presenter.status.StatusContextPresenter
2727
import io.github.composefluent.component.ProgressBar
@@ -53,7 +53,10 @@ internal fun StatusScreen(
5353
) {
5454
status(
5555
state.state.listState,
56-
detailStatusKey = statusKey,
56+
detailStatusKey =
57+
state.state.current
58+
.takeSuccess()
59+
?.statusKey,
5760
)
5861
}
5962
if (state.isRefreshing) {

iosApp/flare/UI/Screen/StatusDetailScreen.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@ import SwiftUI
44
struct StatusDetailScreen: View {
55
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
66
@Environment(\.openURL) private var openURL
7-
@StateObject private var presenter: KotlinPresenter<TimelineState>
7+
@StateObject private var presenter: KotlinPresenter<StatusContextPresenterState>
88
private let statusKey: MicroBlogKey
9+
private var detailStatusKey: MicroBlogKey? {
10+
return switch onEnum(of: presenter.state.current) {
11+
case .success(let data):
12+
data.data.statusKey
13+
default:
14+
nil
15+
}
16+
}
917

1018
init(accountType: AccountType, statusKey: MicroBlogKey) {
1119
self.statusKey = statusKey
@@ -14,7 +22,7 @@ struct StatusDetailScreen: View {
1422

1523
var body: some View {
1624
ZStack {
17-
TimelinePagingContent(data: presenter.state.listState, detailStatusKey: statusKey, key: presenter.key)
25+
TimelinePagingContent(data: presenter.state.listState, detailStatusKey: detailStatusKey, key: presenter.key)
1826
.frame(maxWidth: horizontalSizeClass == .compact ? .infinity : 600, alignment: .center)
1927
}
2028
.frame(maxWidth: .infinity, maxHeight: .infinity)

shared/src/commonMain/kotlin/dev/dimension/flare/common/deeplink/DeepLinkMapping.kt

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ internal object DeepLinkMapping {
5151
)
5252
}
5353

54+
@Serializable
55+
data class BlueskyPost(
56+
val handle: String,
57+
val id: String,
58+
) : Type {
59+
override fun deepLink(accountKey: MicroBlogKey): DeeplinkRoute =
60+
DeeplinkRoute.Status.Detail(
61+
accountType = AccountType.Specific(accountKey),
62+
statusKey =
63+
MicroBlogKey(
64+
"at://$handle/app.bsky.feed.post/$id",
65+
accountKey.host,
66+
),
67+
)
68+
}
69+
5470
@Serializable
5571
data class PostMedia(
5672
val handle: String,
@@ -105,10 +121,24 @@ internal object DeepLinkMapping {
105121
Url("https://$host/profile/{handle}"),
106122
),
107123
DeepLinkPattern(
108-
Type.Post.serializer(),
124+
Type.BlueskyPost.serializer(),
109125
Url("https://$host/profile/{handle}/post/{id}"),
110126
),
111-
)
127+
) +
128+
if (host == "bsky.social") {
129+
listOf(
130+
DeepLinkPattern(
131+
Type.Profile.serializer(),
132+
Url("https://bsky.app/profile/{handle}"),
133+
),
134+
DeepLinkPattern(
135+
Type.BlueskyPost.serializer(),
136+
Url("https://bsky.app/profile/{handle}/post/{id}"),
137+
),
138+
)
139+
} else {
140+
emptyList()
141+
}
112142
}
113143

114144
PlatformType.xQt -> {

shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/dao/PagingTimelineDao.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import dev.dimension.flare.data.database.cache.model.DbStatusWithReference
1515
import dev.dimension.flare.model.AccountType
1616
import dev.dimension.flare.model.DbAccountType
1717
import dev.dimension.flare.model.MicroBlogKey
18+
import kotlinx.coroutines.flow.Flow
1819

1920
@Dao
2021
internal interface PagingTimelineDao {
@@ -41,6 +42,13 @@ internal interface PagingTimelineDao {
4142
)
4243
fun searchHistoryPagingSource(query: String): PagingSource<Int, DbStatusWithReference>
4344

45+
@Transaction
46+
@Query("SELECT * FROM DbPagingTimeline WHERE pagingKey = :pagingKey AND accountType = :accountType LIMIT 1")
47+
fun get(
48+
pagingKey: String,
49+
accountType: DbAccountType,
50+
): Flow<DbPagingTimelineWithStatus?>
51+
4452
@Transaction
4553
@Query("SELECT * FROM DbPagingTimeline WHERE pagingKey = :pagingKey ORDER BY sortId DESC")
4654
fun getStatusHistoryPagingSource(pagingKey: String): PagingSource<Int, DbPagingTimelineWithStatus>

shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/bluesky/BlueskyDataSource.kt

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import chat.bsky.convo.MessageInput
4545
import chat.bsky.convo.MessageView
4646
import chat.bsky.convo.SendMessageRequest
4747
import chat.bsky.convo.UpdateReadRequest
48+
import com.atproto.identity.ResolveHandleQueryParams
4849
import com.atproto.moderation.CreateReportRequest
4950
import com.atproto.moderation.CreateReportRequestSubjectUnion
5051
import com.atproto.moderation.Token
@@ -310,33 +311,63 @@ internal class BlueskyDataSource(
310311

311312
override fun status(statusKey: MicroBlogKey): CacheData<UiTimeline> {
312313
val pagingKey = "status_only_$statusKey"
313-
314314
return Cacheable(
315315
fetchSource = {
316-
val result =
317-
service
318-
.getPosts(
319-
GetPostsQueryParams(
320-
persistentListOf(AtUri(statusKey.id)),
321-
),
322-
).requireResponse()
323-
.posts
324-
.firstOrNull()
325-
.let {
326-
listOfNotNull(it)
327-
}
328-
Bluesky.savePost(
329-
accountKey,
330-
pagingKey,
331-
database,
332-
result,
333-
)
316+
val isDid = statusKey.id.startsWith("at://did:")
317+
if (isDid) {
318+
val result =
319+
service
320+
.getPosts(
321+
GetPostsQueryParams(
322+
persistentListOf(AtUri(statusKey.id)),
323+
),
324+
).requireResponse()
325+
.posts
326+
.firstOrNull()
327+
.let {
328+
listOfNotNull(it)
329+
}
330+
database.connect {
331+
Bluesky.savePost(
332+
accountKey,
333+
pagingKey,
334+
database,
335+
result,
336+
)
337+
}
338+
} else {
339+
// "at://${handle}/app.bsky.feed.post/${id}"
340+
val handle = statusKey.id.substringAfter("at://").substringBefore("/")
341+
val id = statusKey.id.substringAfterLast('/')
342+
val did = service.resolveHandle(ResolveHandleQueryParams(Handle(handle))).requireResponse().did
343+
val actualAtUri = AtUri("at://${did.did}/app.bsky.feed.post/$id")
344+
val result =
345+
service
346+
.getPosts(
347+
GetPostsQueryParams(
348+
persistentListOf(actualAtUri),
349+
),
350+
).requireResponse()
351+
.posts
352+
.firstOrNull()
353+
.let {
354+
listOfNotNull(it)
355+
}
356+
database.connect {
357+
Bluesky.savePost(
358+
accountKey,
359+
pagingKey,
360+
database,
361+
result,
362+
)
363+
}
364+
}
334365
},
335366
cacheSource = {
336367
database
337-
.statusDao()
338-
.get(statusKey, accountType = AccountType.Specific(accountKey))
339-
.mapNotNull { it?.content?.render(this) }
368+
.pagingTimelineDao()
369+
.get(pagingKey, accountType = AccountType.Specific(accountKey))
370+
.mapNotNull { it?.render(this) }
340371
},
341372
)
342373
}

0 commit comments

Comments
 (0)