Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline support #3952

Merged
merged 25 commits into from
Aug 12, 2024
Merged

Offline support #3952

merged 25 commits into from
Aug 12, 2024

Conversation

mahibi
Copy link
Collaborator

@mahibi mahibi commented Jun 4, 2024

resolve #217

Authors: @rapterjet2004 @mahibi

Now in Android Documentation for the App's Architecture

NC Offline DB

🚧 TODO

  • Implement Network Monitor
  • Declare Synchronizer (it's implemented in each repository, using the various DAO's)
  • Implement ChatMessageEntity
  • Implement ConversationEntity
  • Abstract away ChatMessage from ChatMessageEntity and ChatMessageJSON
    • Implement Mappers
    • Implement data classes
  • Abstract away Conversation from ConversationEntity and ConversationJSON
    • Implement Mappers
    • Implement data classes
  • Implement ChatDao
  • Implement ConversationDao
  • Implement local version storage using dataStore
  • Implement OfflineFirstChatRepository
    • Model Flows
    • Sync function
    • Get Chat Messages
    • implement on demand synchronization functions
  • Implement OfflineFirstConversationRepository
    • Model Flows
    • Sync function
  • Research Synchronization strategies for settings as they don't require on demand Synchronization as Chatting does.
  • chat block handling
  • "handleUpdateMessages" method must handle all it's system message types immediately
  • Queued messages fail to be sent after connection gained
    • show them also while being offline
  • enter ConversationList in offline mode:
    • replace "Please check your internet connection" with red info bar as in chat
    • avoid to show "Failed to fetch pending invitations"
  • enter chat in offline mode:
    • avoid "Sorry something went wrong"
  • avoid "unread messages" bubble when some system message is received that is not shown in chat
  • X-Chat-Last-Common-Read handling
  • rearrange entity attributes (to fit documentation order)?
  • double check/ fix data types (esp null allowed) for entities
  • final steps:
    • comment in ".openHelperFactory(factory)" in TalkDatabase.kt
    • set database migrations
    • bump database version

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

@mahibi mahibi changed the title WIP first draft for Conversation and ChatMessage. (Entity+Dao) Offline support Jun 4, 2024
@mahibi mahibi force-pushed the feature/217/offlineSupport branch from 8967a16 to 959f26c Compare June 20, 2024 16:06
@niclasheinz
Copy link

I'm very interested in this function. Can I help you testing the new feature using the Nextcloud Talk QA App?

@rapterjet2004
Copy link
Contributor

I'm very interested in this function. Can I help you testing the new feature using the Nextcloud Talk QA App?

Thanks that would be great, bit of warning though, you might have to login again, and as of now offline conversations are still unimplemented, and offline writing to the server is unavailable. There also might be issues with editing/deleting messages, along with processing read status, and a lot of other bugs as this is a pretty significant (but nevertheless needed) refactoring. Better late than never 😄

@niclasheinz
Copy link

I'm very interested in this function. Can I help you testing the new feature using the Nextcloud Talk QA App?

Thanks that would be great, bit of warning though, you might have to login again, and as of now offline conversations are still unimplemented, and offline writing to the server is unavailable. There also might be issues with editing/deleting messages, along with processing read status, and a lot of other bugs as this is a pretty significant (but nevertheless needed) refactoring. Better late than never 😄

Can you inform me, if I can test the offline features to give you a feedback of it? You can also contact me to test some other new features if needed 😀.

@mahibi mahibi force-pushed the feature/217/offlineSupport branch from db2b781 to 61549ea Compare July 4, 2024 12:01
@rapterjet2004
Copy link
Contributor

rapterjet2004 commented Jul 5, 2024

@mahibi
These are functions that are involved in the on demand synchronization strategy used in ChatActivity. These are loadMoreMessages and initMessagePolling. Both launched in separate threads, they write to messageFlow asynchronously.
Found in GetCapabilitiesInitalLoad

chatViewModel.loadMoreMessages(
                        beforeMessageId = -1, // gets history of conversation
                        withCredentials = credentials!!,
                        withUrl = urlForChatting,
                        roomToken = currentConversation!!.token!!,
                        withMessageLimit = MESSAGE_PULL_LIMIT
                    )

chatViewModel.initMessagePolling(
                        withCredentials = credentials!!,
                        withUrl = urlForChatting,
                        roomToken = currentConversation!!.token!!
                    )


loadMoreMessages

is defined in the OfflineFirstChatRepository. It

Loads messages from local storage. If the messages are not found, then it synchronizes the database with the server, before retrying exactly once. Only emits to messageFlow if the message list is not empty.

NC loadMoreMessage

override fun loadMoreMessages(
        beforeMessageId: Long,
        roomToken: String,
        withMessageLimit: Int,
        withNetworkParams: Bundle
    ): Job =
        scope.launch {
            // Here it sets the fieldmap to retrieve past messages, and mark them as read given the roomToken
            val fieldMap = getFieldMap(
                roomToken,
                lookIntoFuture = false,
                setReadMarker = true
            )
            // Here it sets params to be used in the modelFetcher function, used in the synchronizer. 
            withNetworkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
            withNetworkParams.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)

            var attempts = 0
            do {
                attempts++
                val maxAttemptsAreNotReached = (attempts < 2)

                // Used to offset the query, as the lastKnownMessage gets updated in the sync function.
                val lastKnown = datastore.getLastKnownId(roomToken, 0)
                val id = if (beforeMessageId > 0) beforeMessageId else (lastKnown.toLong() + 1) // so it includes lastKnown
              
                // Gets <= 100 messages before the last known message in the conversation
                val list = getMessagesBefore(
                    id,
                    roomToken,
                    withMessageLimit
                )
 
                // Only emits to the flow if the list is not empty. Emits with false, so that the rest of the app understands that this 
                // data is from the past and is meant to be handled in that way
                if (list.isNotEmpty()) {
                    val pair = Pair(false, list)
                    _messageFlow.emit(pair)
                    break
                } else if (maxAttemptsAreNotReached) this@OfflineFirstChatRepository.sync(withNetworkParams)
            } while (maxAttemptsAreNotReached)
        }

loadMoreMessages is also called in the onLoadMore callback. Here, the offset is the last chat message's id in the adapter, rather than the last known message

override fun onLoadMore(page: Int, totalItemsCount: Int) {
        val calculatedPage = totalItemsCount / PAGE_SIZE
        if (calculatedPage > 0) {
            val id = (adapter?.items?.last {
                it.item is ChatMessage
            }?.item as ChatMessage).jsonMessageId

            val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)

            chatViewModel.loadMoreMessages(
                beforeMessageId = id.toLong(),
                withUrl = urlForChatting,
                withCredentials = credentials!!,
                withMessageLimit = MESSAGE_PULL_LIMIT,
                roomToken = currentConversation!!.token!!
            )
        }
    }

Unlike initMessagePolling, loadMoreMessages is not in an infinite while loop. So further calls to loadMoreMessages will require a UI trigger event.

@rapterjet2004
Copy link
Contributor

InitMessagePolling

Long polls the server for any updates to the chat, if found, it synchronizes the database with the server and emits the new messages to messageFlow, else it simply retries after timeout.

NC InitMessagePolling


override fun initMessagePolling(roomToken: String, withNetworkParams: Bundle): Job =
        scope.launch {
            monitor.isOnline.onEach { online ->
                var fieldMap = getFieldMap(roomToken, lookIntoFuture = true, setReadMarker = true)
                while (!itIsPaused) {
                    if (!online) Thread.sleep(500)

                    // sync database with server ( This is a long blocking call b/c long polling is set )
                    withNetworkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
                    withNetworkParams.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
                    this@OfflineFirstChatRepository.sync(withNetworkParams)

                    // get new messages, if not empty -> emit to flow with APPEND
                    var list = getMessagesFrom(newMessageIds)
                    newMessageIds = listOf() // Clear it after use to prevent duplicates

                    // Process read status if not null
                    val lastKnown = datastore.getLastKnownId(roomToken, 0)
                    list = list.map { chatMessage ->
                        chatMessage.readStatus = if (chatMessage.jsonMessageId <= lastKnown) {
                            ReadStatus.READ
                        } else {
                            ReadStatus.SENT
                        }

                        return@map chatMessage
                    }

                    if (list.isNotEmpty()) {
                        val pair = Pair(true, list)
                        _messageFlow.emit(pair)
                    }
                    // update field map vars for next cycle
                    fieldMap = getFieldMap(roomToken, lookIntoFuture = true, setReadMarker = true)
                }
            }
                .flowOn(Dispatchers.IO).collect()
        }

