Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions android/app/src/main/java/me/masonasons/fastsm/ui/CoreViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ class CoreViewModel(app: Application) : AndroidViewModel(app) {
private val _mediaPicker = MutableStateFlow<List<MediaItemUi>?>(null)
val mediaPicker: StateFlow<List<MediaItemUi>?> = _mediaPicker.asStateFlow()

// A post's links to choose from (url_picker), when "Open links" finds several.
private val _linkPicker = MutableStateFlow<List<LinkUi>?>(null)
val linkPicker: StateFlow<List<LinkUi>?> = _linkPicker.asStateFlow()

// Push notifications: whether the user has them on. PushManager owns the
// persisted value and the device-side plumbing; this mirrors it for the UI.
private val _pushEnabled = MutableStateFlow(false)
Expand Down Expand Up @@ -662,6 +666,16 @@ class CoreViewModel(app: Application) : AndroidViewModel(app) {
"media_open" -> _media.value =
MediaItemUi(e.optString("url"), e.optString("kind"), e.optString("title"))

"url_picker" -> {
val arr = e.optJSONArray("links")
_linkPicker.value = buildList {
if (arr != null) for (i in 0 until arr.length()) {
val l = arr.getJSONObject(i)
add(LinkUi(l.optString("title"), l.optString("url")))
}
}
}

"media_picker" -> {
val arr = e.optJSONArray("items")
_mediaPicker.value = buildList {
Expand Down Expand Up @@ -1338,6 +1352,19 @@ class CoreViewModel(app: Application) : AndroidViewModel(app) {
fun checkForUpdate() =
core.dispatch("check_for_update") { put("silent", false); put("branch", "stable") }

/** Open a post's links: one opens directly, several raise a chooser. */
fun openPostLinks(id: String) = core.dispatch("open_post_links") { put("id", id) }

/** Open the link chosen from a post's several. */
fun openPickedLink(link: LinkUi) {
_linkPicker.value = null
openLink(link.url)
}

fun dismissLinkPicker() {
_linkPicker.value = null
}

/** Open a specific URL (a per-post "expand links" action) in a Custom Tab. */
fun openLink(url: String) {
if (url.isNotEmpty()) _openUrls.tryEmit(url)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ fun HomeScreen(
scrollRequest = scrollRequest?.takeIf { it.tab == pageIndex },
actionOrder = postActionOrder,
onOpenLink = viewModel::openLink,
onOpenLinks = viewModel::openPostLinks,
onLoadOlder = { viewModel.loadOlder(automatic = true) },
onNoteSelection = viewModel::noteSelection,
onOpenThread = viewModel::openThread,
Expand Down Expand Up @@ -605,6 +606,27 @@ fun HomeScreen(
)
}

val linkPicker by viewModel.linkPicker.collectAsStateWithLifecycle()
linkPicker?.let { links ->
AlertDialog(
onDismissRequest = viewModel::dismissLinkPicker,
title = { Text("Which link?") },
text = {
Column {
links.forEach { link ->
TextButton(onClick = { viewModel.openPickedLink(link) }) {
Text(link.title.ifBlank { link.url })
}
}
}
},
confirmButton = {},
dismissButton = {
TextButton(onClick = viewModel::dismissLinkPicker) { Text("Cancel") }
},
)
}

val hashtagPicker by viewModel.hashtagTimelinePicker.collectAsStateWithLifecycle()
hashtagPicker?.let { tags ->
AlertDialog(
Expand Down Expand Up @@ -765,6 +787,7 @@ private fun StatusList(
scrollRequest: CoreViewModel.ScrollRequest?,
actionOrder: List<String>,
onOpenLink: (String) -> Unit,
onOpenLinks: (String) -> Unit,
onLoadOlder: () -> Unit,
onNoteSelection: (String) -> Unit,
onOpenThread: (String) -> Unit,
Expand Down Expand Up @@ -875,6 +898,7 @@ private fun StatusList(
actionOrder = actionOrder,
onSelect = onNoteSelection,
onOpenLink = onOpenLink,
onOpenLinks = onOpenLinks,
onOpenThread = onOpenThread,
onOpenAuthor = onOpenAuthor,
onOpenProfile = onOpenProfile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import me.masonasons.fastsm.ui.RowUi
*/
// The Android-supported post actions, in default order (View Media first).
// A subset of the shared post_action_catalog(): the actions StatusRow has a
// handler for. Keys not here (post_info, links, browser, followers, following,
// handler for. Keys not here (post_info, browser, followers, following,
// follow_hashtag, pin_post) aren't wired on Android yet.
private val DEFAULT_POST_ACTION_ORDER = listOf(
"play_media", "reply", "boost", "favorite", "bookmark", "quote", "thread",
Expand All @@ -51,6 +51,7 @@ fun StatusRow(
/** Acting on a row makes it the reading position (the core persists it). */
onSelect: (String) -> Unit,
onOpenLink: (String) -> Unit,
onOpenLinks: (String) -> Unit,
onOpenThread: (String) -> Unit,
onOpenAuthor: (String) -> Unit,
onOpenProfile: (String) -> Unit,
Expand Down Expand Up @@ -101,6 +102,9 @@ fun StatusRow(
"bookmark" ->
MenuAction(if (row.bookmarked) "Remove bookmark" else "Bookmark") { onToggleBookmark(row.id) }
"quote" -> MenuAction("Quote") { onQuote(row.id) }
// Offered on every post, as on iPhone: the core says when a post
// has no links, opens a single one, and asks which of several.
"links" -> MenuAction("Open links") { onOpenLinks(row.id) }
"play_media" -> if (row.hasMedia) MenuAction("View media") { onViewMedia(row.id) } else null
"thread" -> MenuAction("View conversation") { onOpenThread(row.id) }
"mute_conversation" ->
Expand Down
2 changes: 1 addition & 1 deletion docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FastSMRW changelog

0.6.1
-----

Fixed: on Android, the Open links action now appears in a post's actions when it is turned on in Settings; it opens the post's link, or asks which one when there are several.
- New: on Android, notifications for mentions now have Reply, Favourite and Boost buttons, so you can answer or react without opening the app.
- Fixed: on Android, FastSMRW now installs on 32-bit phones, which previously refused it with "app not compatible" and no way around it.
- Changed: @-mention autocomplete now suggests people you've seen go past in your timelines, instantly and without waiting on the server, and matches on display names as well as handles.
Expand Down
Loading