Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
9567523e
Unverified
Commit
9567523e
authored
Sep 06, 2018
by
Lucio Maciel
Committed by
GitHub
Sep 06, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1615 from luckcoolla/fix/chat_history_sync
[WIP][FIX] Chat history sync
parents
b01c1258
0cb06f84
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
131 additions
and
53 deletions
+131
-53
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+28
-15
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+56
-38
MessagesRepository.kt
...a/chat/rocket/android/server/domain/MessagesRepository.kt
+18
-0
MemoryMessagesRepository.kt
...ndroid/server/infraestructure/MemoryMessagesRepository.kt
+8
-0
SharedPreferencesMessagesRepository.kt
...er/infraestructure/SharedPreferencesMessagesRepository.kt
+21
-0
No files found.
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
9567523e
...
@@ -158,23 +158,36 @@ class ChatRoomAdapter(
...
@@ -158,23 +158,36 @@ class ChatRoomAdapter(
}
}
fun
prependData
(
dataSet
:
List
<
BaseUiModel
<*
>>)
{
fun
prependData
(
dataSet
:
List
<
BaseUiModel
<*
>>)
{
val
item
=
dataSet
.
indexOfFirst
{
newItem
->
//---At first we will update all already saved elements with received updated ones
this
.
dataSet
.
indexOfFirst
{
it
.
messageId
==
newItem
.
messageId
&&
it
.
viewType
==
newItem
.
viewType
}
>
-
1
val
filteredDataSet
=
dataSet
.
filter
{
newItem
->
}
val
matchedIndex
=
this
.
dataSet
.
indexOfFirst
{
it
.
messageId
==
newItem
.
messageId
&&
it
.
viewType
==
newItem
.
viewType
}
if
(
item
==
-
1
)
{
if
(
matchedIndex
>
-
1
)
{
this
.
dataSet
.
addAll
(
0
,
dataSet
)
this
.
dataSet
[
matchedIndex
]
=
newItem
notifyItemRangeInserted
(
0
,
dataSet
.
size
)
notifyItemChanged
(
matchedIndex
)
}
else
{
dataSet
.
forEach
{
item
->
val
index
=
this
.
dataSet
.
indexOfFirst
{
item
.
messageId
==
it
.
messageId
&&
item
.
viewType
==
it
.
viewType
}
if
(
index
>
-
1
)
{
this
.
dataSet
[
index
]
=
item
notifyItemChanged
(
index
)
}
}
}
return
@filter
(
matchedIndex
<
0
)
}
val
minAdditionDate
=
filteredDataSet
.
minBy
{
it
.
message
.
timestamp
}
?:
return
//---In the most cases we will just add new elements to the top of messages heap
if
(
minAdditionDate
.
message
.
timestamp
>
this
.
dataSet
[
0
].
message
.
timestamp
)
{
this
.
dataSet
.
addAll
(
0
,
filteredDataSet
)
notifyItemRangeInserted
(
0
,
filteredDataSet
.
size
)
return
}
}
//---Else branch: merging messages---
//---We are inserting new received elements into set. Sort them by time+type and show
if
(
filteredDataSet
.
isEmpty
())
return
this
.
dataSet
.
addAll
(
0
,
filteredDataSet
)
val
tmp
=
this
.
dataSet
.
sortedWith
(
Comparator
{
t
,
t2
->
val
timeComparison
=
t
.
message
.
timestamp
.
compareTo
(
t2
.
message
.
timestamp
)
if
(
timeComparison
==
0
)
{
return
@Comparator
t
.
viewType
.
compareTo
(
t2
.
viewType
)
}
timeComparison
}).
reversed
()
this
.
dataSet
.
clear
()
this
.
dataSet
.
addAll
(
tmp
)
notifyDataSetChanged
()
}
}
fun
updateItem
(
message
:
BaseUiModel
<
*
>)
{
fun
updateItem
(
message
:
BaseUiModel
<
*
>)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
9567523e
...
@@ -184,7 +184,8 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -184,7 +184,8 @@ class ChatRoomPresenter @Inject constructor(
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
)
)
)
)
if
(
oldMessages
.
isNotEmpty
())
{
val
lastSyncDate
=
messagesRepository
.
getLastSyncDate
(
chatRoomId
)
if
(
oldMessages
.
isNotEmpty
()
&&
lastSyncDate
!=
null
)
{
view
.
showMessages
(
oldMessages
,
clearDataSet
)
view
.
showMessages
(
oldMessages
,
clearDataSet
)
loadMissingMessages
()
loadMissingMessages
()
}
else
{
}
else
{
...
@@ -226,6 +227,19 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -226,6 +227,19 @@ class ChatRoomPresenter @Inject constructor(
client
.
messages
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
30
).
result
client
.
messages
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
30
).
result
}
}
messagesRepository
.
saveAll
(
messages
)
messagesRepository
.
saveAll
(
messages
)
//we are saving last sync date of latest synced chat room message
if
(
offset
==
0L
)
{
//if success - saving last synced time
if
(
messages
.
isEmpty
())
{
//chat history is empty - just saving current date
messagesRepository
.
saveLastSyncDate
(
chatRoomId
,
System
.
currentTimeMillis
())
}
else
{
//assume that BE returns ordered messages, the first message is the latest one
messagesRepository
.
saveLastSyncDate
(
chatRoomId
,
messages
.
first
().
timestamp
)
}
}
view
.
showMessages
(
view
.
showMessages
(
mapper
.
map
(
mapper
.
map
(
messages
,
messages
,
...
@@ -275,7 +289,7 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -275,7 +289,7 @@ class ChatRoomPresenter @Inject constructor(
timestamp
=
Instant
.
now
().
toEpochMilli
(),
timestamp
=
Instant
.
now
().
toEpochMilli
(),
sender
=
SimpleUser
(
null
,
username
,
username
),
sender
=
SimpleUser
(
null
,
username
,
username
),
attachments
=
null
,
attachments
=
null
,
avatar
=
currentServer
.
avatarUrl
(
username
!!
),
avatar
=
currentServer
.
avatarUrl
(
username
?:
""
),
channels
=
null
,
channels
=
null
,
editedAt
=
null
,
editedAt
=
null
,
editedBy
=
null
,
editedBy
=
null
,
...
@@ -483,45 +497,49 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -483,45 +497,49 @@ class ChatRoomPresenter @Inject constructor(
private
fun
loadMissingMessages
()
{
private
fun
loadMissingMessages
()
{
launch
(
parent
=
strategy
.
jobs
)
{
launch
(
parent
=
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
chatRoomType
!=
null
)
{
chatRoomId
?.
let
{
chatRoomId
->
val
roomType
=
roomTypeOf
(
chatRoomType
!!
)
val
roomType
=
roomTypeOf
(
chatRoomType
)
messagesRepository
.
getByRoomId
(
chatRoomId
!!
)
val
lastSyncDate
=
messagesRepository
.
getLastSyncDate
(
chatRoomId
)
.
sortedByDescending
{
it
.
timestamp
}.
firstOrNull
()
?.
let
{
lastMessage
->
// lastSyncDate or 0. LastSyncDate could be in case when we sent some messages offline(and saved them locally),
val
instant
=
Instant
.
ofEpochMilli
(
lastMessage
.
timestamp
).
toString
()
// but never has obtained chatMessages(or history) from remote. In this case we should sync all chat history from beginning
try
{
val
instant
=
Instant
.
ofEpochMilli
(
lastSyncDate
?:
0
).
toString
()
val
messages
=
//
retryIO
(
description
=
"history($chatRoomId, $roomType, $instant)"
)
{
try
{
client
.
history
(
val
messages
=
chatRoomId
!!
,
roomType
,
count
=
50
,
retryIO
(
description
=
"history($chatRoomId, $roomType, $instant)"
)
{
oldest
=
instant
client
.
history
(
)
chatRoomId
!!
,
roomType
,
count
=
50
,
}
oldest
=
instant
Timber
.
d
(
"History: $messages"
)
)
}
if
(
messages
.
result
.
isNotEmpty
())
{
Timber
.
d
(
"History: $messages"
)
val
models
=
mapper
.
map
(
messages
.
result
,
RoomUiModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
// FIXME: Why are we fixing isRoom attribute to true here?
isRoom
=
true
))
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
view
.
showNewMessage
(
models
,
true
)
}
if
(
messages
.
result
.
size
==
50
)
{
if
(
messages
.
result
.
isNotEmpty
())
{
// we loaded at least count messages, try one more to fetch more messages
val
models
=
mapper
.
map
(
messages
.
result
,
RoomUiModel
(
loadMissingMessages
()
roles
=
chatRoles
,
}
isBroadcast
=
chatIsBroadcast
,
}
// FIXME: Why are we fixing isRoom attribute to true here?
}
catch
(
ex
:
Exception
)
{
isRoom
=
true
// TODO - we need to better treat connection problems here, but no let gaps
))
// on the messages list
messagesRepository
.
saveAll
(
messages
.
result
)
Timber
.
d
(
ex
,
"Error fetching channel history"
)
//if success - saving last synced time
//assume that BE returns ordered messages, the first message is the latest one
messagesRepository
.
saveLastSyncDate
(
chatRoomId
,
messages
.
result
.
first
().
timestamp
)
launchUI
(
strategy
)
{
view
.
showNewMessage
(
models
,
true
)
}
if
(
messages
.
result
.
size
==
50
)
{
// we loaded at least count messages, try one more to fetch more messages
loadMissingMessages
()
}
}
}
}
}
catch
(
ex
:
Exception
)
{
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber
.
d
(
ex
,
"Error fetching channel history"
)
}
}
}
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/server/domain/MessagesRepository.kt
View file @
9567523e
...
@@ -72,4 +72,22 @@ interface MessagesRepository {
...
@@ -72,4 +72,22 @@ interface MessagesRepository {
suspend
fun
getAllUnsent
():
List
<
Message
>
suspend
fun
getAllUnsent
():
List
<
Message
>
suspend
fun
getUnsentByRoomId
(
roomId
:
String
):
List
<
Message
>
suspend
fun
getUnsentByRoomId
(
roomId
:
String
):
List
<
Message
>
/**
* Save time of the latest room messages sync.
* Call this fun only when the latest messages list being received via /history or /messages network calls
*
* @param rid The id of the room the messages are.
* @param timeMillis time of room messages sync or the latest room message timestamp(which came with /history request)
*/
suspend
fun
saveLastSyncDate
(
rid
:
String
,
timeMillis
:
Long
)
/**
* Get time when the room chat history has been loaded last time.
*
* @param rid The id of the room the messages are.
*
* @return Last Sync time or Null.
*/
suspend
fun
getLastSyncDate
(
rid
:
String
):
Long
?
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/MemoryMessagesRepository.kt
View file @
9567523e
...
@@ -7,8 +7,16 @@ import kotlinx.coroutines.experimental.withContext
...
@@ -7,8 +7,16 @@ import kotlinx.coroutines.experimental.withContext
class
MemoryMessagesRepository
:
MessagesRepository
{
class
MemoryMessagesRepository
:
MessagesRepository
{
private
var
lastSyncDates
:
HashMap
<
String
,
Long
>
=
HashMap
()
private
val
messages
:
HashMap
<
String
,
Message
>
=
HashMap
()
private
val
messages
:
HashMap
<
String
,
Message
>
=
HashMap
()
override
suspend
fun
saveLastSyncDate
(
rid
:
String
,
timeMillis
:
Long
)
{
lastSyncDates
[
rid
]
=
timeMillis
}
override
suspend
fun
getLastSyncDate
(
rid
:
String
)
=
lastSyncDates
[
rid
]
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
return
@withContext
messages
[
id
]
return
@withContext
messages
[
id
]
}
}
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/SharedPreferencesMessagesRepository.kt
View file @
9567523e
...
@@ -13,6 +13,27 @@ class SharedPreferencesMessagesRepository(
...
@@ -13,6 +13,27 @@ class SharedPreferencesMessagesRepository(
private
val
moshi
:
Moshi
,
private
val
moshi
:
Moshi
,
private
val
currentServerInteractor
:
GetCurrentServerInteractor
private
val
currentServerInteractor
:
GetCurrentServerInteractor
)
:
MessagesRepository
{
)
:
MessagesRepository
{
private
val
KEY_LAST_SYNC_DATE
=
"KEY_LAST_SYNC_DATE"
override
suspend
fun
saveLastSyncDate
(
rid
:
String
,
timeMillis
:
Long
)
{
withContext
(
CommonPool
)
{
currentServerInteractor
.
get
()
?.
let
{
prefs
.
edit
().
putLong
(
getSyncDateKey
(
it
,
rid
),
timeMillis
).
apply
()
}
}
}
override
suspend
fun
getLastSyncDate
(
rid
:
String
):
Long
?
=
withContext
(
CommonPool
)
{
currentServerInteractor
.
get
()
?.
also
{
server
->
if
(!
prefs
.
contains
(
getSyncDateKey
(
server
,
rid
)))
return
@withContext
null
val
time
=
prefs
.
getLong
(
getSyncDateKey
(
server
,
rid
),
-
1
)
return
@withContext
if
(
time
==
-
1L
)
null
else
time
}
return
@withContext
null
}
private
fun
getSyncDateKey
(
server
:
String
,
rid
:
String
)
=
"${KEY_LAST_SYNC_DATE}_$server $rid"
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
currentServerInteractor
.
get
()
?.
also
{
server
->
currentServerInteractor
.
get
()
?.
also
{
server
->
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment