mattermost-mobile/android/app/src/main/java/com/mattermost/helpers/PushNotificationDataHelper.kt
Elias Nahum ee13679f38
Android refactor push notifications to store data or send to JS for processing (#7127)
* Android refactor push notifications to store data or send to JS for processing

* remove blank line

* rename variable
2023-02-15 17:07:27 +02:00

154 lines
7.2 KiB
Kotlin

package com.mattermost.helpers
import android.content.Context
import android.os.Bundle
import android.util.Log
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.mattermost.helpers.database_extension.*
import com.mattermost.helpers.push_notification.*
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class PushNotificationDataHelper(private val context: Context) {
private var coroutineScope = CoroutineScope(Dispatchers.Default)
fun fetchAndStoreDataForPushNotification(initialData: Bundle, isReactInit: Boolean): Bundle? {
var result: Bundle? = null
val job = coroutineScope.launch(Dispatchers.Default) {
result = PushNotificationDataRunnable.start(context, initialData, isReactInit)
}
runBlocking {
job.join()
}
return result
}
}
class PushNotificationDataRunnable {
companion object {
internal val specialMentions = listOf("all", "here", "channel")
private val dbHelper = DatabaseHelper.instance!!
private val mutex = Mutex()
suspend fun start(context: Context, initialData: Bundle, isReactInit: Boolean): Bundle? {
// for more info see: https://blog.danlew.net/2020/01/28/coroutines-and-java-synchronization-dont-mix/
mutex.withLock {
val serverUrl: String = initialData.getString("server_url") ?: return null
val db = dbHelper.getDatabaseForServer(context, serverUrl)
var result: Bundle? = null
try {
if (db != null) {
val teamId = initialData.getString("team_id")
val channelId = initialData.getString("channel_id")
val postId = initialData.getString("post_id")
val rootId = initialData.getString("root_id")
val isCRTEnabled = initialData.getString("is_crt_enabled") == "true"
Log.i("ReactNative", "Start fetching notification data in server=$serverUrl for channel=$channelId")
val receivingThreads = isCRTEnabled && !rootId.isNullOrEmpty()
val notificationData = Arguments.createMap()
if (!teamId.isNullOrEmpty()) {
val res = fetchTeamIfNeeded(db, serverUrl, teamId)
res.first?.let { notificationData.putMap("team", it) }
res.second?.let { notificationData.putMap("myTeam", it) }
}
if (channelId != null && postId != null) {
val channelRes = fetchMyChannel(db, serverUrl, channelId, isCRTEnabled)
channelRes.first?.let { notificationData.putMap("channel", it) }
channelRes.second?.let { notificationData.putMap("myChannel", it) }
val loadedProfiles = channelRes.third
// Fetch categories if needed
if (!teamId.isNullOrEmpty() && notificationData.getMap("myTeam") != null) {
// should load all categories
val res = fetchMyTeamCategories(db, serverUrl, teamId)
res?.let { notificationData.putMap("categories", it) }
} else if (notificationData.getMap("channel") != null) {
// check if the channel is in the category for the team
val res = addToDefaultCategoryIfNeeded(db, notificationData.getMap("channel")!!)
res?.let { notificationData.putArray("categoryChannels", it) }
}
val postData = fetchPosts(db, serverUrl, channelId, isCRTEnabled, rootId, loadedProfiles)
postData?.getMap("posts")?.let { notificationData.putMap("posts", it) }
var notificationThread: ReadableMap? = null
if (isCRTEnabled && !rootId.isNullOrEmpty()) {
notificationThread = fetchThread(db, serverUrl, rootId, teamId)
}
getThreadList(notificationThread, postData?.getArray("threads"))?.let {
val threadsArray = Arguments.createArray()
for(item in it) {
threadsArray.pushMap(item)
}
notificationData.putArray("threads", threadsArray)
}
val userList = fetchNeededUsers(serverUrl, loadedProfiles, postData)
notificationData.putArray("users", ReadableArrayUtils.toWritableArray(userList.toArray()))
}
result = Arguments.toBundle(notificationData)
if (!isReactInit) {
dbHelper.saveToDatabase(db, notificationData, teamId, channelId, receivingThreads)
}
Log.i("ReactNative", "Done processing push notification=$serverUrl for channel=$channelId")
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
db?.close()
Log.i("ReactNative", "DONE fetching notification data")
}
return result
}
}
private fun getThreadList(notificationThread: ReadableMap?, threads: ReadableArray?): ArrayList<ReadableMap>? {
threads?.let {
val threadsArray = ArrayList<ReadableMap>()
val threadIds = ArrayList<String>()
notificationThread?.let { thread ->
thread.getString("id")?.let { it1 -> threadIds.add(it1) }
threadsArray.add(thread)
}
for(i in 0 until it.size()) {
val thread = it.getMap(i)
val threadId = thread.getString("id")
if (threadId != null) {
if (threadIds.contains(threadId)) {
// replace the values for participants and is_following
val index = threadsArray.indexOfFirst { el -> el.getString("id") == threadId }
val prev = threadsArray[index]
val merge = Arguments.createMap()
merge.merge(prev)
merge.putBoolean("is_following", thread.getBoolean("is_following"))
merge.putArray("participants", thread.getArray("participants"))
threadsArray[index] = merge
} else {
threadsArray.add(thread)
threadIds.add(threadId)
}
}
}
return threadsArray
}
return null
}
}
}