Merge branch 'release/0.6.0' into main
This commit is contained in:
commit
e80cfe4a29
36 changed files with 297 additions and 103 deletions
2
.github/workflows/sync-localazy.yml
vendored
2
.github/workflows/sync-localazy.yml
vendored
|
|
@ -36,7 +36,7 @@ jobs:
|
|||
./tools/localazy/importSupportedLocalesFromLocalazy.py
|
||||
./tools/test/generateAllScreenshots.py
|
||||
- name: Create Pull Request for Strings
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
commit-message: Sync Strings from Localazy
|
||||
|
|
|
|||
2
.github/workflows/sync-sas-strings.yml
vendored
2
.github/workflows/sync-sas-strings.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
- name: Run SAS String script
|
||||
run: ./tools/sas/import_sas_strings.py
|
||||
- name: Create Pull Request for SAS Strings
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
commit-message: Sync SAS Strings
|
||||
title: Sync SAS Strings
|
||||
|
|
|
|||
2
.idea/dictionaries/shared.xml
generated
2
.idea/dictionaries/shared.xml
generated
|
|
@ -1,6 +1,7 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="shared">
|
||||
<words>
|
||||
<w>agpl</w>
|
||||
<w>backstack</w>
|
||||
<w>blurhash</w>
|
||||
<w>fdroid</w>
|
||||
|
|
@ -17,6 +18,7 @@
|
|||
<w>securebackup</w>
|
||||
<w>showkase</w>
|
||||
<w>snackbar</w>
|
||||
<w>spdx</w>
|
||||
<w>swipeable</w>
|
||||
<w>textfields</w>
|
||||
<w>tombstoned</w>
|
||||
|
|
|
|||
32
CHANGES.md
32
CHANGES.md
|
|
@ -1,3 +1,35 @@
|
|||
Changes in Element X v0.5.3 (2024-09-10)
|
||||
========================================
|
||||
|
||||
### ✨ Features
|
||||
* Add banner for optional migration to simplified sliding sync by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3429
|
||||
|
||||
### 🙌 Improvements
|
||||
* Timeline : remove the encrypted history banner by @ganfra in https://github.com/element-hq/element-x-android/pull/3410
|
||||
|
||||
### 🐛 Bugfixes
|
||||
* Fix new logins with Simplified SS using the proxy by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3417
|
||||
* Ensure Call is not hang up when user is asked to grant system permissions by @bmarty in https://github.com/element-hq/element-x-android/pull/3419
|
||||
* Wait for a room with joined state in `/sync` after creating it by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3421
|
||||
* [Bugfix] : fix self verification flow by @ganfra in https://github.com/element-hq/element-x-android/pull/3426
|
||||
|
||||
### 🗣 Translations
|
||||
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3425
|
||||
|
||||
### 🚧 In development 🚧
|
||||
* [Feature] Pinned messages list by @ganfra in https://github.com/element-hq/element-x-android/pull/3392
|
||||
* Pinned messages banner : adjust indicator to match design. by @ganfra in https://github.com/element-hq/element-x-android/pull/3415
|
||||
|
||||
### Dependency upgrades
|
||||
* Update plugin dependencycheck to v10.0.4 by @renovate in https://github.com/element-hq/element-x-android/pull/3372
|
||||
* Update plugin detekt to v1.23.7 by @renovate in https://github.com/element-hq/element-x-android/pull/3424
|
||||
|
||||
### Others
|
||||
* Delete old log files by @bmarty in https://github.com/element-hq/element-x-android/pull/3413
|
||||
* Recovery key formatting and wording iteration by @bmarty in https://github.com/element-hq/element-x-android/pull/3409
|
||||
* Change license to AGPL by @bmarty in https://github.com/element-hq/element-x-android/pull/3422
|
||||
* Remove Wait list screen by @bmarty in https://github.com/element-hq/element-x-android/pull/3428
|
||||
|
||||
Changes in Element X v0.5.2 (2024-09-05)
|
||||
=========================================
|
||||
|
||||
|
|
|
|||
2
fastlane/metadata/android/en-US/changelogs/40006000.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40006000.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Element X is the new generation of Element for professional and personal use on mobile. It’s the fastest Matrix client with a seamless & intuitive user interface.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
|
|
@ -1,23 +1,35 @@
|
|||
Element X is the future Element.
|
||||
Element X brings you both sovereign & seamless collaboration built on Matrix.
|
||||
|
||||
It is the brand new, and fastest ever, Matrix client. It is for personal and community use, and will support enterprise functionality later this year.
|
||||
The collaboration capabilities include chat & video calls with the modern set of features such as:
|
||||
• public & private channels
|
||||
• room moderation & access conUpdatetrol
|
||||
• replies, reactions, polls, read receipts, pinned messages, etc.
|
||||
• simultaneous chat & calls (picture in picture)
|
||||
• decentralized & federated communication across organizations
|
||||
|
||||
A complete new build, Element X transforms performance. It’s not just the fastest Matrix client, it’s also fresher and more reliable.
|
||||
All this comes in a secure & sovereign fashion without compromising responsiveness or overall usability of the app:
|
||||
• enterprise-grade single sign-on
|
||||
• easy & secure login & device verification via QR-code
|
||||
• end to end encryption & zero trust
|
||||
• protection against MITM & other cyber attacks
|
||||
|
||||
It’s so fast for a number of reasons, but in particular we’ve introduced a completely new syncing service (‘sliding sync’). So even in big end-to-end encrypted chat rooms it operates incredibly quickly.
|
||||
If you’re a new user, use the new Element X app from the start. Compared to the current Element app you will get:
|
||||
• greatly enhanced performance, sleek user interface and overall better user experience
|
||||
• enterprise-grade support for single sign-on (OIDC)
|
||||
• QR-code based login & device verification
|
||||
• natively integrated Element Call for video calls
|
||||
• continuous improvements, bug fixes and new features
|
||||
|
||||
It’s fresher because we’ve rebuilt the entire user experience. All the power of Matrix - and the complexity of decentralized end-to-end encryption - is now hidden under a beautiful and intuitive user interface using the very latest frameworks and accessibility features.
|
||||
|
||||
Element X delivers speed, usability and reliability on the decentralized Matrix open standard.
|
||||
If you’re an existing user, using the current Element app - check out the new Element X and start planning your transition. The current Element app will be phased out and will only get critical security updates.
|
||||
|
||||
<b>Own your data</b>
|
||||
Matrix-based, Element X lets you self-host your data or choose from any free public server (the default is matrix.org, but there are plenty of others to choose from). However you host, you have ownership; it’s your data. You’re not the product. You’re in control.
|
||||
|
||||
<b>Interoperate natively</b>
|
||||
Enjoy the freedom of the Matrix open standard! You have native interoperability with any other Matrix-based app. So just like email, it doesn't matter if your friends are on a different Matrix-based app you can still connect and chat.
|
||||
Enjoy the freedom of the Matrix open standard! You have native interoperability with any other Matrix-based app. So just like email, it doesn't matter if your friends, partners or customers are on a different Matrix-based app - you can still connect.
|
||||
|
||||
<b>Encrypt your data</b>
|
||||
Enjoy your right to private conversations - free from data mining, ads and all the rest of it - and stay secure. Only the people in your conversation can read your messages. And Element X E2EE applies to voice and video calls too.
|
||||
Enjoy your right to private conversations - free from data mining, ads and all the rest of it - and stay secure. Only the people in your conversation can read your messages.
|
||||
|
||||
<b>Chat across multiple devices</b>
|
||||
Stay in touch wherever you are with fully synchronized message history across all your devices, even those running ‘traditional’ Element, and on the web at https://app.element.io
|
||||
Stay in touch wherever you are with fully synchronized message history across all your devices, even those running Element legacy app, and on the web at https://app.element.io
|
||||
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
Normal file
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
|
|
@ -1 +1 @@
|
|||
Fastest ever Matrix client
|
||||
Sovereign. Seamless. On Matrix
|
||||
|
|
@ -1 +1 @@
|
|||
Element X - Secure messenger
|
||||
Element X - Secure Chat & Call
|
||||
|
|
@ -18,11 +18,13 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -32,7 +34,8 @@ class PinnedEventsTimelineProvider @Inject constructor(
|
|||
private val networkMonitor: NetworkMonitor,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
) : TimelineProvider {
|
||||
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> = MutableStateFlow(AsyncData.Uninitialized)
|
||||
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> =
|
||||
MutableStateFlow(AsyncData.Uninitialized)
|
||||
|
||||
override fun activeTimelineFlow(): StateFlow<Timeline?> {
|
||||
return _timelineStateFlow
|
||||
|
|
@ -44,25 +47,46 @@ class PinnedEventsTimelineProvider @Inject constructor(
|
|||
val timelineStateFlow = _timelineStateFlow
|
||||
|
||||
fun launchIn(scope: CoroutineScope) {
|
||||
_timelineStateFlow.subscriptionCount
|
||||
.map { count -> count > 0 }
|
||||
.distinctUntilChanged()
|
||||
.onEach { isActive ->
|
||||
if (isActive) {
|
||||
onActive()
|
||||
} else {
|
||||
onInactive()
|
||||
}
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
private suspend fun onActive() = coroutineScope {
|
||||
combine(
|
||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinnedEvents),
|
||||
networkMonitor.connectivity
|
||||
) {
|
||||
// do not use connectivity here as data can be loaded from cache, it's just to trigger retry if needed
|
||||
isEnabled, _ ->
|
||||
) { isEnabled, _ ->
|
||||
// do not use connectivity here as data can be loaded from cache, it's just to trigger retry if needed
|
||||
isEnabled
|
||||
}
|
||||
.onEach { isFeatureEnabled ->
|
||||
if (isFeatureEnabled) {
|
||||
loadTimelineIfNeeded()
|
||||
} else {
|
||||
_timelineStateFlow.value = AsyncData.Uninitialized
|
||||
resetTimeline()
|
||||
}
|
||||
}
|
||||
.onCompletion {
|
||||
invokeOnTimeline { close() }
|
||||
}
|
||||
.launchIn(scope)
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private suspend fun onInactive() {
|
||||
resetTimeline()
|
||||
}
|
||||
|
||||
private suspend fun resetTimeline() {
|
||||
invokeOnTimeline {
|
||||
close()
|
||||
}
|
||||
_timelineStateFlow.emit(AsyncData.Uninitialized)
|
||||
}
|
||||
|
||||
suspend fun invokeOnTimeline(action: suspend Timeline.() -> Unit) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -27,6 +26,7 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc
|
|||
import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
@ -55,16 +55,23 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||
class PinnedMessagesListPresenter @AssistedInject constructor(
|
||||
@Assisted private val navigator: PinnedMessagesListNavigator,
|
||||
private val room: MatrixRoom,
|
||||
private val timelineItemsFactory: TimelineItemsFactory,
|
||||
timelineItemsFactoryCreator: TimelineItemsFactory.Creator,
|
||||
private val timelineProvider: PinnedEventsTimelineProvider,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
actionListPresenterFactory: ActionListPresenter.Factory,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
) : Presenter<PinnedMessagesListState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(navigator: PinnedMessagesListNavigator): PinnedMessagesListPresenter
|
||||
}
|
||||
|
||||
private val timelineItemsFactory: TimelineItemsFactory = timelineItemsFactoryCreator.create(
|
||||
config = TimelineItemsFactoryConfig(
|
||||
computeReadReceipts = false,
|
||||
computeReactions = false,
|
||||
)
|
||||
)
|
||||
private val actionListPresenter = actionListPresenterFactory.create(PinnedMessagesListTimelineActionPostProcessor())
|
||||
|
||||
@Composable
|
||||
|
|
@ -93,10 +100,9 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
|
|||
}
|
||||
)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
fun handleEvents(event: PinnedMessagesListEvents) {
|
||||
when (event) {
|
||||
is PinnedMessagesListEvents.HandleAction -> coroutineScope.handleTimelineAction(event.action, event.event)
|
||||
is PinnedMessagesListEvents.HandleAction -> appCoroutineScope.handleTimelineAction(event.action, event.event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import dagger.assisted.AssistedFactory
|
|||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.messages.impl.MessagesNavigator
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
||||
import io.element.android.features.messages.impl.timeline.model.NewEventState
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
|
||||
|
|
@ -54,7 +55,7 @@ import kotlinx.coroutines.withContext
|
|||
const val FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS = 200L
|
||||
|
||||
class TimelinePresenter @AssistedInject constructor(
|
||||
private val timelineItemsFactory: TimelineItemsFactory,
|
||||
timelineItemsFactoryCreator: TimelineItemsFactory.Creator,
|
||||
private val timelineItemIndexer: TimelineItemIndexer,
|
||||
private val room: MatrixRoom,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
|
|
@ -71,6 +72,13 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
fun create(navigator: MessagesNavigator): TimelinePresenter
|
||||
}
|
||||
|
||||
private val timelineItemsFactory: TimelineItemsFactory = timelineItemsFactoryCreator.create(
|
||||
config = TimelineItemsFactoryConfig(
|
||||
computeReadReceipts = true,
|
||||
computeReactions = true,
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun present(): TimelineState {
|
||||
val localScope = rememberCoroutineScope()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.factories
|
||||
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.messages.impl.timeline.TimelineItemIndexer
|
||||
import io.element.android.features.messages.impl.timeline.diff.TimelineItemsCacheInvalidator
|
||||
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory
|
||||
|
|
@ -26,15 +29,21 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemsFactory @Inject constructor(
|
||||
class TimelineItemsFactory @AssistedInject constructor(
|
||||
@Assisted config: TimelineItemsFactoryConfig,
|
||||
eventItemFactoryCreator: TimelineItemEventFactory.Creator,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val eventItemFactory: TimelineItemEventFactory,
|
||||
private val virtualItemFactory: TimelineItemVirtualFactory,
|
||||
private val timelineItemGrouper: TimelineItemGrouper,
|
||||
private val timelineItemIndexer: TimelineItemIndexer,
|
||||
) {
|
||||
@AssistedFactory
|
||||
interface Creator {
|
||||
fun create(config: TimelineItemsFactoryConfig): TimelineItemsFactory
|
||||
}
|
||||
|
||||
private val eventItemFactory = eventItemFactoryCreator.create(config)
|
||||
private val _timelineItems = MutableSharedFlow<ImmutableList<TimelineItem>>(replay = 1)
|
||||
private val lock = Mutex()
|
||||
private val diffCache = MutableListDiffCache<TimelineItem>()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.timeline.factories
|
||||
|
||||
/**
|
||||
* Some data used to configure the creation of timeline items.
|
||||
* @param computeReadReceipts when false, read receipts will be empty.
|
||||
* @param computeReactions when false, reactions will be empty.
|
||||
*/
|
||||
data class TimelineItemsFactoryConfig(
|
||||
val computeReadReceipts: Boolean,
|
||||
val computeReactions: Boolean,
|
||||
)
|
||||
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.factories.event
|
||||
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
||||
import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReactionSender
|
||||
|
|
@ -26,17 +30,23 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName
|
||||
import io.element.android.libraries.matrix.ui.messages.reply.map
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemEventFactory @Inject constructor(
|
||||
class TimelineItemEventFactory @AssistedInject constructor(
|
||||
@Assisted private val config: TimelineItemsFactoryConfig,
|
||||
private val contentFactory: TimelineItemContentFactory,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val lastMessageTimestampFormatter: LastMessageTimestampFormatter,
|
||||
private val permalinkParser: PermalinkParser,
|
||||
) {
|
||||
@AssistedFactory
|
||||
interface Creator {
|
||||
fun create(config: TimelineItemsFactoryConfig): TimelineItemEventFactory
|
||||
}
|
||||
|
||||
suspend fun create(
|
||||
currentTimelineItem: MatrixTimelineItem.Event,
|
||||
index: Int,
|
||||
|
|
@ -92,8 +102,11 @@ class TimelineItemEventFactory @Inject constructor(
|
|||
}
|
||||
|
||||
private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions {
|
||||
if (!config.computeReactions) {
|
||||
return TimelineItemReactions(reactions = persistentListOf())
|
||||
}
|
||||
val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT)
|
||||
var aggregatedReactions = event.reactions.map { reaction ->
|
||||
var aggregatedReactions = this.event.reactions.map { reaction ->
|
||||
// Sort reactions within an aggregation by timestamp descending.
|
||||
// This puts the most recent at the top, useful in cases like the
|
||||
// reaction summary view or getting the most recent reaction.
|
||||
|
|
@ -129,6 +142,9 @@ class TimelineItemEventFactory @Inject constructor(
|
|||
private fun MatrixTimelineItem.Event.computeReadReceiptState(
|
||||
roomMembers: List<RoomMember>,
|
||||
): TimelineItemReadReceipts {
|
||||
if (!config.computeReadReceipts) {
|
||||
return TimelineItemReadReceipts(receipts = persistentListOf())
|
||||
}
|
||||
return TimelineItemReadReceipts(
|
||||
receipts = event.receipts
|
||||
.map { receipt ->
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import io.element.android.features.messages.impl.actionlist.FakeActionListPresen
|
|||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.draft.FakeComposerDraftService
|
||||
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator
|
||||
import io.element.android.features.messages.impl.messagecomposer.DefaultMessageComposerContext
|
||||
import io.element.android.features.messages.impl.messagecomposer.FakeRoomAliasSuggestionsDataSource
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||
|
|
@ -1024,7 +1024,7 @@ class MessagesPresenterTest {
|
|||
permissionsPresenterFactory,
|
||||
)
|
||||
val timelinePresenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
|
||||
room = matrixRoom,
|
||||
dispatchers = coroutineDispatchers,
|
||||
appScope = this,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.fixtures
|
|||
|
||||
import io.element.android.features.messages.impl.timeline.TimelineItemIndexer
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
|
||||
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseMessageFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseStateFactory
|
||||
|
|
@ -39,40 +40,56 @@ import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorW
|
|||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
internal fun TestScope.aTimelineItemsFactoryCreator(
|
||||
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
|
||||
): TimelineItemsFactory.Creator {
|
||||
return object : TimelineItemsFactory.Creator {
|
||||
override fun create(config: TimelineItemsFactoryConfig): TimelineItemsFactory {
|
||||
return aTimelineItemsFactory(config, timelineItemIndexer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun TestScope.aTimelineItemsFactory(
|
||||
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer()
|
||||
config: TimelineItemsFactoryConfig,
|
||||
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
|
||||
): TimelineItemsFactory {
|
||||
val timelineEventFormatter = aTimelineEventFormatter()
|
||||
val matrixClient = FakeMatrixClient()
|
||||
return TimelineItemsFactory(
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
eventItemFactory = TimelineItemEventFactory(
|
||||
contentFactory = TimelineItemContentFactory(
|
||||
messageFactory = TimelineItemContentMessageFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||
eventItemFactoryCreator = object : TimelineItemEventFactory.Creator {
|
||||
override fun create(config: TimelineItemsFactoryConfig): TimelineItemEventFactory {
|
||||
return TimelineItemEventFactory(
|
||||
contentFactory = TimelineItemContentFactory(
|
||||
messageFactory = TimelineItemContentMessageFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||
permalinkParser = FakePermalinkParser(),
|
||||
textPillificationHelper = FakeTextPillificationHelper(),
|
||||
),
|
||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||
stickerFactory = TimelineItemContentStickerFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
||||
),
|
||||
pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()),
|
||||
utdFactory = TimelineItemContentUTDFactory(),
|
||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
||||
stateFactory = TimelineItemContentStateFactory(timelineEventFormatter),
|
||||
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(),
|
||||
),
|
||||
matrixClient = matrixClient,
|
||||
lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(),
|
||||
permalinkParser = FakePermalinkParser(),
|
||||
textPillificationHelper = FakeTextPillificationHelper(),
|
||||
),
|
||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||
stickerFactory = TimelineItemContentStickerFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation()
|
||||
),
|
||||
pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()),
|
||||
utdFactory = TimelineItemContentUTDFactory(),
|
||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
||||
stateFactory = TimelineItemContentStateFactory(timelineEventFormatter),
|
||||
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(),
|
||||
),
|
||||
matrixClient = matrixClient,
|
||||
lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(),
|
||||
permalinkParser = FakePermalinkParser(),
|
||||
),
|
||||
config = config
|
||||
)
|
||||
}
|
||||
},
|
||||
virtualItemFactory = TimelineItemVirtualFactory(
|
||||
daySeparatorFactory = TimelineItemDaySeparatorFactory(
|
||||
FakeDaySeparatorFormatter()
|
||||
|
|
@ -80,6 +97,7 @@ internal fun TestScope.aTimelineItemsFactory(
|
|||
),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
timelineItemIndexer = timelineItemIndexer,
|
||||
config = config
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.pinned.list
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.actionlist.FakeActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator
|
||||
import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
|
|
@ -35,11 +35,14 @@ import io.element.android.tests.testutils.lambda.assert
|
|||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class PinnedMessagesListPresenterTest {
|
||||
@Test
|
||||
fun `present - initial state feature disabled`() = runTest {
|
||||
|
|
@ -155,6 +158,7 @@ class PinnedMessagesListPresenterTest {
|
|||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Redact, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(redactEventLambda)
|
||||
.isCalledOnce()
|
||||
|
|
@ -184,9 +188,11 @@ class PinnedMessagesListPresenterTest {
|
|||
|
||||
pinnedEventsTimeline.unpinEventLambda = successUnpinEventLambda
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
advanceUntilIdle()
|
||||
|
||||
pinnedEventsTimeline.unpinEventLambda = failureUnpinEventLambda
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Unpin, eventItem))
|
||||
advanceUntilIdle()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
|
||||
|
|
@ -221,6 +227,7 @@ class PinnedMessagesListPresenterTest {
|
|||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.ViewInTimeline, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onViewInTimelineClickLambda)
|
||||
.isCalledOnce()
|
||||
|
|
@ -249,6 +256,7 @@ class PinnedMessagesListPresenterTest {
|
|||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.ViewSource, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onShowEventDebugInfoClickLambda)
|
||||
.isCalledOnce()
|
||||
|
|
@ -277,6 +285,7 @@ class PinnedMessagesListPresenterTest {
|
|||
val filledState = awaitItem() as PinnedMessagesListState.Filled
|
||||
val eventItem = filledState.timelineItems.first() as TimelineItem.Event
|
||||
filledState.eventSink(PinnedMessagesListEvents.HandleAction(TimelineItemAction.Forward, eventItem))
|
||||
advanceUntilIdle()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
assert(onForwardEventClickLambda)
|
||||
.isCalledOnce()
|
||||
|
|
@ -318,10 +327,11 @@ class PinnedMessagesListPresenterTest {
|
|||
return PinnedMessagesListPresenter(
|
||||
navigator = navigator,
|
||||
room = room,
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
|
||||
timelineProvider = timelineProvider,
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
actionListPresenterFactory = FakeActionListPresenter.Factory,
|
||||
appCoroutineScope = this,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ import app.cash.turbine.test
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.FakeMessagesNavigator
|
||||
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator
|
||||
import io.element.android.features.messages.impl.timeline.components.aCriticalShield
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.model.NewEventState
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager
|
||||
|
|
@ -662,7 +661,6 @@ import kotlin.time.Duration.Companion.seconds
|
|||
liveTimeline = timeline,
|
||||
canUserSendMessageResult = { _, _ -> Result.success(true) }
|
||||
),
|
||||
timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(),
|
||||
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
|
||||
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
|
||||
endPollAction: EndPollAction = FakeEndPollAction(),
|
||||
|
|
@ -671,7 +669,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
|
||||
): TimelinePresenter {
|
||||
return TimelinePresenter(
|
||||
timelineItemsFactory = timelineItemsFactory,
|
||||
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
|
||||
room = room,
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
appScope = this,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ package io.element.android.features.preferences.impl.root
|
|||
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
data class PreferencesRootState(
|
||||
val myUser: MatrixUser,
|
||||
val version: String,
|
||||
val deviceId: String?,
|
||||
val deviceId: DeviceId?,
|
||||
val showSecureBackup: Boolean,
|
||||
val showSecureBackupBadge: Boolean,
|
||||
val accountManagementUrl: String?,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.features.preferences.impl.root
|
|||
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ fun aPreferencesRootState(
|
|||
) = PreferencesRootState(
|
||||
myUser = myUser,
|
||||
version = "Version 1.1 (1)",
|
||||
deviceId = "ILAKNDNASDLK",
|
||||
deviceId = DeviceId("ILAKNDNASDLK"),
|
||||
showSecureBackup = true,
|
||||
showSecureBackupBadge = true,
|
||||
accountManagementUrl = "aUrl",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.MatrixUserProvider
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
|
@ -229,7 +230,7 @@ private fun ColumnScope.GeneralSection(
|
|||
@Composable
|
||||
private fun ColumnScope.Footer(
|
||||
version: String,
|
||||
deviceId: String?,
|
||||
deviceId: DeviceId?,
|
||||
onClick: (() -> Unit)?,
|
||||
) {
|
||||
val text = remember(version, deviceId) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ datastore = "1.0.0"
|
|||
constraintlayout = "2.1.4"
|
||||
constraintlayout_compose = "1.0.1"
|
||||
lifecycle = "2.8.4"
|
||||
activity = "1.9.1"
|
||||
activity = "1.9.2"
|
||||
media3 = "1.4.1"
|
||||
camera = "1.3.4"
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.18.1"
|
|||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.42"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.43"
|
||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ enum class FeatureFlags(
|
|||
key = "feature.pinnedEvents",
|
||||
title = "Pinned Events",
|
||||
description = "Allow user to pin events in a room",
|
||||
defaultValue = { false },
|
||||
defaultValue = { true },
|
||||
isFinished = false,
|
||||
),
|
||||
SyncOnPush(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -41,7 +42,7 @@ import java.util.Optional
|
|||
|
||||
interface MatrixClient : Closeable {
|
||||
val sessionId: SessionId
|
||||
val deviceId: String
|
||||
val deviceId: DeviceId
|
||||
val userProfile: StateFlow<MatrixUser>
|
||||
val roomListService: RoomListService
|
||||
val mediaLoader: MatrixMediaLoader
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.api.core
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
@JvmInline
|
||||
value class DeviceId(val value: String) : Serializable {
|
||||
override fun toString(): String = value
|
||||
}
|
||||
|
|
@ -7,9 +7,11 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.oidc
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
|
||||
sealed interface AccountManagementAction {
|
||||
data object Profile : AccountManagementAction
|
||||
data object SessionsList : AccountManagementAction
|
||||
data class SessionView(val deviceId: String) : AccountManagementAction
|
||||
data class SessionEnd(val deviceId: String) : AccountManagementAction
|
||||
data class SessionView(val deviceId: DeviceId) : AccountManagementAction
|
||||
data class SessionEnd(val deviceId: DeviceId) : AccountManagementAction
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.file.safeDelete
|
|||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -120,7 +121,7 @@ class RustMatrixClient(
|
|||
sessionDelegate: RustClientSessionDelegate,
|
||||
) : MatrixClient {
|
||||
override val sessionId: UserId = UserId(client.userId())
|
||||
override val deviceId: String = client.deviceId()
|
||||
override val deviceId: DeviceId = DeviceId(client.deviceId())
|
||||
override val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-$sessionId")
|
||||
|
||||
private val innerRoomListService = syncService.roomListService()
|
||||
|
|
@ -173,13 +174,13 @@ class RustMatrixClient(
|
|||
roomListService = roomListService,
|
||||
innerRoomListService = innerRoomListService,
|
||||
sessionId = sessionId,
|
||||
deviceId = deviceId,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
dispatchers = dispatchers,
|
||||
systemClock = clock,
|
||||
roomContentForwarder = RoomContentForwarder(innerRoomListService),
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
getSessionData = { sessionStore.getSession(sessionId.value)!! },
|
||||
)
|
||||
|
||||
override val mediaLoader: MatrixMediaLoader = RustMediaLoader(
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import org.matrix.rustcomponents.sdk.AccountManagementAction as RustAccountManag
|
|||
fun AccountManagementAction.toRustAction(): RustAccountManagementAction {
|
||||
return when (this) {
|
||||
AccountManagementAction.Profile -> RustAccountManagementAction.Profile
|
||||
is AccountManagementAction.SessionEnd -> RustAccountManagementAction.SessionEnd(deviceId)
|
||||
is AccountManagementAction.SessionView -> RustAccountManagementAction.SessionView(deviceId)
|
||||
is AccountManagementAction.SessionEnd -> RustAccountManagementAction.SessionEnd(deviceId.value)
|
||||
is AccountManagementAction.SessionView -> RustAccountManagementAction.SessionView(deviceId.value)
|
||||
AccountManagementAction.SessionsList -> RustAccountManagementAction.SessionsList
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room
|
|||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScope
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
|
|
@ -52,7 +53,6 @@ import io.element.android.libraries.matrix.impl.util.MessageEventContent
|
|||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -89,6 +89,7 @@ import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RustMatrixRoom(
|
||||
override val sessionId: SessionId,
|
||||
private val deviceId: DeviceId,
|
||||
private val roomListItem: RoomListItem,
|
||||
private val innerRoom: InnerRoom,
|
||||
innerTimeline: InnerTimeline,
|
||||
|
|
@ -97,7 +98,6 @@ class RustMatrixRoom(
|
|||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val systemClock: SystemClock,
|
||||
private val roomContentForwarder: RoomContentForwarder,
|
||||
private val sessionData: SessionData,
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val matrixRoomInfoMapper: MatrixRoomInfoMapper,
|
||||
) : MatrixRoom {
|
||||
|
|
@ -124,7 +124,7 @@ class RustMatrixRoom(
|
|||
override fun call(typingUserIds: List<String>) {
|
||||
channel.trySend(
|
||||
typingUserIds
|
||||
.filter { it != sessionData.userId }
|
||||
.filter { it != sessionId.value }
|
||||
.map(::UserId)
|
||||
)
|
||||
}
|
||||
|
|
@ -188,6 +188,7 @@ class RustMatrixRoom(
|
|||
innerRoom.pinnedEventsTimeline(
|
||||
internalIdPrefix = "pinned_events",
|
||||
maxEventsToLoad = 100u,
|
||||
maxConcurrentRequests = 10u,
|
||||
).let { inner ->
|
||||
createTimeline(inner, mode = Timeline.Mode.PINNED_EVENTS)
|
||||
}
|
||||
|
|
@ -606,7 +607,7 @@ class RustMatrixRoom(
|
|||
room = innerRoom,
|
||||
widgetCapabilitiesProvider = object : WidgetCapabilitiesProvider {
|
||||
override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities {
|
||||
return getElementCallRequiredPermissions(sessionId.value, sessionData.deviceId)
|
||||
return getElementCallRequiredPermissions(sessionId.value, deviceId.value)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room
|
|||
import androidx.collection.lruCache
|
||||
import io.element.android.appconfig.TimelineConfig
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
|
|
@ -18,7 +19,6 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
|||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -38,6 +38,7 @@ private const val CACHE_SIZE = 16
|
|||
|
||||
class RustRoomFactory(
|
||||
private val sessionId: SessionId,
|
||||
private val deviceId: DeviceId,
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
|
|
@ -46,7 +47,6 @@ class RustRoomFactory(
|
|||
private val roomListService: RoomListService,
|
||||
private val innerRoomListService: InnerRoomListService,
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val getSessionData: suspend () -> SessionData,
|
||||
) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val dispatcher = dispatchers.io.limitedParallelism(1)
|
||||
|
|
@ -108,6 +108,7 @@ class RustRoomFactory(
|
|||
val liveTimeline = roomReferences.fullRoom.timeline()
|
||||
RustMatrixRoom(
|
||||
sessionId = sessionId,
|
||||
deviceId = deviceId,
|
||||
roomListItem = roomReferences.roomListItem,
|
||||
innerRoom = roomReferences.fullRoom,
|
||||
innerTimeline = liveTimeline,
|
||||
|
|
@ -116,7 +117,6 @@ class RustRoomFactory(
|
|||
coroutineDispatchers = dispatchers,
|
||||
systemClock = systemClock,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
sessionData = getSessionData(),
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
matrixRoomInfoMapper = matrixRoomInfoMapper,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -61,9 +61,11 @@ import kotlinx.coroutines.flow.onEach
|
|||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.EditedContent
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||
import org.matrix.rustcomponents.sdk.PollData
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
|
|
@ -274,8 +276,15 @@ class RustTimeline(
|
|||
withContext(dispatcher) {
|
||||
runCatching<Unit> {
|
||||
getEventTimelineItem(originalEventId, transactionId).use { item ->
|
||||
val editedContent = EditedContent.RoomMessage(
|
||||
content = MessageEventContent.from(
|
||||
body = body,
|
||||
htmlBody = htmlBody,
|
||||
intentionalMentions = intentionalMentions
|
||||
),
|
||||
)
|
||||
inner.edit(
|
||||
newContent = MessageEventContent.from(body, htmlBody, intentionalMentions),
|
||||
newContent = editedContent,
|
||||
item = item,
|
||||
)
|
||||
}
|
||||
|
|
@ -434,16 +443,21 @@ class RustTimeline(
|
|||
inner.getEventTimelineItemByEventId(
|
||||
eventId = pollStartId.value
|
||||
)
|
||||
pollStartEvent.use {
|
||||
inner.editPoll(
|
||||
val editedContent = EditedContent.PollStart(
|
||||
pollData = PollData(
|
||||
question = question,
|
||||
answers = answers,
|
||||
maxSelections = maxSelections.toUByte(),
|
||||
pollKind = pollKind.toInner(),
|
||||
editItem = pollStartEvent,
|
||||
),
|
||||
)
|
||||
pollStartEvent.use {
|
||||
inner.edit(
|
||||
newContent = editedContent,
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.map { }
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -58,7 +59,7 @@ import java.util.Optional
|
|||
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val deviceId: String = "A_DEVICE_ID",
|
||||
override val deviceId: DeviceId = A_DEVICE_ID,
|
||||
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
||||
private val userDisplayName: String? = A_USER_NAME,
|
||||
private val userAvatarUrl: String? = AN_AVATAR_URL,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
package io.element.android.libraries.matrix.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -47,6 +48,7 @@ val AN_EVENT_ID = EventId("\$anEventId")
|
|||
val AN_EVENT_ID_2 = EventId("\$anEventId2")
|
||||
val A_ROOM_ALIAS = RoomAlias("#alias1:domain")
|
||||
val A_TRANSACTION_ID = TransactionId("aTransactionId")
|
||||
val A_DEVICE_ID = DeviceId("ILAKNDNASDLK")
|
||||
|
||||
val A_UNIQUE_ID = UniqueId("aUniqueId")
|
||||
val A_UNIQUE_ID_2 = UniqueId("aUniqueId2")
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|||
|
||||
// Note: 2 digits max for each value
|
||||
private const val versionMajor = 0
|
||||
private const val versionMinor = 5
|
||||
private const val versionMinor = 6
|
||||
|
||||
// Note: even values are reserved for regular release, odd values for hotfix release.
|
||||
// When creating a hotfix, you should decrease the value, since the current value
|
||||
// is the value for the next regular release.
|
||||
private const val versionPatch = 3
|
||||
private const val versionPatch = 0
|
||||
|
||||
object Versions {
|
||||
val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch
|
||||
|
|
|
|||
|
|
@ -177,9 +177,8 @@ printf "Committing...\n"
|
|||
git commit -a -m 'version++'
|
||||
|
||||
printf "\n================================================================================\n"
|
||||
printf "Wait for the GitHub action https://github.com/element-hq/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n"
|
||||
printf "Please enter the url of the github action (!!! WARNING: NOT THE URL OF THE ARTIFACT ANYMORE !!!)\n"
|
||||
read -p "For instance https://github.com/element-hq/element-x-android/actions/runs/9065756777: " runUrl
|
||||
printf "The GitHub action https://github.com/element-hq/element-x-android/actions/workflows/release.yml?query=branch%%3Amain should have start a new run.\n"
|
||||
read -p "Please enter the url of the run, no need to wait for it to complete (example: https://github.com/element-hq/element-x-android/actions/runs/9065756777): " runUrl
|
||||
|
||||
targetPath="./tmp/Element/${version}"
|
||||
|
||||
|
|
@ -270,7 +269,7 @@ printf "File app-fdroid-x86_64-release-signed.apk:\n"
|
|||
"${buildToolsPath}"/aapt dump badging "${fdroidTargetPath}"/app-fdroid-x86_64-release-signed.apk | grep package
|
||||
|
||||
printf "\n"
|
||||
read -p "Does it look correct? Press enter when it's done."
|
||||
read -p "Does it look correct? Press enter when it's done. "
|
||||
|
||||
printf "\n================================================================================\n"
|
||||
printf "The APKs in ${fdroidTargetPath} have been signed!\n"
|
||||
|
|
@ -363,7 +362,7 @@ read -p ". Press enter to continue. "
|
|||
printf "\n================================================================================\n"
|
||||
printf "Update the project release notes:\n\n"
|
||||
|
||||
read -p "Copy the content of the release note generated by GitHub to the file CHANGES.md and press enter to commit the change. \n"
|
||||
read -p "Copy the content of the release note generated by GitHub to the file CHANGES.md and press enter to commit the change. "
|
||||
|
||||
printf "\n================================================================================\n"
|
||||
printf "Committing...\n"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue