Skip to content

Commit a0c1f2c

Browse files
authored
Display room invitation notification (#735)
* Notifications: Add some extra mappings so we keep the original contents and can pass it later to an UI layer * Fix notifications not appearing for a room if the app was on that room when it went to background. * Modernize how we create spannable strings for notifications, remove unneeded dependency * Remove actions from invite notifications temporarily * Add `NotificationDrawerManager` interface to be able to clear membership notifications when accepting or rejecting a room invite * Fix tests * Add comment to clarify some weird behaviours * Address review comments * Set circle shape for `largeBitmap` in message notifications * Fix no avatar in DM rooms * Fix rebase issues * Add invite list pending intent: - Refactor pending intents. - Make `DeepLinkData` a sealed interface. - Fix and add tests. * Rename `navigate__` functions to `attach__` * Add an extra test case for the `InviteList` deep link * Address most review comments. * Fix rebase issue * Add fallback notification type, allow dismissing invite notifications. Fallback notifications have a different underlying type and can be dismissed at will. * Fix tests
1 parent 0fbf799 commit a0c1f2c

File tree

55 files changed

+905
-327
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+905
-327
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@
4444

4545
<category android:name="android.intent.category.LAUNCHER" />
4646
</intent-filter>
47-
<!-- Handle deep-link for notification, uncomment to be able to test deeplink with ./tools/adb/deeplink.sh -->
48-
<!--intent-filter>
47+
<!-- Handle deep-link for notification ./tools/adb/deeplink.sh -->
48+
<intent-filter>
4949
<action android:name="android.intent.action.VIEW" />
5050
<category android:name="android.intent.category.DEFAULT" />
5151
<data
5252
android:host="open"
5353
android:scheme="elementx" />
54-
</intent-filter-->
54+
</intent-filter>
5555
<intent-filter>
5656
<action android:name="android.intent.action.VIEW" />
5757

app/src/main/kotlin/io/element/android/x/ElementXApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ package io.element.android.x
1818

1919
import android.app.Application
2020
import androidx.startup.AppInitializer
21-
import io.element.android.x.di.AppComponent
2221
import io.element.android.libraries.di.DaggerComponentOwner
22+
import io.element.android.x.di.AppComponent
2323
import io.element.android.x.di.DaggerAppComponent
2424
import io.element.android.x.info.logApplicationInfo
2525
import io.element.android.x.initializer.CrashInitializer

app/src/main/kotlin/io/element/android/x/intent/IntentProviderImpl.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,21 @@ class IntentProviderImpl @Inject constructor(
3535
@ApplicationContext private val context: Context,
3636
private val deepLinkCreator: DeepLinkCreator,
3737
) : IntentProvider {
38-
override fun getViewIntent(
38+
override fun getViewRoomIntent(
3939
sessionId: SessionId,
4040
roomId: RoomId?,
4141
threadId: ThreadId?,
4242
): Intent {
4343
return Intent(context, MainActivity::class.java).apply {
4444
action = Intent.ACTION_VIEW
45-
data = deepLinkCreator.create(sessionId, roomId, threadId).toUri()
45+
data = deepLinkCreator.room(sessionId, roomId, threadId).toUri()
46+
}
47+
}
48+
49+
override fun getInviteListIntent(sessionId: SessionId): Intent {
50+
return Intent(context, MainActivity::class.java).apply {
51+
action = Intent.ACTION_VIEW
52+
data = deepLinkCreator.inviteList(sessionId).toUri()
4653
}
4754
}
4855
}

appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ package io.element.android.appnav
1818

1919
import android.os.Parcelable
2020
import androidx.compose.foundation.layout.Box
21-
import androidx.compose.foundation.layout.fillMaxSize
2221
import androidx.compose.runtime.Composable
23-
import androidx.compose.ui.Alignment
2422
import androidx.compose.ui.Modifier
2523
import androidx.lifecycle.Lifecycle
2624
import androidx.lifecycle.lifecycleScope
@@ -30,7 +28,6 @@ import com.bumble.appyx.core.composable.Children
3028
import com.bumble.appyx.core.lifecycle.subscribe
3129
import com.bumble.appyx.core.modality.BuildContext
3230
import com.bumble.appyx.core.node.Node
33-
import com.bumble.appyx.core.node.node
3431
import com.bumble.appyx.core.plugin.Plugin
3532
import com.bumble.appyx.core.plugin.plugins
3633
import com.bumble.appyx.navmodel.backstack.BackStack
@@ -58,13 +55,15 @@ import io.element.android.libraries.architecture.animation.rememberDefaultTransi
5855
import io.element.android.libraries.architecture.bindings
5956
import io.element.android.libraries.architecture.createNode
6057
import io.element.android.libraries.architecture.inputs
58+
import io.element.android.libraries.deeplink.DeeplinkData
6159
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
6260
import io.element.android.libraries.di.AppScope
6361
import io.element.android.libraries.matrix.api.MatrixClient
6462
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
6563
import io.element.android.libraries.matrix.api.core.RoomId
6664
import io.element.android.libraries.matrix.api.sync.SyncState
6765
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
66+
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
6867
import io.element.android.services.analytics.api.AnalyticsService
6968
import io.element.android.services.appnavstate.api.AppNavigationStateService
7069
import kotlinx.coroutines.CoroutineScope
@@ -89,6 +88,7 @@ class LoggedInFlowNode @AssistedInject constructor(
8988
private val analyticsService: AnalyticsService,
9089
private val coroutineScope: CoroutineScope,
9190
private val networkMonitor: NetworkMonitor,
91+
private val notificationDrawerManager: NotificationDrawerManager,
9292
snackbarDispatcher: SnackbarDispatcher,
9393
) : BackstackNode<LoggedInFlowNode.NavTarget>(
9494
backstack = BackStack(
@@ -340,4 +340,13 @@ class LoggedInFlowNode @AssistedInject constructor(
340340
PermanentChild(navTarget = NavTarget.Permanent)
341341
}
342342
}
343+
344+
internal suspend fun attachRoom(deeplinkData: DeeplinkData.Room) {
345+
backstack.push(NavTarget.Room(deeplinkData.roomId))
346+
}
347+
348+
internal suspend fun attachInviteList(deeplinkData: DeeplinkData.InviteList) {
349+
notificationDrawerManager.clearMembershipNotificationForSession(deeplinkData.sessionId)
350+
backstack.push(NavTarget.InviteList)
351+
}
343352
}

appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,10 @@ class RootFlowNode @AssistedInject constructor(
253253
Timber.d("Navigating to $deeplinkData")
254254
attachSession(deeplinkData.sessionId)
255255
.apply {
256-
val roomId = deeplinkData.roomId
257-
if (roomId == null) {
258-
// In case room is not provided, ensure the app navigate back to the room list
259-
attachRoot()
260-
} else {
261-
attachRoom(roomId)
262-
// TODO .attachThread(deeplinkData.threadId)
256+
when (deeplinkData) {
257+
is DeeplinkData.Root -> attachRoot()
258+
is DeeplinkData.Room -> attachRoom(deeplinkData)
259+
is DeeplinkData.InviteList -> attachInviteList(deeplinkData)
263260
}
264261
}
265262
}

appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import io.element.android.features.login.api.oidc.OidcAction
2121
import io.element.android.features.login.api.oidc.OidcIntentResolver
2222
import io.element.android.libraries.deeplink.DeeplinkData
2323
import io.element.android.libraries.deeplink.DeeplinkParser
24+
import io.element.android.libraries.matrix.api.core.RoomId
25+
import io.element.android.libraries.matrix.api.core.SessionId
2426
import timber.log.Timber
2527
import javax.inject.Inject
2628

features/invitelist/impl/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ dependencies {
4343
implementation(projects.libraries.designsystem)
4444
implementation(projects.libraries.uiStrings)
4545
implementation(projects.services.analytics.api)
46+
implementation(projects.libraries.push.api)
4647

4748
testImplementation(libs.test.junit)
4849
testImplementation(libs.coroutines.test)
4950
testImplementation(libs.molecule.runtime)
5051
testImplementation(libs.test.truth)
5152
testImplementation(libs.test.turbine)
5253
testImplementation(projects.libraries.matrix.test)
54+
testImplementation(projects.libraries.push.test)
5355
testImplementation(projects.features.invitelist.test)
5456
testImplementation(projects.features.analytics.test)
5557

features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
3737
import io.element.android.libraries.matrix.api.MatrixClient
3838
import io.element.android.libraries.matrix.api.core.RoomId
3939
import io.element.android.libraries.matrix.api.room.RoomSummary
40+
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
4041
import io.element.android.services.analytics.api.AnalyticsService
4142
import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom
4243
import kotlinx.collections.immutable.toPersistentList
@@ -49,6 +50,7 @@ class InviteListPresenter @Inject constructor(
4950
private val client: MatrixClient,
5051
private val store: SeenInvitesStore,
5152
private val analyticsService: AnalyticsService,
53+
private val notificationDrawerManager: NotificationDrawerManager,
5254
) : Presenter<InviteListState> {
5355

5456
@Composable
@@ -138,6 +140,7 @@ class InviteListPresenter @Inject constructor(
138140
suspend {
139141
client.getRoom(roomId)?.use {
140142
it.acceptInvitation().getOrThrow()
143+
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
141144
analyticsService.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
142145
}
143146
roomId
@@ -148,7 +151,9 @@ class InviteListPresenter @Inject constructor(
148151
suspend {
149152
client.getRoom(roomId)?.use {
150153
it.rejectInvitation().getOrThrow()
151-
} ?: Unit
154+
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
155+
}
156+
Unit
152157
}.runCatchingUpdatingState(declinedAction)
153158
}
154159

0 commit comments

Comments
 (0)