Skip to content

Commit ddcab3f

Browse files
committed
Stop relying on conversationTitle, always use profile data
1 parent 6e67bff commit ddcab3f

21 files changed

+69
-173
lines changed

android/app/src/main/java/com/converse/dev/PushNotificationsService.kt

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ import java.util.*
7070
import kotlin.reflect.full.declaredMemberProperties
7171
import kotlin.reflect.jvm.isAccessible
7272
import kotlin.reflect.jvm.javaField
73+
import androidx.lifecycle.Lifecycle
74+
import androidx.lifecycle.ProcessLifecycleOwner
7375

7476
class PushNotificationsService : FirebaseMessagingService() {
7577
companion object {
@@ -111,6 +113,12 @@ class PushNotificationsService : FirebaseMessagingService() {
111113
return
112114
}
113115

116+
val appIsInForeground = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
117+
val currentAccount = getCurrentAccount(this)
118+
if (appIsInForeground && currentAccount !== null && currentAccount.lowercase() == notificationData.account.lowercase()) {
119+
Log.d(TAG, "Preventing notification for ${notificationData.account} because user is on it")
120+
return
121+
}
114122
Log.d(TAG, "INSTANTIATED XMTP CLIENT FOR ${notificationData.contentTopic}")
115123

116124
val encryptedMessageData = Base64.decode(notificationData.message, Base64.NO_WRAP)

android/app/src/main/java/com/converse/dev/xmtp/Conversations.kt

-12
Original file line numberDiff line numberDiff line change
@@ -135,18 +135,6 @@ fun persistNewConversation(appContext:Context, account: String, conversation: Co
135135
}
136136
}
137137

138-
fun getSavedConversationTitle(appContext: Context, topic: String): String {
139-
try {
140-
Log.d("PushNotificationsService", "Getting data conversation-$topic")
141-
val mmkv = getMmkv(appContext)
142-
val savedConversationDict = mmkv?.decodeString("conversation-$topic") ?: return ""
143-
val parsedConversationDict = Klaxon().parse<ConversationDictData>(savedConversationDict)
144-
return parsedConversationDict?.title ?: parsedConversationDict?.shortAddress ?: ""
145-
} catch (e: Exception) {
146-
return ""
147-
}
148-
}
149-
150138
suspend fun getNewGroup(xmtpClient: Client, contentTopic: String): Group? {
151139
return try {
152140
if (isGroupWelcomeTopic(contentTopic)) {

android/app/src/main/java/com/converse/dev/xmtp/Messages.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,18 @@ suspend fun handleOngoingConversationMessage(
192192
}
193193

194194
val message = conversation.decode(envelope)
195-
val contentTopic = envelope.contentTopic
196-
var conversationTitle = getSavedConversationTitle(appContext, contentTopic)
195+
var conversationTitle = ""
197196

198197
val decodedMessageResult = handleMessageByContentType(
199198
appContext,
200199
message,
201200
xmtpClient,
202201
)
203202

203+
decodedMessageResult.senderAddress?.let {
204+
conversationTitle = shortAddress(it)
205+
}
206+
204207
val senderProfile =
205208
decodedMessageResult.senderAddress?.let { getProfile(appContext, xmtpClient.address, it) }
206209
var senderAvatar: String? = null
@@ -210,7 +213,7 @@ suspend fun handleOngoingConversationMessage(
210213
}
211214

212215
val shouldShowNotification = if (decodedMessageResult.senderAddress != xmtpClient.address && !decodedMessageResult.forceIgnore && decodedMessageResult.content != null) {
213-
if (conversationTitle.isEmpty() && decodedMessageResult.senderAddress != null) {
216+
if ((conversationTitle == null || conversationTitle!!.isEmpty()) && decodedMessageResult.senderAddress != null) {
214217
conversationTitle = shortAddress(decodedMessageResult.senderAddress)
215218
}
216219
true

components/Conversation/ConversationTitle.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export default function ConversationTitle({
7777
conversation.peerAddress !== previousConversation.peerAddress ||
7878
conversation.context?.conversationId !==
7979
previousConversation.context?.conversationId ||
80-
conversation.conversationTitle ||
80+
conversationName(conversation) ||
8181
(previousConversation.isGroup &&
8282
conversation.isGroup &&
8383
previousConversation.groupName !== conversation.groupName)

components/PinnedConversations/PinnedConversation.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { backgroundColor, textSecondaryColor } from "@styles/colors";
55
import { AvatarSizes } from "@styles/sizes";
66
import { ConversationWithLastMessagePreview } from "@utils/conversation";
77
import { showUnreadOnConversation } from "@utils/conversation/showUnreadOnConversation";
8+
import { conversationName } from "@utils/str";
89
import { FC, useCallback, useMemo } from "react";
910
import {
1011
StyleSheet,
@@ -41,7 +42,7 @@ export const PinnedConversation: FC<Props> = ({ conversation }) => {
4142
const { data: groupPhoto } = useGroupPhotoQuery(account, topic, {
4243
refetchOnMount: false,
4344
});
44-
const title = isGroup ? groupName : conversation.conversationTitle;
45+
const title = isGroup ? groupName : conversationName(conversation);
4546
const socials = getProfile(conversation.peerAddress, profiles)?.socials;
4647
const avatar = isGroup ? groupPhoto : getPreferredAvatar(socials);
4748
const setPinnedConversations = useChatStore((s) => s.setPinnedConversations);

data/helpers/conversations/upsertConversations.ts

+1-17
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ import {
66
navigateToTopicWithRetry,
77
topicToNavigateTo,
88
} from "../../../utils/navigation";
9-
import { saveConversationIdentifiersForNotifications } from "../../../utils/notifications";
10-
import { getPreferredName, getProfile } from "../../../utils/profile";
119
import { getRepository } from "../../db";
1210
import { getExistingDataSource } from "../../db/datasource";
1311
import { Conversation } from "../../db/entities/conversationEntity";
1412
import { upsertRepository } from "../../db/upsert";
1513
import { xmtpConversationToDb } from "../../mappers";
16-
import { getChatStore, getProfilesStore } from "../../store/accountsStore";
14+
import { getChatStore } from "../../store/accountsStore";
1715
import { XmtpConversation } from "../../store/chatStore";
1816
import { refreshProfilesIfNeeded } from "../profiles/profilesUpdate";
1917

@@ -81,26 +79,12 @@ const setupAndSaveConversations = async (
8179
const alreadyConversationInDbWithTopic =
8280
alreadyConversationsByTopic[conversation.topic];
8381

84-
if (!conversation.isGroup) {
85-
const profileSocials = getProfile(
86-
conversation.peerAddress,
87-
getProfilesStore(account).getState().profiles
88-
)?.socials;
89-
90-
conversation.conversationTitle = getPreferredName(
91-
profileSocials,
92-
conversation.peerAddress,
93-
conversation.context?.conversationId
94-
);
95-
}
96-
9782
conversation.readUntil =
9883
conversation.readUntil ||
9984
alreadyConversationInDbWithTopic?.readUntil ||
10085
0;
10186

10287
conversationsToUpsert.push(xmtpConversationToDb(conversation));
103-
saveConversationIdentifiersForNotifications(conversation);
10488
});
10589

10690
// Let's save by batch to avoid hermes issues

data/helpers/profiles/profilesUpdate.ts

+2-49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { getCleanAddress } from "@utils/eth";
2-
import logger from "@utils/logger";
32

43
import { getProfilesForAddresses } from "../../../utils/api";
5-
import { saveConversationIdentifiersForNotifications } from "../../../utils/notifications";
6-
import { getPreferredName, getProfile } from "../../../utils/profile";
4+
import { getProfile } from "../../../utils/profile";
75
import { getChatStore, getProfilesStore } from "../../store/accountsStore";
86
import { XmtpConversation } from "../../store/chatStore";
97
import { ProfileSocials } from "../../store/profilesStore";
@@ -17,7 +15,6 @@ export const updateProfilesForConvos = async (
1715
account: string,
1816
profilesWithGroups: Map<string, XmtpConversation[]>
1917
) => {
20-
const updates: ConversationHandlesUpdate[] = [];
2118
let batch: string[] = [];
2219
let rest = Array.from(profilesWithGroups.keys());
2320

@@ -40,40 +37,7 @@ export const updateProfilesForConvos = async (
4037
};
4138
}
4239
getProfilesStore(account).getState().setProfiles(socialsToDispatch);
43-
const handleConversation = async (conversation: XmtpConversation) => {
44-
if (conversation.isGroup) return;
45-
const currentTitle = conversation.conversationTitle;
46-
let updated = false;
47-
try {
48-
const profileForConversation =
49-
profilesByAddress[conversation.peerAddress];
50-
51-
const newTitle = getPreferredName(
52-
profileForConversation,
53-
conversation.peerAddress,
54-
conversation.context?.conversationId
55-
);
56-
57-
if (newTitle !== currentTitle) {
58-
updated = true;
59-
}
60-
conversation.conversationTitle = newTitle;
61-
} catch (e) {
62-
// Error (probably rate limited)
63-
logger.warn("Could not resolve handles:", conversation.peerAddress, e);
64-
}
65-
66-
updates.push({ conversation, updated });
67-
saveConversationIdentifiersForNotifications(conversation);
68-
};
69-
const allGroups: XmtpConversation[] = [];
70-
batch.forEach((address) => {
71-
allGroups.push(...(profilesWithGroups.get(address) || []));
72-
});
73-
await Promise.all(allGroups.map(handleConversation));
7440
}
75-
76-
return updates;
7741
};
7842

7943
export const refreshProfileForAddress = async (
@@ -134,17 +98,6 @@ export const refreshProfilesIfNeeded = async (account: string) => {
13498
});
13599

136100
if (staleProfilesWithConversations.size > 0) {
137-
updateProfilesForConvos(account, staleProfilesWithConversations).then(
138-
(resolveResult) => {
139-
const updatedConversations = resolveResult
140-
.filter((r) => r.updated)
141-
.map((r) => r.conversation);
142-
if (updatedConversations.length > 0) {
143-
getChatStore(account)
144-
.getState()
145-
.setConversations(updatedConversations);
146-
}
147-
}
148-
);
101+
updateProfilesForConvos(account, staleProfilesWithConversations);
149102
}
150103
};

data/index.ts

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "reflect-metadata";
22

3+
import logger from "@utils/logger";
34
import { getProfile } from "@utils/profile";
45

56
import { getRepository } from "./db";
@@ -33,6 +34,10 @@ export const loadDataToContext = async (account: string) => {
3334
isActive: getTypeormBoolValue(c.isActive),
3435
}));
3536

37+
logger.debug(
38+
`[InitialData] ${account}: Loading ${conversationsWithMessages.length} conversations from local db`
39+
);
40+
3641
const conversationsMessages: Message[][] = await Promise.all(
3742
conversationsWithMessages.map((c) =>
3843
messageRepository
@@ -48,6 +53,15 @@ export const loadDataToContext = async (account: string) => {
4853
)
4954
);
5055

56+
const totalMessagesCount = conversationsMessages.reduce(
57+
(count, conversation) => count + conversation.length,
58+
0
59+
);
60+
61+
logger.debug(
62+
`[InitialData] ${account}: Loading ${totalMessagesCount} messages from local db`
63+
);
64+
5165
conversationsWithMessages.forEach((conversation, index) => {
5266
// If no limit => ASC then no reverse
5367
conversation.messages = conversationsMessages[index]

data/mappers.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,7 @@ export const xmtpConversationFromDb = (
104104
}
105105

106106
const conversationTitle = dbConversation.peerAddress
107-
? getPreferredName(
108-
socials,
109-
dbConversation.peerAddress,
110-
dbConversation.contextConversationId
111-
)
107+
? getPreferredName(socials, dbConversation.peerAddress)
112108
: undefined;
113109

114110
const hasOneMessageFromMe = !!dbConversation.messages?.find(

data/store/chatStore.ts

-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ type XmtpConversationShared = {
3939
context?: XmtpConversationContext;
4040
messages: Map<string, XmtpMessage>;
4141
messagesIds: string[];
42-
conversationTitle?: string | null;
4342
messageDraft?: string;
4443
mediaPreview?: MediaPreview;
4544
readUntil: number; // UNUSED
@@ -388,7 +387,6 @@ export const initChatStore = (account: string) => {
388387
messages: oldMessages,
389388
messagesIds: oldMessagesIds,
390389
hasOneMessageFromMe: oldHasOneMessageFromMe,
391-
conversationTitle: existingConversation.conversationTitle,
392390
};
393391
newState.lastUpdateAt = now();
394392
delete newState.conversations[oldTopic];

ios/ConverseNotificationExtension/NotificationService.swift

-2
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,6 @@ class NotificationService: UNNotificationServiceExtension {
209209
sentryTrackMessage(message: "NOTIFICATION_TIMEOUT", extras: ["body": bestAttemptContent?.userInfo["body"]])
210210
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
211211
if let body = bestAttemptContent.userInfo["body"] as? [String: Any], let contentTopic = body["contentTopic"] as? String {
212-
let conversationTitle = getSavedConversationTitle(contentTopic: contentTopic);
213-
bestAttemptContent.title = conversationTitle;
214212
incrementBadge(for: bestAttemptContent)
215213
}
216214
contentHandler(bestAttemptContent)

ios/ConverseNotificationExtension/Profile.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import web3
1111

1212
func getProfile(account: String, address: String) async -> Profile? {
1313
var profileState = getProfilesStore(account: account)?.state
14-
var lowercasedAddress = address.lowercased()
15-
var formattedAddress = EthereumAddress(lowercasedAddress).toChecksumAddress()
14+
let lowercasedAddress = address.lowercased()
15+
let formattedAddress = EthereumAddress(lowercasedAddress).toChecksumAddress()
1616
if let profile = profileState?.profiles?[address] ?? profileState?.profiles?[formattedAddress] ?? profileState?.profiles?[lowercasedAddress] {
1717
return profile
1818
}

ios/ConverseNotificationExtension/Xmtp/Conversations.swift

-13
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,6 @@ func saveConversation(account: String, topic: String, peerAddress: String, creat
7272
mmkv?.set(encodedString!, forKey: "saved-notifications-conversations")
7373
}
7474

75-
func getSavedConversationTitle(contentTopic: String)-> String {
76-
let mmkv = getMmkv()
77-
let conversationDictString = mmkv?.string(forKey: "conversation-\(contentTopic)")
78-
if let data = conversationDictString?.data(using: .utf8) {
79-
if let conversationDict = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] {
80-
let shortAddress = conversationDict["shortAddress"]
81-
let title = conversationDict["title"]
82-
return "\(title ?? shortAddress ?? "")"
83-
}
84-
}
85-
return "";
86-
}
87-
8875
func getPersistedConversation(xmtpClient: XMTP.Client, contentTopic: String) async -> XMTP.Conversation? {
8976
let secureMmkv = getSecureMmkvForAccount(account: xmtpClient.address)
9077
if let mmkv = secureMmkv {

ios/ConverseNotificationExtension/Xmtp/Messages.swift

+15-6
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,13 @@ func handleNewConversationFirstMessage(xmtpClient: XMTP.Client, apiURI: String?,
5858
print("[NotificationExtension] Not showing a notification")
5959
break
6060
} else if let content = decodedMessageResult.content {
61-
bestAttemptContent.title = shortAddress(address: try conversation.peerAddress)
61+
let senderAddress = try conversation.peerAddress
62+
let conversationTitle: String? = nil
63+
if let senderProfile = await getProfile(account: xmtpClient.address, address: senderAddress) {
64+
bestAttemptContent.title = getPreferredName(address: senderAddress, socials: senderProfile.socials)
65+
} else {
66+
bestAttemptContent.title = shortAddress(address: senderAddress)
67+
}
6268
bestAttemptContent.body = content
6369
shouldShowNotification = true
6470
messageId = decodedMessageResult.id // @todo probably remove this?
@@ -198,15 +204,15 @@ func handleGroupMessage(xmtpClient: XMTP.Client, envelope: XMTP.Envelope, apiURI
198204
func handleOngoingConversationMessage(xmtpClient: XMTP.Client, envelope: XMTP.Envelope, bestAttemptContent: inout UNMutableNotificationContent, body: [String: Any]) async -> (shouldShowNotification: Bool, messageId: String?, messageIntent: INSendMessageIntent?) {
199205
var shouldShowNotification = false
200206
let contentTopic = envelope.contentTopic
201-
var conversationTitle = getSavedConversationTitle(contentTopic: contentTopic)
207+
var conversationTitle: String? = nil
202208
var messageId: String? = nil
203209
var messageIntent: INSendMessageIntent? = nil
204210

205211
let decodedMessage = try? await decodeMessage(xmtpClient: xmtpClient, envelope: envelope)
206212
// If couldn't decode the message, not showing
207213
if let message = decodedMessage {
208214
let decodedMessageResult = handleMessageByContentType(decodedMessage: message, xmtpClient: xmtpClient);
209-
215+
210216
if decodedMessageResult.senderAddress == xmtpClient.address || decodedMessageResult.forceIgnore {
211217
// Message is from me or a reaction removal, let's drop it
212218
print("[NotificationExtension] Not showing a notification")
@@ -218,11 +224,14 @@ func handleOngoingConversationMessage(xmtpClient: XMTP.Client, envelope: XMTP.En
218224
conversationTitle = getPreferredName(address: senderAddress, socials: senderProfile.socials)
219225
senderAvatar = getPreferredAvatar(socials: senderProfile.socials)
220226
}
221-
222-
if conversationTitle.isEmpty, let senderAddress = decodedMessageResult.senderAddress {
227+
228+
if (conversationTitle == nil), let senderAddress = decodedMessageResult.senderAddress {
223229
conversationTitle = shortAddress(address: senderAddress)
224230
}
225-
bestAttemptContent.title = conversationTitle
231+
if let convoTitle = conversationTitle {
232+
bestAttemptContent.title = convoTitle
233+
}
234+
226235
shouldShowNotification = true
227236
messageId = decodedMessageResult.id
228237
messageIntent = getIncoming1v1MessageIntent(topic: envelope.contentTopic, senderId: decodedMessage?.senderAddress ?? "", senderName: bestAttemptContent.title, senderAvatar: senderAvatar, content: bestAttemptContent.body)

0 commit comments

Comments
 (0)