feat: more opengroup in chat manager, poller and API. refactor mentions to libsession

This commit is contained in:
jubb 2021-04-15 17:17:55 +10:00
parent 6f46bbefbe
commit 96e604d06b
14 changed files with 241 additions and 63 deletions

View File

@ -45,6 +45,7 @@ import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
import org.session.libsession.utilities.mentions.MentionsManager;
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
import org.session.libsignal.service.api.util.StreamDetails;
import org.session.libsignal.service.loki.api.PushNotificationAPI;
@ -52,7 +53,6 @@ import org.session.libsignal.service.loki.api.SnodeAPI;
import org.session.libsignal.service.loki.api.SwarmAPI;
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
import org.session.libsignal.utilities.logging.Log;
import org.signal.aesgcmprovider.AesGcmProvider;
import org.thoughtcrime.securesms.components.TypingStatusSender;

View File

@ -83,21 +83,42 @@ import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate;
import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.threads.DistributionTypes;
import org.session.libsession.messaging.threads.GroupRecord;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.messaging.threads.recipients.RecipientFormattingException;
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
import org.session.libsession.utilities.ExpirationUtil;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.MediaTypes;
import org.session.libsession.utilities.ServiceUtil;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.ViewUtil;
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
import org.session.libsession.utilities.mentions.Mention;
import org.session.libsession.utilities.mentions.MentionsManager;
import org.session.libsession.utilities.views.Stub;
import org.session.libsignal.libsignal.InvalidMessageException;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
import org.session.libsignal.service.loki.utilities.mentions.Mention;
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager;
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
import org.session.libsignal.utilities.concurrent.ListenableFuture;
import org.session.libsignal.utilities.concurrent.SettableFuture;
import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ExpirationDialog;
import org.thoughtcrime.securesms.MediaOverviewActivity;
@ -121,7 +142,6 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.contactshare.ContactUtil;
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -135,7 +155,6 @@ import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity;
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
@ -158,9 +177,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteId;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
@ -169,30 +185,11 @@ import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.messaging.threads.recipients.RecipientFormattingException;
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.search.model.MessageResult;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.PushCharacterCalculator;
import org.session.libsession.utilities.ServiceUtil;
import org.session.libsession.utilities.Util;
import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact;
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.messaging.threads.GroupRecord;
import org.session.libsession.utilities.ExpirationUtil;
import org.session.libsession.utilities.views.Stub;
import org.session.libsession.utilities.ViewUtil;
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
import org.session.libsignal.utilities.concurrent.ListenableFuture;
import org.session.libsignal.utilities.concurrent.SettableFuture;
import org.session.libsession.utilities.TextSecurePreferences;
import java.io.IOException;
import java.text.SimpleDateFormat;

View File

@ -274,11 +274,21 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName)
}
override fun setOpenGroupDisplayName(publicKey: String, room: String, server: String, displayName: String) {
val groupID = "$server.$room"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(groupID, publicKey, displayName)
}
override fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String? {
val groupID = "$server.$channel"
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey)
}
override fun getOpenGroupDisplayName(publicKey: String, room: String, server: String): String? {
val groupID = "$server.$room"
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey)
}
override fun getLastMessageServerId(room: String, server: String): Long? {
return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(room, server)
}

View File

@ -33,7 +33,7 @@ import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.opengroups.OpenGroupAPI
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.utilities.*
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
import org.session.libsession.utilities.mentions.MentionsManager
import org.session.libsignal.service.loki.utilities.toHexString
import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.ApplicationContext

View File