This is a lifecycle aware infinite loop. It should terminate on lifecycle change, and retry every 500 ms on connection loss. I might change that later though.

@mahibi
Copy link
Collaborator Author

mahibi commented Jul 8, 2024


update1: it's necessary to also check if the loaded 100 messages itself contains gaps....
update2: the concept draft won't be exactly implemented like this. @Ivansss introduced the similar concept from iOS talk app which uses chat-blocks (see https://github.com/nextcloud/talk-ios/blob/master/NextcloudTalk/NCChatBlock.h). Most probably we will adapt this...


@rapterjet2004 thanks for the summary.
As far as i see this implementation does not respect gaps and it is all dependend on a full history?
We have to deal with incomplete chat history as loading everything is too much for server and takes too long.
We should begin with to load the last 100 messages and lookintofuture and from there on handle the history.
Just loading the last 100 offline messages from DB could display gaps in the chat if it was not opened for a longer time..

I just wrote some concept for this, but take it as a draft for now / let me know if it makes sense for you.

wenn chat is opened:

read last 100 messages from DB (show them, even if they are older)
read last 100 messages from server (regardless if offline messages are available)
set lastKnownOfflineMessageId to newest message
attention: when loading just the last 100 messages from server, this can introduce gaps if chat was not opened for a long time!

start polling with lastKnownOfflineMessageId

At this point, there are no problems until the user does not scroll back beyond the 100 messages.

load message history

Whenever scrolling back via onLoadMore, we have to check for gaps when loading offline messages.
For that we introduce the hasNoGapUntilMessageId attribute for each message.
For every message it is saved until which other message there is known to be no gap between them.

We want to load the 100 message before this messageX. When scrolling back, this check is made:
Fetch next 100 offline messages from DB (but do not show them).
For the newest messageY from these messages:

if 
	messageY.hasNoGapUntilMessageId < messageX.messageId
then 		// a gap is detected
	load messages from server, insert into DB, show messages in UI
	for the new loaded messages, set hasNoGapUntilMessageId to messageX.hasNoGapUntilMessageId

else if 
	messageY.hasNoGapUntilMessageId >= messageX.messageId
then 		// no gap is detected
	load messages from DB, show messages in UI (no need to make server request)

(Note that the message id's are NOT continuous! Only higher or lower is important.)

Example for a gap:

Lets say we have a messageX with id 400 and it's hasNoGapUntilMessageId is 450. We are scrolling back so we want the previous 100 message before message with id 400.

The previous 100 messages are fetched from DB.
It's newest messageY with id 320 has also hasNoGapUntilMessageId=320

Because 320 < 400 --> There is a gap between message 320 and 400 (so if ids were continuous there would be a gap of 80)
messages 300 to 399 are loaded from the server, insert into DB and shown in UI
for all new loaded messages beginning with message 300, hasNoGapUntilMessageId is set to 450 as we can prove now that there are no gaps between message 300 and message 450.

Example for no gap:

Lets say we have a messageX with id 400 and it's hasNoGapUntilMessageId is 450. We are scrolling back so we want the previous 100 message before message with id 400.

The previous 100 messages are fetched from DB.
It's newest messageY with id 220 has hasNoGapUntilMessageId=400

Because 400 >= 400 --> There is no gap
messages are taken from DB, and shown in UI
for all loaded messages beginning with message 120, hasNoGapUntilMessageId is set to 450 as we can prove now that there are no gaps between message 120 and message 450.

@niclasheinz
Copy link

With the release of 09.07.2024 at 17:49 there are two bugs:

  • the chat list is not loaded
  • The users and groups are displayed under the pen
    -> When I click on a user, the app crashes

@mahibi
Copy link
Collaborator Author

mahibi commented Jul 10, 2024

With the release of 09.07.2024 at 17:49 there are two bugs:

* the chat list is not loaded

* The users and groups are displayed under the pen
  -> When I click on a user, the app crashes

Thanks for testing @niclasheinz . Appreciate that 👍
This branch is under heavy construction so there will be bugs all around for now.
Once the branch is tagged as "to review" any testing is highly welcomed. This will take weeks though.

@rapterjet2004 rapterjet2004 mentioned this pull request Jul 11, 2024
10 tasks
@mahibi mahibi force-pushed the feature/217/offlineSupport branch 2 times, most recently from 27326d7 to b2927dd Compare July 15, 2024 12:32
@mahibi mahibi added this to the 20.0.0 milestone Jul 15, 2024
@mahibi mahibi force-pushed the feature/217/offlineSupport branch from 162f16c to 50f1b50 Compare July 17, 2024 06:48
@mahibi mahibi force-pushed the feature/217/offlineSupport branch 2 times, most recently from ffb471f to a3704d2 Compare July 25, 2024 14:49
@mahibi mahibi force-pushed the feature/217/offlineSupport branch 3 times, most recently from 4bf0d06 to e5f19fc Compare August 9, 2024 13:31
@rapterjet2004
Copy link
Contributor

rapterjet2004 commented Aug 9, 2024

Testing

Duplication bug occurs when asking for chat permissions

issue-3952-dup-bug-permission.webm

Entering a empty conversation triggers a crash

FATAL EXCEPTION: DefaultDispatcher-worker-3
                                                                                                    Process: com.nextcloud.talk2, PID: 30798
                                                                                                    android.database.sqlite.SQLiteConstraintException: FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY)
                                                                                                    	at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
                                                                                                    	at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:961)
                                                                                                    	at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
                                                                                                    	at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:89)
                                                                                                    	at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.kt:42)
                                                                                                    	at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.kt:51)
                                                                                                    	at com.nextcloud.talk.data.database.dao.ChatMessagesDao_Impl$6.call(ChatMessagesDao_Impl.java:345)
                                                                                                    	at com.nextcloud.talk.data.database.dao.ChatMessagesDao_Impl$6.call(ChatMessagesDao_Impl.java:339)
                                                                                                    	at androidx.room.CoroutinesRoom$Companion$execute$2.invokeSuspend(CoroutinesRoom.kt:64)
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
                                                                                                    	at androidx.room.TransactionExecutor.execute$lambda$1$lambda$0(TransactionExecutor.kt:36)
                                                                                                    	at androidx.room.TransactionExecutor.$r8$lambda$FZWr2PGmP3sgXLCiri-DCcePXSs(Unknown Source:0)
                                                                                                    	at androidx.room.TransactionExecutor$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                                                                                                    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
                                                                                                    	at java.lang.Thread.run(Thread.java:1012)
                                                                                                    	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@cfb291, Dispatchers.IO]
FATAL EXCEPTION: main
                                                                                                    Process: com.nextcloud.talk2, PID: 31065
                                                                                                    java.lang.IllegalStateException: java.lang.IllegalStateException: Not initialized yet
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity.setActionBarTitle(ChatActivity.kt:2293)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity.initObservers$lambda$13(ChatActivity.kt:596)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity.$r8$lambda$QKH5JCFLmCzRMlSJ-EV-m4IW5ig(Unknown Source:0)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity$$ExternalSyntheticLambda38.invoke(D8$$SyntheticClass:0)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity$sam$androidx_lifecycle_Observer$0.onChanged(Unknown Source:2)
                                                                                                    	at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
                                                                                                    	at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
                                                                                                    	at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
                                                                                                    	at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
                                                                                                    	at com.nextcloud.talk.chat.viewmodels.ChatViewModel.getCapabilities(ChatViewModel.kt:246)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity$initObservers$1$1.invokeSuspend(ChatActivity.kt:544)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity$initObservers$1$1.invoke(Unknown Source:8)
                                                                                                    	at com.nextcloud.talk.chat.ChatActivity$initObservers$1$1.invoke(Unknown Source:4)
                                                                                                    	at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Emitters.kt:219)
                                                                                                    	at kotlinx.coroutines.flow.FlowKt__ErrorsKt$catchImpl$2.emit(Errors.kt:154)
                                                                                                    	at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Emitters.kt:220)
                                                                                                    	at kotlinx.coroutines.flow.SharedFlowImpl.collect$suspendImpl(SharedFlow.kt:392)
                                                                                                    	at kotlinx.coroutines.flow.SharedFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:958)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                    	at android.os.Looper.loop(Looper.java:294)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8177)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
                                                                                                    	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@9f7be80, Dispatchers.Main.immediate]
