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
15 changes: 15 additions & 0 deletions apps/flipcash/core/src/androidMain/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,21 @@
<string name="title_shareProfileToChats">Share Profile</string>
<string name="title_shareMessageToChats">Share Message</string>
<string name="action_forwardMessage">Forward</string>
<string name="action_pinMessage">Pin</string>
<string name="action_unpinMessage">Unpin</string>
<string name="action_jumpToMessage">Jump to message</string>
<string name="title_pinnedMessage">Pinned message</string>
<string name="cd_pinnedMessageFrom">Pinned message from %1$s: %2$s</string>
<string name="label_pinnedEncryptedMessage">Encrypted message</string>
<string name="title_pinnedMessages">Pinned Messages</string>
<string name="label_pinnedBy">Pinned by %1$s · %2$s</string>
<string name="label_pinnedByYou">you</string>
<string name="label_pinsFull">Pinned messages are full. Unpin one to pin \"%1$s\"</string>
<string name="label_pinsFullUntitled">Pinned messages are full. Unpin one to pin this message</string>
<string name="title_pinNotAdded">Couldn\'t pin message</string>
<string name="title_unpinFailed">Couldn\'t unpin message</string>
<string name="title_messageUnreachable">Couldn\'t find that message</string>
<string name="cd_openPinnedMessages">Pinned messages</string>
<!-- Who the share sheet is for: display name, then @handle -->
<string name="action_showProfileCard">Show Profile Card</string>
<string name="action_copyLink">Copy Link</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ enum class InviteLinkAction : Parcelable { Share, Revoke }
@Serializable
data class NewInviteLinkChoice(val expiry: String, val uses: String) : Parcelable

/** The pinned message tapped in [ChatStep.PinnedMessages]: the chat jumps to it once the list closes. */
@Parcelize
@Serializable
data class PinnedMessageJump(val messageId: Long) : Parcelable

/** A custom paid-reaction amount, in whole US cents. */
@Parcelize
@Serializable
Expand Down Expand Up @@ -357,6 +362,21 @@ sealed interface ChatStep : FlowStep, Parcelable {
@Parcelize
@Serializable
data class Reactors(val chatId: ChatId, val messageId: Long) : ChatStep, Sheet, HalfSheet

/**
* The chat's pinned messages, newest first (L1). The pins are the conversation's, read when the
* chat opened; the route names the chat only. [pinAfterUnpin] is set when it opened because the
* chat is at its pin cap (L1b): that message is pinned once an unpin frees a slot.
*
* Returns the tapped row as a [PinnedMessageJump]; the conversation jumps to it.
*/
@Parcelize
@Serializable
data class PinnedMessages(val chatId: ChatId, val pinAfterUnpin: Long? = null) :
ChatStep,
NavigationRetVal<PinnedMessageJump>,
Sheet,
HalfSheet
}

/** What opened a [ChatStep.Profile], which decides the actions it offers. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
Expand Down Expand Up @@ -44,6 +45,10 @@ import com.flipcash.app.messenger.internal.ChatViewModel
import com.flipcash.app.messenger.internal.GroupInviteViewModel
import com.flipcash.app.core.chat.InviteLinkAction
import com.flipcash.app.core.chat.NewInviteLinkChoice
import com.flipcash.app.core.chat.PinnedMessageJump
import com.flipcash.app.messenger.internal.pins.PinnedRow
import com.flipcash.app.messenger.internal.pins.pinnedListDone
import com.flipcash.app.messenger.internal.screens.components.PinnedMessagesSheet
import com.flipcash.app.messenger.internal.invites.InviteLinkActions
import com.flipcash.app.messenger.internal.invites.NewInviteLinkContent
import com.flipcash.app.messenger.internal.invites.toChoice
Expand Down Expand Up @@ -334,6 +339,9 @@ private fun chatEntryProvider(
annotatedEntry<ChatStep.Reactors> { step ->
FlowReactorsScreen(step.chatId, step.messageId)
}
annotatedEntry<ChatStep.PinnedMessages> { step ->
FlowPinnedMessagesSheet(step.pinAfterUnpin)
}
}

@Composable
Expand Down Expand Up @@ -837,6 +845,39 @@ private fun FlowReactionPickerScreen(messageId: Long) {
)
}

/**
* [ChatStep.PinnedMessages]: the conversation's pins on the app's sheet scene (L1, L1c, and L1b when
* [pinAfterUnpin] is set). The pins are the conversation's own, so this reads its view model; a
* tapped row closes the sheet and returns to the conversation as a [PinnedMessageJump].
*/
@Composable
private fun FlowPinnedMessagesSheet(pinAfterUnpin: Long?) {
val viewModel = flowSharedViewModel<ChatViewModel>()
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val dismissSheet = LocalBottomSheetDismissDispatcher.current
val resultBack = resultBackNavigator<PinnedMessageJump>(exit = dismissSheet)

// Built off the main thread: a cash row looks up its token before it can draw.
val rows by produceState<List<PinnedRow>?>(null, state.pins) { value = viewModel.pinnedRows(state.pins) }
// The last rows stay up while the sheet slides away, so it never empties on the way out.
var shownRows by remember { mutableStateOf<List<PinnedRow>>(emptyList()) }
rows?.takeIf { it.isNotEmpty() }?.let { shownRows = it }

// The last unpin, or the waiting message landing in the slot it freed, leaves nothing to do here.
val done = state.pins.pinnedListDone(pinAfterUnpin)
LaunchedEffect(done) { if (done) dismissSheet() }

PinnedMessagesSheet(
rows = shownRows,
canUnpin = state.canPin,
full = pinAfterUnpin != null,
fullQuote = state.waitingPin?.takeIf { it.messageId == pinAfterUnpin }?.quote,
onOpen = { resultBack.returnValue(PinnedMessageJump(it)) },
onUnpin = { viewModel.dispatchEvent(ChatViewModel.Event.UnpinMessage(it, pinAfter = pinAfterUnpin)) },
onClose = { dismissSheet() },
)
}

/**
* Who reacted to [messageId], and with what, from the entry's own [ReactorsViewModel]. Tapping a
* named row dismisses this sheet, then opens that person's profile on the chat.
Expand Down
Loading
Loading