@ -6,10 +6,9 @@ import android.graphics.Bitmap
import android.text.TextUtils
import androidx.annotation.WorkerThread
import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.opengroups.OpenGroupAPI
import org.session.libsession.messaging.opengroups.OpenGroupInfo
import org.session.libsession.messaging.opengroups.*
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupV2Poller
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.Util
import org.session.libsignal.service.loki.api.opengroups.PublicChat
@ -21,7 +20,9 @@ import java.util.concurrent.Executors
class PublicChatManager(private val context: Context) {
private var chats = mutableMapOf<Long, OpenGroup>()
private var v2Chats = mutableMapOf<Long, OpenGroupV2>()
private val pollers = mutableMapOf<Long, OpenGroupPoller>()
private val v2Pollers = mutableMapOf<Long, OpenGroupV2Poller>()
private val observers = mutableMapOf<Long, ContentObserver>()
private var isPolling = false
private val executorService = Executors.newScheduledThreadPool(4)
@ -53,6 +54,12 @@ class PublicChatManager(private val context: Context) {
listenToThreadDeletion(threadId)
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
}
for ((threadId, chat) in v2Chats) {
val poller = v2Pollers[threadId] ?: OpenGroupV2Poller(chat, executorService)
poller.startIfNeeded()
listenToThreadDeletion(threadId)
if (!v2Pollers.containsKey(threadId)) { v2Pollers[threadId] = poller }
}
isPolling = true
}
@ -73,6 +80,15 @@ class PublicChatManager(private val context: Context) {
return addChat(server, channel, channelInfo)
}
@WorkerThread
fun addChat(server: String, room: String): OpenGroupV2 {
// Ensure the auth token is acquired.
OpenGroupAPIV2.getAuthToken(server).get()
val channelInfo = OpenGroupAPIV2.getChannelInfo(channel, server).get()
return addChat(server, room, channelInfo)
}
@WorkerThread
public fun addChat(server: String, channel: Long, info: OpenGroupInfo): OpenGroup {
val chat = PublicChat(channel, server, info.displayName, true)
@ -99,6 +115,20 @@ class PublicChatManager(private val context: Context) {
return OpenGroup.from(chat)
}
@WorkerThread
fun addChat(server: String, room: String, info: OpenGroupInfo): OpenGroupV2 {
val chat = OpenGroupV2(server, room, info.displayName, )
var threadID = GroupManager.getOpenGroupThreadID(chat.id, context)
var profilePicture: Bitmap? = null
if (threadID < 0) {
if (info.profilePictureURL.isNotEmpty()) {
val profilePictureAsByteArray = OpenGroupAPIV2.downloadOpenGroupProfilePicture(server, info.profilePictureURL)
profilePicture = BitmapUtil.fromByteArray(profilePictureAsByteArray)
}
val result = GroupManager.createOpenGroup()
}
}
public fun removeChat(server: String, channel: Long) {
val threadDB = DatabaseFactory.getThreadDatabase(context)
val groupId = PublicChat.getId(channel, server)
@ -112,11 +142,13 @@ class PublicChatManager(private val context: Context) {
private fun refreshChatsAndPollers() {
val storage = MessagingConfiguration.shared.storage
val chatsInDB = storage.getAllOpenGroups()
val v2ChatsInDB = storage.getAllV2OpenGroups()
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
// Only append to chats if we have a thread for the chat
chats = chatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap()
v2Chats = v2ChatsInDB.filter { GroupManager.getOpenGroupThreadID(it.value.id, context) > -1 }.toMutableMap()
}
private fun listenToThreadDeletion(threadID: Long) {

View File

@ -1,10 +1,10 @@
package org.thoughtcrime.securesms.loki.utilities
import android.content.Context
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.mentions.MentionsManager
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
object MentionManagerUtilities {

View File

@ -7,10 +7,10 @@ import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ListView
import org.session.libsession.utilities.mentions.Mention
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.GlideRequests
import org.session.libsignal.service.loki.utilities.mentions.Mention
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
private var mentionCandidates = listOf<Mention>()

View File

@ -9,7 +9,7 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.opengroups.OpenGroupAPI
import org.session.libsignal.service.loki.utilities.mentions.Mention
import org.session.libsession.utilities.mentions.Mention
import org.thoughtcrime.securesms.mms.GlideRequests
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {

View File

@ -14,7 +14,7 @@ import org.session.libsession.messaging.avatars.ProfileContactPhoto
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.loki.utilities.mentions.MentionsManager
import org.session.libsession.utilities.mentions.MentionsManager
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator
import org.thoughtcrime.securesms.mms.GlideRequests

View File

@ -71,8 +71,8 @@ interface StorageProtocol {
fun setOpenGroupPublicKey(server: String, newValue: String)
// Open Group User Info
fun setOpenGroupDisplayName(publicKey: String, channel: Long, server: String, displayName: String)
fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String?
fun setOpenGroupDisplayName(publicKey: String, room: String, server: String, displayName: String)
fun getOpenGroupDisplayName(publicKey: String, room: String, server: String): String?
// Open Group Metadata
@ -180,4 +180,7 @@ interface StorageProtocol {
fun setOpenGroupProfilePictureURL(group: Long, server: String, newValue: String)
fun getOpenGroupProfilePictureURL(group: Long, server: String): String?
fun setOpenGroupDisplayName(publicKey: String, channel: Long, server: String, displayName: String)
fun getOpenGroupDisplayName(publicKey: String, channel: Long, server: String): String?
}

View File

@ -1,32 +1,153 @@
package org.session.libsession.messaging.opengroups
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.bind
import okhttp3.HttpUrl
import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.opengroups.OpenGroupAPIV2.Error
import org.session.libsession.messaging.utilities.DotNetAPI
import org.session.libsignal.service.loki.api.utilities.HTTP
import java.util.*
object OpenGroupAPIV2: DotNetAPI() {
enum class Error {
GENERIC,
PARSING_FAILED,
DECRYPTION_FAILED,
SIGNING_FAILED,
INVALID_URL,
NO_PUBLIC_KEY
}
object OpenGroupAPIV2 {
private val moderators: HashMap<String, HashMap<String, Set<String>>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
const val DEFAULT_SERVER = "https://sessionopengroup.com"
const val DEFAULT_SERVER_PUBLIC_KEY = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b"
fun getMessages(room: String, server: String): Promise<List<OpenGroupV2Message>, Exception> {
sealed class Error : Exception() {
object GENERIC : Error()
object PARSING_FAILED : Error()
object DECRYPTION_FAILED : Error()
object SIGNING_FAILED : Error()
object INVALID_URL : Error()
object NO_PUBLIC_KEY : Error()
}
data class Info(
val id: String,
val name: String,
val imageID: String
)
data class Request(
val verb: HTTP.Verb,
val room: String?,
val server: String,
val endpoint: String,
val queryParameters: Map<String, String>,
val parameters: Any,
val headers: Map<String, String>,
val isAuthRequired: Boolean,
// Always `true` under normal circumstances. You might want to disable
// this when running over Lokinet.
val useOnionRouting: Boolean
)
private fun send(request: Request): Promise<Any, Exception> {
val parsed = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.INVALID_URL)
val urlBuilder = HttpUrl.Builder()
.scheme(parsed.scheme())
.host(parsed.host())
.addPathSegment(request.endpoint)
for ((key, value) in request.queryParameters) {
urlBuilder.addQueryParameter(key, value)
}
fun execute(token: String?): Promise<Map<*, *>, Exception> {
}
return if (request.isAuthRequired) {
getAuthToken(request.room!!, request.server).bind(::execute)
} else {
execute(null)
}
}
fun getAuthToken(room: String, server: String): Promise<String, Exception> {
val storage = MessagingConfiguration.shared.storage
return storage.getAuthToken(room, server)?.let {
Promise.of(it)
} ?: run {
requestNewAuthToken(room, server)
.bind { claimAuthToken(it, room, server) }
.success { authToken ->
storage.setAuthToken(room, server, authToken)
}
}
}
fun requestNewAuthToken(room: String, server: String): Promise<String, Exception> {
val (publicKey, _) = MessagingConfiguration.shared.storage.getUserKeyPair()
?: return Promise.ofFail(Error.GENERIC)
val queryParameters = mutableMapOf("public_key" to publicKey)
}
fun claimAuthToken(authToken: String, room: String, server: String): Promise<String, Exception> {
TODO("implement")
}
data class Info(val id: String, val name: String, val imageId: String?)
fun deleteAuthToken(room: String, server: String): Promise<Long, Exception> {
TODO("implement")
}
fun upload(file: ByteArray, room: String, server: String): Promise<Long, Exception> {
TODO("implement")
}
fun download(file: Long, room: String, server: String): Promise<ByteArray, Exception> {
TODO("implement")
}
fun send(message: OpenGroupMessageV2, room: String, server: String): Promise<OpenGroupMessageV2, Exception> {
TODO("implement")
}
fun getMessages(room: String, server: String): Promise<List<OpenGroupMessageV2>, Exception> {
TODO("implement")
}
fun deleteMessage(serverID: Long, room: String, server: String): Promise<Unit, Exception> {
TODO("implement")
}
fun getDeletedMessages(room: String, server: String): Promise<List<Long>, Exception> {
TODO("implement")
}
fun getModerators(room: String, server: String): Promise<List<String>, Exception> {
TODO("implement")
}
fun ban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
TODO("implement")
}
fun unban(publicKey: String, room: String, server: String): Promise<Unit, Exception> {
TODO("implement")
}
fun isUserModerator(publicKey: String, room: String, server: String): Promise<Boolean, Exception> {
TODO("implement")
}
fun getDefaultRoomsIfNeeded() {
TODO("implement")
}
fun getInfo(room: String, server: String): Promise<Info, Exception> {
TODO("implement")
}
fun getAllRooms(server: String): Promise<List<Info>, Exception> {
TODO("implement")
}
fun getMemberCount(room: String, server: String): Promise<Long, Exception> {
TODO("implement")
}
}
fun Error.errorDescription() = when (this) {
Error.GENERIC -> "An error occurred."

View File

@ -5,7 +5,7 @@ import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.logging.Log
import org.whispersystems.curve25519.Curve25519
data class OpenGroupV2Message(
data class OpenGroupMessageV2(
val serverID: Long?,
val sender: String?,
val sentTimestamp: Long,
@ -20,7 +20,7 @@ data class OpenGroupV2Message(
private val curve = Curve25519.getInstance(Curve25519.BEST)
}
fun sign(): OpenGroupV2Message? {
fun sign(): OpenGroupMessageV2? {
if (base64EncodedData.isEmpty()) return null
val (publicKey, privateKey) = MessagingConfiguration.shared.storage.getUserKeyPair() ?: return null
@ -41,11 +41,21 @@ data class OpenGroupV2Message(
serverID?.let { jsonMap["server_id"] = serverID }
sender?.let { jsonMap["public_key"] = sender }
base64EncodedSignature?.let { jsonMap["signature"] = base64EncodedSignature }
return jsonMap
}
fun fromJSON(json: Map<String, Any>): OpenGroupV2Message? {
if (!json.containsKey("data") || !json.containsKey("timestamp")) return null
fun fromJSON(json: Map<String, Any>): OpenGroupMessageV2? {
val base64EncodedData = json["data"] as? String ?: return null
val sentTimestamp = json["timestamp"] as? Long ?: return null
val serverID = json["server_id"] as? Long
val sender = json["public_key"] as? String
val base64EncodedSignature = json["signature"] as? String
return OpenGroupMessageV2(serverID = serverID,
sender = sender,
sentTimestamp = sentTimestamp,
base64EncodedData = base64EncodedData,
base64EncodedSignature = base64EncodedSignature
)
}

View File

@ -1,3 +1,3 @@
package org.session.libsignal.service.loki.utilities.mentions
package org.session.libsession.utilities.mentions
data class Mention(val publicKey: String, val displayName: String)

View File

@ -1,5 +1,6 @@
package org.session.libsignal.service.loki.utilities.mentions
package org.session.libsession.utilities.mentions
import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
@ -9,9 +10,9 @@ class MentionsManager(private val userPublicKey: String, private val threadDatab
companion object {
public lateinit var shared: MentionsManager
lateinit var shared: MentionsManager
public fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) {
fun configureIfNeeded(userPublicKey: String, threadDatabase: LokiThreadDatabaseProtocol, userDatabase: LokiUserDatabaseProtocol) {
if (::shared.isInitialized) { return; }
shared = MentionsManager(userPublicKey, threadDatabase, userDatabase)
}
@ -30,11 +31,15 @@ class MentionsManager(private val userPublicKey: String, private val threadDatab
// Prepare
val cache = userPublicKeyCache[threadID] ?: return listOf()
// Gather candidates
val storage = MessagingConfiguration.shared.storage
val publicChat = threadDatabase.getPublicChat(threadID)
val openGroupV2 = storage.getV2OpenGroup(threadID.toString())
var candidates: List<Mention> = cache.mapNotNull { publicKey ->
val displayName: String?
if (publicChat != null) {
displayName = userDatabase.getServerDisplayName(publicChat.id, publicKey)
} else if (openGroupV2 != null) {
displayName = userDatabase.getServerDisplayName(openGroupV2.id, publicKey)
} else {
displayName = userDatabase.getDisplayName(publicKey)
}