issue-3952-crash-on-empty-conversation.webm

Duplication Bug when reentering conversation

issue-3952-dup-bug-reentering.webm

@rapterjet2004
Copy link
Contributor

rapterjet2004 commented Aug 9, 2024

Testing

Duplicate accounts?

Screenshot 2024-08-09 at 10 32 04 AM

Duplicate conversations on search (refreshing the adapter corrects it)

Screenshot 2024-08-09 at 10 33 20 AM

Reaction from user don't show up as being from current user

issue-3952-reactions-removal-bug.webm

@mahibi mahibi force-pushed the feature/217/offlineSupport branch from 9992835 to dd4df84 Compare August 9, 2024 19:47
mahibi and others added 5 commits August 12, 2024 16:47
Authors: Julius Linus and Marcel Hibbe

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
was falsely merged, so now reapplying commit 98f8361

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
…o work and show up as temp messages ( I have some creative ideas for the UI )

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
mahibi and others added 17 commits August 12, 2024 16:47
fix to show unreadMessagesPopup at bottom (typingIndicatorWrapper must not be gone but invisible)

align unreadMessagesPopup to not overlap typingIndicator

fix to hide scrollDown button when unreadMessagesPopup is shown

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
contains one workaround for now, see TODO in updateUiForLastCommonRead method

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
method was extracted for other purposes but was not used in the end. Anyway it made sense to use it for resumeAudioPlaybackIfNeeded.

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
not needed for our requirements + it simplifies code

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
… or remove it otherwise

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Before, old conversations that were left still occurred in the list (only adding+updating was done, but never deleting)

also, the list is up to date when coming back from chat. Otherwise there may be unread messages shown for a short moment which were already read.

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
...will be done by foreign keys cascading. Therefore, also added foreign key to ChatBlockEntity

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
…es sense

remove roomId from Conversation (deprecated. only token should be used)

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
…phabetically

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
comment in openHelperFactory

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Copy link
Contributor

Codacy

Lint

TypemasterPR
Warnings9087
Errors129132

SpotBugs

CategoryBaseNew
Bad practice66
Correctness1111
Dodgy code7878
Internationalization33
Malicious code vulnerability33
Performance66
Security11
Total108108

Lint increased!

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
@mahibi mahibi marked this pull request as ready for review August 12, 2024 15:18
@mahibi mahibi merged commit 1e257fc into master Aug 12, 2024
13 of 19 checks passed
@mahibi mahibi deleted the feature/217/offlineSupport branch August 12, 2024 15:20
Copy link
Contributor

APK file: https://www.kaminsky.me/nc-dev/android-artifacts/3952-talk.apk

qrcode

To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud Talk app.

@niclasheinz
Copy link

niclasheinz commented Aug 12, 2024

Hello @mahibi and @rapterjet2004

That's great 🎉 . It works well. It would be nice if I could switch offline support on and off for some chats. It would also be great if I could compose and send a message (text, image, audio, poll, location, file or contact) offline and when I am back online this message is sent automatically.

But otherwise really good work. Really great.

Best regards, Niclas

@migulen
Copy link

migulen commented Aug 13, 2024

Great!!!!

I've tested the offline support with my account on nextcloud server version v28.0.4 and works fine, but my account at nextcloud server v25.0.12 can't show any "room" after upgrade the client to Android Nextcloud Talk v20.0.0-RC1.

I've uninstalled completly and reinstaled and configured accounts, with the same result. I supose that this version of Android Nextcloud Talk don't have support to older Spreed(talk) servers like v15.0.8. The client Android Nextcloud Talk v19.1.0-RC1 works fine with v28.0.4 and v25.0.12 of nextcloud server.

Thanks for all @mahibi & CO!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Not usable offline and slow on bad connection
4 participants