Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import com.flipcash.app.messenger.CurrencySendScreen
import com.flipcash.app.messenger.internal.screens.profile.GroupInviteScreen
import com.flipcash.app.messenger.E2eeLearnMoreScreen
import com.flipcash.app.messenger.MuteChatScreen
import com.flipcash.app.messenger.MuteChatsScreen
import com.flipcash.app.messenger.SilenceUserScreen
import com.flipcash.app.messenger.ProfileScreen
import com.flipcash.app.messenger.SharePickerScreen
Expand Down Expand Up @@ -185,6 +186,7 @@ internal fun appEntryProvider(
annotatedEntry<AppRoute.Messaging.ShareProfileToChats> { key -> ShareProfileToChatsScreen(key) }
annotatedEntry<AppRoute.Messaging.ShareCurrencyToChats> { key -> ShareCurrencyToChatsScreen(key) }
annotatedEntry<AppRoute.Messaging.MuteChat> { key -> MuteChatScreen(key.chatId, key.chatType) }
annotatedEntry<AppRoute.Messaging.MuteChats> { key -> MuteChatsScreen(key.chatIds, key.chatTypes) }
annotatedEntry<AppRoute.Messaging.SilenceUser> { key ->
SilenceUserScreen(key.chatId, key.userId, key.firstName, key.groupTitle)
}
Expand Down
21 changes: 21 additions & 0 deletions apps/flipcash/core/src/androidMain/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,27 @@
<string name="content_description_archiveChat">Archive chat</string>
<string name="content_description_unarchiveChat">Unarchive chat</string>

<!-- Mark as read / unread on the chat list: the leading swipe, select mode and the "…" menu. -->
<string name="action_markAsUnread">Mark as Unread</string>
<string name="action_markAsRead">Mark as Read</string>
<string name="action_unread">Unread</string>
<string name="action_read">Read</string>
<string name="action_readAll">Read All</string>
<string name="action_selectChats">Select Chats</string>
<string name="action_mute">Mute</string>
<string name="action_unmute">Unmute</string>
<string name="action_archive">Archive</string>
<string name="action_unarchive">Unarchive</string>
<string name="action_closeChatSelection">Close</string>
<string name="content_description_moreChatActions">More</string>
<string name="title_chatsSelected">%1$d Selected</string>
<plurals name="message_chatsArchived">
<item quantity="one">Chat archived</item>
<item quantity="other">%1$d chats archived</item>
</plurals>
<string name="prompt_title_readAllChats">Mark All Chats as Read?</string>
<string name="prompt_description_readAllChats">Everyone in these chats will see that you\'ve read their messages</string>

<!-- Starting a chat by @handle from the Chats tab's "+" (nodes 9442:5825 / 9442:103386). -->
<string name="title_newChat">Enter Flipcash username</string>
<string name="subtitle_newChat">Enter the Flipcash username of the person you want to chat with</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,18 @@ sealed interface AppRoute : NavKey, Parcelable {
com.getcode.navigation.Sheet,
com.getcode.navigation.WrapContentSheet

/**
* The mute sheet for several chats at once, from the chat list's select mode: the duration
* picked applies to every one of [chatIds], including any already muted. [chatTypes] pairs
* with [chatIds] by index, for the per-chat mute events. Presented like [MuteChat].
*/
@Serializable
@Parcelize
data class MuteChats(val chatIds: List<ChatId>, val chatTypes: List<ChatType>) :
Messaging,
com.getcode.navigation.Sheet,
com.getcode.navigation.WrapContentSheet

/**
* How long to silence [userId] in the group [chatId]. Carries the words the sheet shows
* ([firstName], [groupTitle]) so it needs no lookup before it can draw. Presented like
Expand Down
4 changes: 4 additions & 0 deletions apps/flipcash/features/chat-list/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ kotlin {
commonMain.dependencies {
implementation(project(":apps:flipcash:core"))
implementation(project(":apps:flipcash:core-ui"))
implementation(project(":apps:flipcash:shared:analytics"))
implementation(project(":apps:flipcash:shared:chat"))
implementation(project(":apps:flipcash:shared:chat-ui"))
implementation(project(":apps:flipcash:shared:common-ui"))
Expand Down Expand Up @@ -42,20 +43,23 @@ kotlin {
androidMain.dependencies {
// What flipcash.android.feature supplied before the move to KMP.
implementation(libs.bundles.hilt)
implementation(project(":libs:messaging"))
implementation(project.dependencies.platform(libs.compose.bom))
implementation(libs.bundles.compose)
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
implementation(libs.mockk)
implementation(project(":apps:flipcash:shared:analytics:test-fixtures"))
}
getByName("androidHostTest").dependencies {
implementation(kotlin("test"))
implementation(libs.bundles.unit.testing)
implementation(libs.robolectric)
implementation(project(":ui:resources:test-fixtures"))
implementation(project(":libs:test-utils"))
implementation(project(":apps:flipcash:shared:analytics:test-fixtures"))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.flipcash.app.tipping

import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.getUnclippedBoundsInRoot
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.unit.dp
import com.flipcash.app.theme.FlipcashPreview
import com.getcode.ui.components.rememberSwipeRevealGroup
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import kotlin.test.assertEquals
import kotlin.test.assertTrue

/**
* The main list's trailing swipe: mute inside, archive outermost, and a full swipe archives.
* The gesture is the one a device sees: a slow partial drag that leaves both actions revealed,
* then a fast full swipe from there.
*/
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(sdk = [34], qualifiers = "w400dp-h800dp-xhdpi")
class ChatSwipeRowTest {

@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()

private var archived = 0
private var muted = 0

private fun show() {
composeRule.setContent {
FlipcashPreview(showBackground = true) {
ChatSwipeRow(
isMuted = false,
isUnread = false,
swipeEnabled = true,
onToggleRead = {},
onMute = { muted++ },
onArchive = { archived++ },
stateKey = "chat",
revealGroup = rememberSwipeRevealGroup(),
) {
Box(Modifier.fillMaxWidth().height(72.dp).testTag(ROW))
}
}
}
composeRule.waitForIdle()
}

private fun rowLeft() = composeRule.onNodeWithTag(ROW).getUnclippedBoundsInRoot().left

@Test
fun `archive is outermost and a full swipe from the revealed row archives`() {
show()

composeRule.onNodeWithTag(ROW).performTouchInput {
swipeLeft(startX = right - 1f, endX = right - 120.dp.toPx(), durationMillis = 1_500)
}
composeRule.waitForIdle()
assertTrue(rowLeft() < 0.dp, "the short drag should leave the row revealed")
assertEquals(0, archived)
assertEquals(0, muted)

val mute = composeRule.onNodeWithContentDescription(MUTE).getUnclippedBoundsInRoot()
val archive = composeRule.onNodeWithContentDescription(ARCHIVE).getUnclippedBoundsInRoot()
assertTrue(archive.left > mute.left, "archive should sit outside mute, at the trailing edge")

composeRule.onNodeWithTag(ROW).performTouchInput {
swipeLeft(startX = right - 1f, endX = left, durationMillis = 120)
}
composeRule.waitForIdle()

assertEquals(1, archived)
assertEquals(0, muted)
}

private companion object {
const val ROW = "row"
const val MUTE = "Mute notifications"
const val ARCHIVE = "Archive chat"
}
}
Loading
Loading