forked from Ivasoft/mattermost-mobile
Fix iOS Push notification crashes (#7237)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Emm from '@mattermost/react-native-emm';
|
||||
import {Alert, DeviceEventEmitter, Linking, Platform} from 'react-native';
|
||||
import {Alert, AppState, DeviceEventEmitter, Linking, Platform} from 'react-native';
|
||||
import {Notifications} from 'react-native-notifications';
|
||||
|
||||
import {appEntry, pushNotificationEntry, upgradeEntry} from '@actions/remote/entry';
|
||||
@@ -45,7 +45,8 @@ export const initialLaunch = async () => {
|
||||
return launchAppFromNotification(convertToNotificationData(notification!), true);
|
||||
}
|
||||
|
||||
return launchApp({launchType: Launch.Normal, coldStart: notification ? tapped : true});
|
||||
const coldStart = notification ? (tapped || AppState.currentState === 'active') : true;
|
||||
return launchApp({launchType: Launch.Normal, coldStart});
|
||||
};
|
||||
|
||||
const launchAppFromDeepLink = async (deepLinkUrl: string, coldStart = false) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ import NativeNotifications from '@notifications';
|
||||
import {getServerDisplayName} from '@queries/app/servers';
|
||||
import {getCurrentChannelId} from '@queries/servers/system';
|
||||
import {getIsCRTEnabled, getThreadById} from '@queries/servers/thread';
|
||||
import {dismissOverlay, showOverlay} from '@screens/navigation';
|
||||
import {showOverlay} from '@screens/navigation';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {isBetaApp} from '@utils/general';
|
||||
@@ -139,6 +139,7 @@ class PushNotifications {
|
||||
const condition3 = isInThreadScreen && !isSameThreadNotification;
|
||||
|
||||
if (condition1 || condition2 || condition3) {
|
||||
// Dismiss the screen if it's already visible or else it blocks the navigation
|
||||
DeviceEventEmitter.emit(Navigation.NAVIGATION_SHOW_OVERLAY);
|
||||
|
||||
const screen = Screens.IN_APP_NOTIFICATION;
|
||||
@@ -148,9 +149,6 @@ class PushNotifications {
|
||||
serverUrl,
|
||||
};
|
||||
|
||||
// Dismiss the screen if it's already visible or else it blocks the navigation
|
||||
await dismissOverlay(screen);
|
||||
|
||||
showOverlay(screen, passProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ type InAppNotificationProps = {
|
||||
}
|
||||
|
||||
const AUTO_DISMISS_TIME_MILLIS = 5000;
|
||||
const noInsets = {top: 0, bottom: 0, left: 0, right: 0};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
@@ -69,11 +70,10 @@ const InAppNotification = ({componentId, serverName, serverUrl, notification}: I
|
||||
const dismissTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const initial = useSharedValue(-130);
|
||||
const isTablet = useIsTablet();
|
||||
let insets = {top: 0};
|
||||
if (Platform.OS === 'ios') {
|
||||
let insets = useSafeAreaInsets();
|
||||
if (Platform.OS === 'android') {
|
||||
// on Android we disable the safe area provider as it conflicts with the gesture system
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
insets = useSafeAreaInsets();
|
||||
insets = noInsets;
|
||||
}
|
||||
|
||||
const tapped = useRef<boolean>(false);
|
||||
|
||||
@@ -15,7 +15,7 @@ extension Network {
|
||||
// remove existing users in the database
|
||||
users = [User]()
|
||||
let storedUserIds = Database.default.queryUsers(byIds: userIds, forServerUrl: serverUrl)
|
||||
if !(userIds.filter{ !storedUserIds.contains($0) }).isEmpty {
|
||||
if !(userIds.subtracting(storedUserIds)).isEmpty {
|
||||
group.enter()
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
self.fetchUsers(byIds: Array(userIds), forServerUrl: serverUrl) {data, response, error in
|
||||
@@ -29,7 +29,7 @@ extension Network {
|
||||
}
|
||||
|
||||
let storedUsernames = Database.default.queryUsers(byUsernames: usernames, forServerUrl: serverUrl)
|
||||
if !(usernames.filter{ !storedUsernames.contains($0) }).isEmpty {
|
||||
if !(usernames.subtracting(storedUsernames)).isEmpty {
|
||||
group.enter()
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
self.fetchUsers(byUsernames: Array(usernames), forServerUrl: serverUrl) {data, response, error in
|
||||
|
||||
@@ -6,16 +6,19 @@ extension PushNotification {
|
||||
var updatedAt: Double = 0
|
||||
func processResponse(data: Data?, response: URLResponse?, error: Error?) {
|
||||
if let httpResponse = response as? HTTPURLResponse {
|
||||
if (httpResponse.statusCode == 200 && error == nil) {
|
||||
let statusCode = httpResponse.statusCode
|
||||
let errorMessage = error?.localizedDescription ?? ""
|
||||
if (statusCode == 200 && error == nil) {
|
||||
ImageCache.default.insertImage(data, for: senderId, updatedAt: updatedAt, forServer: serverUrl)
|
||||
completionHandler(data)
|
||||
} else {
|
||||
os_log(
|
||||
OSLogType.default,
|
||||
"Mattermost Notifications: Request for profile image failed with status %{public}@ and error %{public}@",
|
||||
httpResponse.statusCode,
|
||||
(error?.localizedDescription ?? "")
|
||||
String(statusCode),
|
||||
errorMessage
|
||||
)
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,16 +73,19 @@ public class PushNotification: NSObject {
|
||||
let url = Network.default.buildApiUrl(ackNotification.serverUrl, endpoint)
|
||||
Network.default.request(
|
||||
url, withMethod: "POST", withBody: jsonData,
|
||||
andHeaders: headers, forServerUrl: ackNotification.serverUrl) { data, response, error in
|
||||
if (error != nil && ackNotification.isIdLoaded) {
|
||||
let backoffInSeconds = self.fibonacciBackoffsInSeconds[self.retryIndex]
|
||||
self.retryIndex += 1
|
||||
andHeaders: headers, forServerUrl: ackNotification.serverUrl) {[weak self] data, response, error in
|
||||
if error != nil && ackNotification.isIdLoaded,
|
||||
let fibonacciBackoffsInSeconds = self?.fibonacciBackoffsInSeconds,
|
||||
let retryIndex = self?.retryIndex,
|
||||
fibonacciBackoffsInSeconds.count > retryIndex {
|
||||
let backoffInSeconds = fibonacciBackoffsInSeconds[retryIndex]
|
||||
self?.retryIndex += 1
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + backoffInSeconds, execute: {[weak self] in
|
||||
os_log(
|
||||
OSLogType.default,
|
||||
"Mattermost Notifications: receipt retrieval failed. Retry %{public}@",
|
||||
String(describing: self?.retryIndex)
|
||||
String(retryIndex)
|
||||
)
|
||||
self?.postNotificationReceiptWithRetry(ackNotification, completionHandler: completionHandler)
|
||||
})
|
||||
@@ -94,6 +97,7 @@ public class PushNotification: NSObject {
|
||||
}
|
||||
} catch {
|
||||
os_log(OSLogType.default, "Mattermost Notifications: receipt failed %{public}@", error.localizedDescription)
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ extension Database {
|
||||
SELECT SUM(my.mentions_count) \
|
||||
FROM MyChannel my \
|
||||
INNER JOIN MyChannelSettings mys ON mys.id=my.id \
|
||||
INNER JOIN Channel c ON c.id=my.id \
|
||||
INNER JOIN Channel c INDEXED BY sqlite_autoindex_Channel_1 ON c.id=my.id \
|
||||
WHERE c.delete_at = 0 AND mys.notify_props NOT LIKE '%"mark_unread":"mention"%'
|
||||
"""
|
||||
let mentions = try? db.prepare(stmtString).scalar() as? Double
|
||||
@@ -28,11 +28,12 @@ extension Database {
|
||||
|
||||
public func getThreadMentions(_ db: Connection) -> Int {
|
||||
let stmtString = """
|
||||
SELECT SUM(unread_mentions) \
|
||||
FROM Thread t
|
||||
INNER JOIN Post p ON t.id=p.id \
|
||||
INNER JOIN Channel c ON p.channel_id=c.id
|
||||
WHERE c.delete_at = 0
|
||||
SELECT SUM(t.unread_mentions) \
|
||||
FROM Thread t \
|
||||
INNER JOIN Post p INDEXED BY Post_channel_id ON t.id=p.id \
|
||||
INNER JOIN Channel c ON p.channel_id=c.id \
|
||||
INNER JOIN MyChannelSettings mys ON mys.id=c.id \
|
||||
WHERE c.delete_at = 0 AND mys.notify_props NOT LIKE '%"mark_unread":"mention"%'
|
||||
"""
|
||||
let mentions = try? db.prepare(stmtString).scalar() as? Double
|
||||
return Int(mentions ?? 0)
|
||||
|
||||
@@ -27,7 +27,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
})
|
||||
} else {
|
||||
bestAttemptContent.badge = Gekidou.Database.default.getTotalMentions() as NSNumber
|
||||
os_log(OSLogType.default, "Mattermost Notifications: app in the foreground, no data processed. Will call sendMessageIntent")
|
||||
os_log(OSLogType.default, "Mattermost Notifications: app in use, no data processed. Will call sendMessageIntent")
|
||||
self?.sendMessageIntent()
|
||||
}
|
||||
return
|
||||
@@ -55,10 +55,8 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
if #available(iOSApplicationExtension 15.0, *) {
|
||||
let overrideUsername = notification.userInfo["override_username"] as? String
|
||||
let senderId = notification.userInfo["sender_id"] as? String
|
||||
let sender = overrideUsername ?? senderId
|
||||
|
||||
guard let serverUrl = notification.userInfo["server_url"] as? String,
|
||||
let sender = sender
|
||||
guard let serverUrl = notification.userInfo["server_url"] as? String
|
||||
else {
|
||||
os_log(OSLogType.default, "Mattermost Notifications: No intent created. will call contentHandler to present notification")
|
||||
self.contentHandler?(notification)
|
||||
@@ -66,10 +64,13 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
let overrideIconUrl = notification.userInfo["override_icon_url"] as? String
|
||||
os_log(OSLogType.default, "Mattermost Notifications: Fetching profile Image in server %{public}@ for sender %{public}@", serverUrl, sender)
|
||||
|
||||
PushNotification.default.fetchProfileImageSync(serverUrl, senderId: sender, overrideIconUrl: overrideIconUrl) {[weak self] data in
|
||||
self?.sendMessageIntentCompletion(data)
|
||||
os_log(OSLogType.default, "Mattermost Notifications: Fetching profile Image in server %{public}@ for sender %{public}@", serverUrl, senderId ?? overrideUsername ?? "no sender is set")
|
||||
if senderId != nil || overrideIconUrl != nil {
|
||||
PushNotification.default.fetchProfileImageSync(serverUrl, senderId: senderId ?? "", overrideIconUrl: overrideIconUrl) {[weak self] data in
|
||||
self?.sendMessageIntentCompletion(data)
|
||||
}
|
||||
} else {
|
||||
self.sendMessageIntentCompletion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user