Skip to content

Commit 99d71d4

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # shared/src/commonMain/kotlin/site/remlit/snowdrop/App.kt
2 parents 7547e07 + 67c517a commit 99d71d4

8 files changed

Lines changed: 104 additions & 33 deletions

File tree

shared/src/commonMain/kotlin/site/remlit/snowdrop/App.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object MyProfileRoute : Destination(5)
8686
@Serializable
8787
data class ProfileRoute(val id: String) : Destination(6)
8888
@Serializable
89-
data class StatusRoute(val id: String) : Destination(7)
89+
data class ThreadRoute(val id: String) : Destination(7)
9090
@Serializable
9191
object ComposeRoute : Destination(8)
9292

@@ -279,9 +279,9 @@ fun App() = safe {
279279
else Text("Error")
280280
}
281281

282-
composable<StatusRoute> {
283-
val args = it.toRoute<StatusRoute>()
284-
StatusView(args.id)
282+
composable<ThreadRoute> {
283+
val args = it.toRoute<ThreadRoute>()
284+
ThreadView(args.id)
285285
}
286286
composable<ProfileRoute> {
287287
val args = it.toRoute<ProfileRoute>()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package site.remlit.snowdrop.api.statuses
2+
3+
import com.russhwolf.settings.ExperimentalSettingsApi
4+
import io.ktor.client.request.*
5+
import site.remlit.snowdrop.model.ApiResponse
6+
import site.remlit.snowdrop.model.StatusContext
7+
import site.remlit.snowdrop.util.config.endOfRequest
8+
import site.remlit.snowdrop.util.getCurrentAccountHost
9+
import site.remlit.snowdrop.util.getCurrentAccountId
10+
import site.remlit.snowdrop.util.config.httpClient
11+
import site.remlit.snowdrop.util.safeApiRequest
12+
import site.remlit.snowdrop.util.settings
13+
14+
@OptIn(ExperimentalSettingsApi::class)
15+
suspend fun getStatusContext(id: String): ApiResponse<StatusContext> = safeApiRequest {
16+
val accountId = getCurrentAccountId()
17+
val host = getCurrentAccountHost()
18+
val token = settings.getString("account_${accountId}_token", "")
19+
20+
val req = httpClient.get("https://$host/api/v1/statuses/$id/context") {
21+
header("Authorization", "Bearer $token")
22+
}
23+
24+
endOfRequest(req)
25+
}

shared/src/commonMain/kotlin/site/remlit/snowdrop/component/MiniStatus.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import androidx.compose.ui.text.style.TextOverflow
2020
import androidx.compose.ui.unit.dp
2121
import androidx.compose.ui.unit.sp
2222
import org.jetbrains.compose.resources.painterResource
23-
import site.remlit.snowdrop.StatusRoute
23+
import site.remlit.snowdrop.ThreadRoute
2424
import site.remlit.snowdrop.model.Status
2525
import site.remlit.snowdrop.util.LocalNavController
2626
import site.remlit.snowdrop.util.extension.toRelativeString
@@ -36,15 +36,15 @@ fun MiniStatus(status: Status) {
3636
.clip(RoundedCornerShape(10.dp))
3737
.border(1.dp, MaterialTheme.colorScheme.surfaceContainerHigh, RoundedCornerShape(10.dp))
3838
.clickable(onClick = {
39-
navHandler.navigate(StatusRoute(status.id))
39+
navHandler.navigate(ThreadRoute(status.id!!))
4040
})
4141
) {
4242
Column(modifier = Modifier.padding(10.dp)) {
4343
Row(
4444
horizontalArrangement = Arrangement.spacedBy(5.dp),
4545
verticalAlignment = Alignment.CenterVertically
4646
) {
47-
Avatar(status.account, small = true)
47+
Avatar(status.account!!, small = true)
4848
Text(
4949
status.account.displayName ?: status.account.username,
5050
fontWeight = FontWeight.Bold,
@@ -61,7 +61,7 @@ fun MiniStatus(status: Status) {
6161
"${status.getCreatedAtTimestamp()?.toRelativeString()}",
6262
fontSize = 13.sp
6363
)
64-
Visibility(status.visibility)
64+
Visibility(status.visibility!!)
6565
}
6666
}
6767

shared/src/commonMain/kotlin/site/remlit/snowdrop/component/Status.kt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
3737
import com.russhwolf.settings.ExperimentalSettingsApi
3838
import org.jetbrains.compose.resources.painterResource
3939
import site.remlit.snowdrop.ProfileRoute
40-
import site.remlit.snowdrop.StatusRoute
40+
import site.remlit.snowdrop.ThreadRoute
4141
import site.remlit.snowdrop.component.dropdown.DangerDropdownItem
4242
import site.remlit.snowdrop.model.Status
4343
import site.remlit.snowdrop.model.User
@@ -87,7 +87,7 @@ fun Status(status: Status) {
8787
var realStatus by remember { mutableStateOf(status) }
8888
var isReblog by remember { mutableStateOf(false) }
8989
var rebloggingAccount by remember { mutableStateOf<User?>(null) }
90-
var isMine by remember { mutableStateOf(realStatus.account.id == currentAccount?.id) }
90+
var isMine by remember { mutableStateOf(realStatus.account?.id == currentAccount?.id) }
9191
//todo: or is admin? figure out how to do that
9292

9393
if (status.reblog != null) {
@@ -119,9 +119,9 @@ fun Status(status: Status) {
119119
Column(
120120
modifier = Modifier.clickable(
121121
// todo: fix this when we add ascendants/descendants (idk how to get the id of the current view)
122-
enabled = !atRoute<StatusRoute>(currentDest),
122+
enabled = !atRoute<ThreadRoute>(currentDest),
123123
onClick = {
124-
navHandler.navigate(StatusRoute(realStatus.id))
124+
navHandler.navigate(ThreadRoute(realStatus.id!!))
125125
}
126126
)
127127
) {
@@ -175,26 +175,26 @@ fun Status(status: Status) {
175175
Column(
176176
modifier = Modifier.padding(end = 10.dp)
177177
.clickable(onClick = {
178-
navHandler.navigate(ProfileRoute(realStatus.account.id))
178+
navHandler.navigate(ProfileRoute(realStatus.account?.id!!))
179179
})
180180
) {
181-
Avatar(realStatus.account)
181+
Avatar(realStatus.account!!)
182182
}
183183

184184
Column(
185185
modifier = Modifier.weight(1f)
186186
.clickable(onClick = {
187-
navHandler.navigate(ProfileRoute(realStatus.account.id))
187+
navHandler.navigate(ProfileRoute(realStatus.account?.id!!))
188188
})
189189
) {
190190
Text(
191-
realStatus.account.displayName ?: realStatus.account.username,
191+
realStatus.account?.displayName ?: realStatus.account?.username!!,
192192
fontWeight = FontWeight.Medium,
193193
overflow = TextOverflow.Ellipsis,
194194
maxLines = 1
195195
)
196196
Text(
197-
"@${realStatus.account.acct}",
197+
"@${realStatus.account?.acct}",
198198
overflow = TextOverflow.Ellipsis,
199199
color = MaterialTheme.colorScheme.onSurfaceVariant,
200200
fontSize = 13.sp,
@@ -208,7 +208,7 @@ fun Status(status: Status) {
208208
Column(
209209
horizontalAlignment = Alignment.CenterHorizontally
210210
) {
211-
Visibility(status.visibility)
211+
Visibility(status.visibility!!)
212212
Text(
213213
"${realStatus.getCreatedAtTimestamp()?.toRelativeString()}",
214214
fontSize = 13.sp
@@ -381,7 +381,7 @@ fun Status(status: Status) {
381381
Icon(painterResource(Res.drawable.icon_link_24px), null)
382382
},
383383
onClick = {
384-
clipboardManager.setText(AnnotatedString(status.url))
384+
clipboardManager.setText(AnnotatedString(realStatus.url!!))
385385
showDropdown = !showDropdown
386386
}
387387
)
@@ -392,7 +392,7 @@ fun Status(status: Status) {
392392
Icon(painterResource(Res.drawable.icon_open_in_new_24px), null)
393393
},
394394
onClick = {
395-
uriHandler.openUri(status.url)
395+
uriHandler.openUri(realStatus.url!!)
396396
showDropdown = !showDropdown
397397
}
398398
)

shared/src/commonMain/kotlin/site/remlit/snowdrop/model/Status.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import kotlin.time.Instant
77

88
@Serializable
99
data class Status(
10-
val id: String,
11-
val account: User,
10+
val id: String? = null,
11+
val account: User? = null,
1212

1313
@SerialName("spoiler_text")
1414
val spoilerText: String? = null,
@@ -50,7 +50,7 @@ data class Status(
5050
val sensitive: Boolean = false,
5151
val pinned: Boolean = false,
5252

53-
val visibility: String,
53+
val visibility: String? = null,
5454

5555
val poll: Poll? = null,
5656
val filtered: List<Filtered>? = null,
@@ -61,7 +61,7 @@ data class Status(
6161
val reactions: List<Reaction> = listOf(),
6262
val tags: List<Tag> = listOf(),
6363

64-
val card: String? = null,
64+
val card: Unit? = null,
6565
val application: Unit? = null,
6666
val language: String? = null,
6767

@@ -70,7 +70,7 @@ data class Status(
7070
val quotedStatus: Status? = null,
7171

7272
@SerialName("created_at")
73-
val createdAt: String,
73+
val createdAt: String? = null,
7474
@SerialName("edited_at")
7575
val editedAt: String? = null,
7676
) {
@@ -177,7 +177,7 @@ data class Status(
177177
data class Original(
178178
val width: Long = 0,
179179
val height: Long = 0,
180-
val size: String,
180+
val size: String? = null,
181181
val aspect: Double = 0.0,
182182
)
183183
}
@@ -204,6 +204,6 @@ data class Status(
204204

205205
// Instants
206206
fun getCreatedAtTimestamp(): Instant? = safeReturnable {
207-
Instant.parse(this.createdAt)
207+
Instant.parse(this.createdAt!!)
208208
}
209209
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package site.remlit.snowdrop.model
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class StatusContext(
8+
val ancestors: List<Status>,
9+
val descendants: List<Status>
10+
)

shared/src/commonMain/kotlin/site/remlit/snowdrop/view/StatusView.kt renamed to shared/src/commonMain/kotlin/site/remlit/snowdrop/view/ThreadView.kt

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.fillMaxHeight
66
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.lazy.LazyColumn
8+
import androidx.compose.foundation.lazy.items
9+
import androidx.compose.foundation.lazy.rememberLazyListState
710
import androidx.compose.foundation.rememberScrollState
811
import androidx.compose.material3.CircularProgressIndicator
912
import androidx.compose.material3.Text
1013
import androidx.compose.material3.TopAppBar
1114
import androidx.compose.runtime.Composable
1215
import androidx.compose.runtime.LaunchedEffect
1316
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateListOf
1418
import androidx.compose.runtime.mutableStateOf
1519
import androidx.compose.runtime.remember
1620
import androidx.compose.runtime.setValue
@@ -20,17 +24,27 @@ import androidx.compose.ui.text.style.TextOverflow
2024
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2125
import kotlinx.coroutines.Dispatchers
2226
import site.remlit.snowdrop.api.statuses.getStatus
27+
import site.remlit.snowdrop.api.statuses.getStatusContext
28+
import site.remlit.snowdrop.api.timeline.getHomeTimeline
29+
import site.remlit.snowdrop.component.Status
2330
import site.remlit.snowdrop.component.ViewSurface
2431
import site.remlit.snowdrop.model.Status
32+
import site.remlit.snowdrop.model.StatusContext
2533
import site.remlit.snowdrop.util.getCurrentAccountObjectFlow
2634
import site.remlit.snowdrop.component.Status as StatusComponent
2735

2836
@Composable
29-
fun StatusView(id: String) = ViewSurface {
37+
fun ThreadView(id: String, status: Status? = null) = ViewSurface {
3038
val currentAccount by getCurrentAccountObjectFlow()
3139
.collectAsStateWithLifecycle(null)
3240

33-
var status by remember { mutableStateOf<Status?>(null) }
41+
var status by remember { mutableStateOf(status) }
42+
43+
val threadStatuses = remember { mutableStateListOf<Status>() }
44+
val context = remember { mutableStateOf<StatusContext?>(null) }
45+
46+
val listState = rememberLazyListState()
47+
3448
var ready by remember { mutableStateOf(false) }
3549

3650
val scrollState = rememberScrollState()
@@ -41,7 +55,18 @@ fun StatusView(id: String) = ViewSurface {
4155
if (req.error) return@LaunchedEffect
4256
status = req.response
4357

58+
threadStatuses.clear()
59+
threadStatuses.add(status!!)
60+
val res = getStatusContext(status!!.id!!)
61+
if (res.error) return@LaunchedEffect
62+
if (res.response == null) return@LaunchedEffect
63+
context.value = res.response
64+
threadStatuses.addAll(0, res.response.ancestors)
65+
threadStatuses.addAll(res.response.descendants)
66+
4467
ready = true
68+
69+
listState.scrollToItem(threadStatuses.indexOf(threadStatuses.find { sts -> status!!.id == sts.id }))
4570
}
4671

4772
TopAppBar(
@@ -51,7 +76,7 @@ fun StatusView(id: String) = ViewSurface {
5176
}
5277
else Column {
5378
Text(
54-
"Post by " + (status!!.account.displayName ?: status!!.account.username),
79+
"Post by " + (status!!.account?.displayName ?: status!!.account?.username),
5580
maxLines = 1,
5681
overflow = TextOverflow.Ellipsis
5782
)
@@ -68,6 +93,17 @@ fun StatusView(id: String) = ViewSurface {
6893
CircularProgressIndicator()
6994
}
7095
} else {
71-
StatusComponent(status!!)
96+
LazyColumn(
97+
state = listState,
98+
) {
99+
items(
100+
items = threadStatuses,
101+
key = { status ->
102+
status.id!!
103+
}
104+
) { status ->
105+
StatusComponent(status)
106+
}
107+
}
72108
}
73109
}

shared/src/commonMain/kotlin/site/remlit/snowdrop/view/TimelineView.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,12 @@ fun TimelineView() = ViewSurface {
148148
items(
149149
items = timeline,
150150
key = { status ->
151-
status.id
151+
status.id!!
152152
}
153153
) { status ->
154154
Status(status)
155155
}
156156
}
157157
}
158158
}
159-
}
159+
}

0 commit comments

Comments
 (0)