Skip to content

Commit 066b504

Browse files
committed
add post media deelink handling
1 parent da5e98d commit 066b504

File tree

2 files changed

+129
-22
lines changed

2 files changed

+129
-22
lines changed

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ internal object DeepLinkMapping {
5050
statusKey = MicroBlogKey(id, accountKey.host),
5151
)
5252
}
53+
54+
@Serializable
55+
data class PostMedia(
56+
val handle: String,
57+
val id: String,
58+
val index: Int,
59+
) : Type {
60+
override fun deepLink(accountKey: MicroBlogKey): DeeplinkRoute =
61+
DeeplinkRoute.Media.StatusMedia(
62+
accountType = AccountType.Specific(accountKey),
63+
statusKey = MicroBlogKey(id, accountKey.host),
64+
index = index,
65+
preview = null,
66+
)
67+
}
5368
}
5469

5570
fun generatePattern(
@@ -111,6 +126,13 @@ internal object DeepLinkMapping {
111126
"https://www.$xqtHost/{handle}/status/{id}",
112127
"https://www.$xqtOldHost/{handle}/",
113128
)
129+
val media =
130+
listOf(
131+
"https://$xqtHost/{handle}/status/{id}/photo/{index}",
132+
"https://$xqtOldHost/{handle}/status/{id}/photo/{index}",
133+
"https://www.$xqtHost/{handle}/status/{id}/photo/{index}",
134+
"https://www.$xqtOldHost/{handle}/status/{id}/photo/{index}",
135+
)
114136
profile.map {
115137
DeepLinkPattern(
116138
Type.Profile.serializer(),
@@ -122,6 +144,12 @@ internal object DeepLinkMapping {
122144
Type.Post.serializer(),
123145
Url(it),
124146
)
147+
} +
148+
media.map {
149+
DeepLinkPattern(
150+
Type.PostMedia.serializer(),
151+
Url(it),
152+
)
125153
}
126154
}
127155

shared/src/commonTest/kotlin/dev/dimension/flare/common/deeplink/DeepLinkMappingTest.kt

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@ class DeepLinkMappingTest {
2929
assertEquals(Url("https://$host/@{handle}"), profile.uriPattern)
3030
assertEquals(
3131
listOf("handle" to true),
32-
profile.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
32+
profile.pathSegments
33+
.filter { it.stringValue.isNotEmpty() }
34+
.map { it.stringValue to it.isParamArg },
3335
)
3436

3537
val post = patterns[1]
3638
assertEquals(DeepLinkMapping.Type.Post.serializer(), post.serializer)
3739
assertEquals(Url("https://$host/@{handle}/{id}"), post.uriPattern)
3840
assertEquals(
3941
listOf("handle" to true, "id" to true),
40-
post.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
42+
post.pathSegments
43+
.filter { it.stringValue.isNotEmpty() }
44+
.map { it.stringValue to it.isParamArg },
4145
)
4246
}
4347

@@ -54,15 +58,19 @@ class DeepLinkMappingTest {
5458
assertEquals(Url("https://$host/@{handle}"), profile.uriPattern)
5559
assertEquals(
5660
listOf("handle" to true),
57-
profile.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
61+
profile.pathSegments
62+
.filter { it.stringValue.isNotEmpty() }
63+
.map { it.stringValue to it.isParamArg },
5864
)
5965

6066
val post = patterns[1]
6167
assertEquals(DeepLinkMapping.Type.Post.serializer(), post.serializer)
6268
assertEquals(Url("https://$host/notes/{id}"), post.uriPattern)
6369
assertEquals(
6470
listOf("notes" to false, "id" to true),
65-
post.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
71+
post.pathSegments
72+
.filter { it.stringValue.isNotEmpty() }
73+
.map { it.stringValue to it.isParamArg },
6674
)
6775
}
6876

@@ -79,15 +87,19 @@ class DeepLinkMappingTest {
7987
assertEquals(Url("https://$host/profile/{handle}"), profile.uriPattern)
8088
assertEquals(
8189
listOf("profile" to false, "handle" to true),
82-
profile.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
90+
profile.pathSegments
91+
.filter { it.stringValue.isNotEmpty() }
92+
.map { it.stringValue to it.isParamArg },
8393
)
8494

8595
val post = patterns[1]
8696
assertEquals(DeepLinkMapping.Type.Post.serializer(), post.serializer)
8797
assertEquals(Url("https://$host/profile/{handle}/post/{id}"), post.uriPattern)
8898
assertEquals(
8999
listOf("profile" to false, "handle" to true, "post" to false, "id" to true),
90-
post.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
100+
post.pathSegments
101+
.filter { it.stringValue.isNotEmpty() }
102+
.map { it.stringValue to it.isParamArg },
91103
)
92104
}
93105

@@ -97,22 +109,42 @@ class DeepLinkMappingTest {
97109

98110
val patterns = DeepLinkMapping.generatePattern(PlatformType.xQt, host)
99111

100-
assertEquals(8, patterns.size)
112+
assertEquals(12, patterns.size)
101113

102114
val profile = patterns[0]
103115
assertEquals(DeepLinkMapping.Type.Profile.serializer(), profile.serializer)
104116
assertEquals(Url("https://$host/{handle}"), profile.uriPattern)
105117
assertEquals(
106118
listOf("handle" to true),
107-
profile.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
119+
profile.pathSegments
120+
.filter { it.stringValue.isNotEmpty() }
121+
.map { it.stringValue to it.isParamArg },
108122
)
109123

110124
val post = patterns[4]
111125
assertEquals(DeepLinkMapping.Type.Post.serializer(), post.serializer)
112126
assertEquals(Url("https://$host/{handle}/status/{id}"), post.uriPattern)
113127
assertEquals(
114128
listOf("handle" to true, "status" to false, "id" to true),
115-
post.pathSegments.filter { it.stringValue.isNotEmpty() }.map { it.stringValue to it.isParamArg },
129+
post.pathSegments
130+
.filter { it.stringValue.isNotEmpty() }
131+
.map { it.stringValue to it.isParamArg },
132+
)
133+
134+
val media = patterns[8]
135+
assertEquals(DeepLinkMapping.Type.PostMedia.serializer(), media.serializer)
136+
assertEquals(Url("https://$host/{handle}/status/{id}/photo/{index}"), media.uriPattern)
137+
assertEquals(
138+
listOf(
139+
"handle" to true,
140+
"status" to false,
141+
"id" to true,
142+
"photo" to false,
143+
"index" to true,
144+
),
145+
media.pathSegments
146+
.filter { it.stringValue.isNotEmpty() }
147+
.map { it.stringValue to it.isParamArg },
116148
)
117149
}
118150

@@ -172,8 +204,18 @@ class DeepLinkMappingTest {
172204
val mapping:
173205
ImmutableMap<UiAccount, ImmutableList<DeepLinkPattern<out DeepLinkMapping.Type>>> =
174206
persistentMapOf(
175-
account1 to DeepLinkMapping.generatePattern(PlatformType.Mastodon, account1.accountKey.host).toImmutableList(),
176-
account2 to DeepLinkMapping.generatePattern(PlatformType.Mastodon, account2.accountKey.host).toImmutableList(),
207+
account1 to
208+
DeepLinkMapping
209+
.generatePattern(
210+
PlatformType.Mastodon,
211+
account1.accountKey.host,
212+
).toImmutableList(),
213+
account2 to
214+
DeepLinkMapping
215+
.generatePattern(
216+
PlatformType.Mastodon,
217+
account2.accountKey.host,
218+
).toImmutableList(),
177219
)
178220

179221
val matches = DeepLinkMapping.matches("https://mastodon.social/@alice", mapping)
@@ -193,7 +235,12 @@ class DeepLinkMappingTest {
193235
val mapping:
194236
ImmutableMap<UiAccount, ImmutableList<DeepLinkPattern<out DeepLinkMapping.Type>>> =
195237
persistentMapOf(
196-
account to DeepLinkMapping.generatePattern(PlatformType.Mastodon, account.accountKey.host).toImmutableList(),
238+
account to
239+
DeepLinkMapping
240+
.generatePattern(
241+
PlatformType.Mastodon,
242+
account.accountKey.host,
243+
).toImmutableList(),
197244
)
198245

199246
// URL containing none of the valid hosts
@@ -239,33 +286,60 @@ class DeepLinkMappingTest {
239286
PlatformType.Misskey,
240287
misskeyAccount.accountKey.host,
241288
).toImmutableList(),
242-
bskyAccount to DeepLinkMapping.generatePattern(PlatformType.Bluesky, bskyAccount.accountKey.host).toImmutableList(),
243-
xAccount to DeepLinkMapping.generatePattern(PlatformType.xQt, xAccount.accountKey.host).toImmutableList(),
289+
bskyAccount to
290+
DeepLinkMapping
291+
.generatePattern(
292+
PlatformType.Bluesky,
293+
bskyAccount.accountKey.host,
294+
).toImmutableList(),
295+
xAccount to
296+
DeepLinkMapping
297+
.generatePattern(
298+
PlatformType.xQt,
299+
xAccount.accountKey.host,
300+
).toImmutableList(),
244301
)
245302

246303
// https://mastodon.example/@alice
247-
val mastodonProfileMatch = DeepLinkMapping.matches("https://mastodon.example/@alice", mapping)
304+
val mastodonProfileMatch =
305+
DeepLinkMapping.matches("https://mastodon.example/@alice", mapping)
248306
assertEquals(DeepLinkMapping.Type.Profile("alice"), mastodonProfileMatch[mastodonAccount])
249307

250308
// https://mastodon.example/@alice/12345
251-
val mastodonPostMatch = DeepLinkMapping.matches("https://mastodon.example/@alice/12345", mapping)
252-
assertEquals(DeepLinkMapping.Type.Post("alice", "12345"), mastodonPostMatch[mastodonAccount])
309+
val mastodonPostMatch =
310+
DeepLinkMapping.matches("https://mastodon.example/@alice/12345", mapping)
311+
assertEquals(
312+
DeepLinkMapping.Type.Post("alice", "12345"),
313+
mastodonPostMatch[mastodonAccount],
314+
)
253315

254316
// https://misskey.example/@bob
255317
val misskeyProfileMatch = DeepLinkMapping.matches("https://misskey.example/@bob", mapping)
256318
assertEquals(DeepLinkMapping.Type.Profile("bob"), misskeyProfileMatch[misskeyAccount])
257319

258320
// https://misskey.example/notes/12345
259-
val misskeyPostMatch = DeepLinkMapping.matches("https://misskey.example/notes/12345", mapping)
321+
val misskeyPostMatch =
322+
DeepLinkMapping.matches("https://misskey.example/notes/12345", mapping)
260323
assertEquals(DeepLinkMapping.Type.Post(null, "12345"), misskeyPostMatch[misskeyAccount])
261324

262325
// https://bsky.example/profile/alice.bsky.social
263-
val bskyProfileMatch = DeepLinkMapping.matches("https://bsky.example/profile/alice.bsky.social", mapping)
264-
assertEquals(DeepLinkMapping.Type.Profile("alice.bsky.social"), bskyProfileMatch[bskyAccount])
326+
val bskyProfileMatch =
327+
DeepLinkMapping.matches("https://bsky.example/profile/alice.bsky.social", mapping)
328+
assertEquals(
329+
DeepLinkMapping.Type.Profile("alice.bsky.social"),
330+
bskyProfileMatch[bskyAccount],
331+
)
265332

266333
// https://bsky.example/profile/alice.bsky.social/post/12345
267-
val bskyPostMatch = DeepLinkMapping.matches("https://bsky.example/profile/alice.bsky.social/post/12345", mapping)
268-
assertEquals(DeepLinkMapping.Type.Post("alice.bsky.social", "12345"), bskyPostMatch[bskyAccount])
334+
val bskyPostMatch =
335+
DeepLinkMapping.matches(
336+
"https://bsky.example/profile/alice.bsky.social/post/12345",
337+
mapping,
338+
)
339+
assertEquals(
340+
DeepLinkMapping.Type.Post("alice.bsky.social", "12345"),
341+
bskyPostMatch[bskyAccount],
342+
)
269343

270344
// https://x.example/alice
271345
val xProfileMatch = DeepLinkMapping.matches("https://$xqtHost/alice", mapping)
@@ -274,6 +348,11 @@ class DeepLinkMappingTest {
274348
// https://x.example/alice/status/12345
275349
val xPostMatch = DeepLinkMapping.matches("https://$xqtHost/alice/status/12345", mapping)
276350
assertEquals(DeepLinkMapping.Type.Post("alice", "12345"), xPostMatch[xAccount])
351+
352+
// https://x.example/alice/status/12345/photo/1
353+
val xPostPhotoMatch =
354+
DeepLinkMapping.matches("https://$xqtHost/alice/status/12345/photo/1", mapping)
355+
assertEquals(DeepLinkMapping.Type.PostMedia("alice", "12345", 1), xPostPhotoMatch[xAccount])
277356
}
278357

279358
@Test

0 commit comments

Comments
 (0)