Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mentions toggle #2434

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add option to toggle mention notifications as replies or reactions in groups
- Access system notification settings directly from the new notification settings
- Access your favorite apps and chats from the Homescreen using our shiny new widget (#2406, #2449)
- Add and remove apps and chats to that widget (#2426, #2449)
- Don't show message-input when forwarding (#2435)
Expand Down
34 changes: 18 additions & 16 deletions DcNotificationService/NotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,31 @@ class NotificationService: UNNotificationServiceExtension {
if event.id == DC_EVENT_INCOMING_MSG {
let dcContext = dcAccounts.get(id: event.accountId)
let chat = dcContext.getChat(chatId: event.data1Int)
if !dcContext.isMuted() && !chat.isMuted {
if !dcContext.isMuted() {
let msg = dcContext.getMessage(id: event.data2Int)
let sender = msg.getSenderName(dcContext.getContact(id: msg.fromContactId))
if chat.isGroup {
bestAttemptContent.title = chat.name
bestAttemptContent.body = "\(sender): " + (msg.summary(chars: 80) ?? "")
} else {
bestAttemptContent.title = sender
bestAttemptContent.body = msg.summary(chars: 80) ?? ""
}
bestAttemptContent.userInfo["account_id"] = dcContext.id
bestAttemptContent.userInfo["chat_id"] = chat.id
bestAttemptContent.userInfo["message_id"] = msg.id
if !chat.isMuted || (chat.isGroup && msg.isReplyToSelf && dcContext.isMentionsEnabled) {
let sender = msg.getSenderName(dcContext.getContact(id: msg.fromContactId))
if chat.isGroup {
bestAttemptContent.title = chat.name
bestAttemptContent.body = "\(sender): " + (msg.summary(chars: 80) ?? "")
} else {
bestAttemptContent.title = sender
bestAttemptContent.body = msg.summary(chars: 80) ?? ""
}
bestAttemptContent.userInfo["account_id"] = dcContext.id
bestAttemptContent.userInfo["chat_id"] = chat.id
bestAttemptContent.userInfo["message_id"] = msg.id

uniqueChats["\(dcContext.id)-\(chat.id)"] = bestAttemptContent.title
messageCount += 1
uniqueChats["\(dcContext.id)-\(chat.id)"] = bestAttemptContent.title
messageCount += 1
}
}
} else if event.id == DC_EVENT_INCOMING_REACTION {
let dcContext = dcAccounts.get(id: event.accountId)
if !dcContext.isMuted() {
let msg = dcContext.getMessage(id: event.data2Int)
let chat = dcContext.getChat(chatId: msg.chatId)
if !chat.isMuted {
if !chat.isMuted || (chat.isGroup && dcContext.isMentionsEnabled) {
let sender = dcContext.getContact(id: event.data1Int).displayName
let summary = (msg.summary(chars: 80) ?? "")
bestAttemptContent.title = chat.name
Expand All @@ -94,7 +96,7 @@ class NotificationService: UNNotificationServiceExtension {
if !dcContext.isMuted() {
let msg = dcContext.getMessage(id: event.data2Int)
let chat = dcContext.getChat(chatId: msg.chatId)
if !chat.isMuted {
if !chat.isMuted || (chat.isGroup && dcContext.isMentionsEnabled) {
bestAttemptContent.title = chat.name
bestAttemptContent.body = msg.getWebxdcAppName() + ": " + event.data2String
bestAttemptContent.userInfo["account_id"] = dcContext.id
Expand Down
4 changes: 4 additions & 0 deletions deltachat-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
B20462E42440A4A600367A57 /* AutodelOverviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E32440A4A600367A57 /* AutodelOverviewViewController.swift */; };
B20462E62440C99600367A57 /* AutodelOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E52440C99600367A57 /* AutodelOptionsViewController.swift */; };
B206C2AB2B7B8088003ACBE6 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */; };
B20A907B2D05112800F44463 /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20A907A2D05112700F44463 /* NotificationsViewController.swift */; };
B21005DB23383664004C70C5 /* EmailOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21005DA23383664004C70C5 /* EmailOptionsViewController.swift */; };
B2172F3C29C125F2002C289E /* AdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2172F3B29C125F2002C289E /* AdvancedViewController.swift */; };
B259D64329B771D5008FB706 /* BackupTransferViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B259D64229B771D5008FB706 /* BackupTransferViewController.swift */; };
Expand Down Expand Up @@ -548,6 +549,7 @@
B209B2042B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
B209B2052B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
B209B2062B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
B20A907A2D05112700F44463 /* NotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = "<group>"; };
B21005DA23383664004C70C5 /* EmailOptionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailOptionsViewController.swift; sourceTree = "<group>"; };
B2172F3B29C125F2002C289E /* AdvancedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedViewController.swift; sourceTree = "<group>"; };
B2537DD625E2F92F0010D739 /* ckb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ckb; path = ckb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1178,6 +1180,7 @@
D8CF2DDB2CDD110F001C2352 /* Proxy */,
78E45E3921D3CFBC00D4B15E /* SettingsViewController.swift */,
B2D4B63A29C38D1900B47DA8 /* ChatsAndMediaViewController.swift */,
B20A907A2D05112700F44463 /* NotificationsViewController.swift */,
B2172F3B29C125F2002C289E /* AdvancedViewController.swift */,
AEE6EC472283045D00EDC689 /* SelfProfileViewController.swift */,
B21005DA23383664004C70C5 /* EmailOptionsViewController.swift */,
Expand Down Expand Up @@ -1779,6 +1782,7 @@
70B8882E2091B8550074812E /* ContactCell.swift in Sources */,
305961CD2346125100C80F33 /* UIEdgeInsets+Extensions.swift in Sources */,
30EF7324252FF15F00E2C54A /* MessageLabel.swift in Sources */,
B20A907B2D05112800F44463 /* NotificationsViewController.swift in Sources */,
30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */,
3080A034277DE30100E74565 /* NSNotification+Extensions.swift in Sources */,
3080A01B277DDB8A00E74565 /* InputPlugin.swift in Sources */,
Expand Down
145 changes: 145 additions & 0 deletions deltachat-ios/Controller/Settings/NotificationsViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import UIKit
import DcCore
import Intents

internal final class NotificationsViewController: UITableViewController {

private struct SectionConfigs {
let headerTitle: String?
let footerTitle: String?
let cells: [UITableViewCell]
}

private enum CellTags: Int {
case defaultTagValue = 0
case systemSettings
}

private var dcContext: DcContext
internal let dcAccounts: DcAccounts

// MARK: - cells
private lazy var notificationsCell: SwitchCell = {
return SwitchCell(
textLabel: String.localized("pref_notifications"),
on: !dcContext.isMuted(),
action: { [weak self] cell in
guard let self else { return }

dcContext.setMuted(!cell.isOn)
if cell.isOn {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.registerForNotifications()
}
} else {
NotificationManager.removeAllNotifications()
}

updateCells()
NotificationManager.updateBadgeCounters()
NotificationCenter.default.post(name: Event.messagesChanged, object: nil, userInfo: ["message_id": Int(0), "chat_id": Int(0)])
})
}()

private lazy var mentionsCell: SwitchCell = {
return SwitchCell(
textLabel: String.localized("pref_mention_notifications"),
on: false, // set in updateCells()
action: { [weak self] cell in
self?.dcContext.setMentionsEnabled(cell.isOn)
})
}()

private lazy var systemSettingsCell: UITableViewCell = {
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.tag = CellTags.systemSettings.rawValue
cell.textLabel?.text = String.localized("system_settings")
cell.accessoryType = .disclosureIndicator
return cell
}()

private lazy var sections: [SectionConfigs] = {
let preferencesSection = SectionConfigs(
headerTitle: nil,
footerTitle: String.localized("pref_mention_notifications_explain"),
cells: [notificationsCell, mentionsCell]
)
let systemSettingsSection = SectionConfigs(
headerTitle: nil,
footerTitle: String.localized("system_settings_notify_explain_ios"),
cells: [systemSettingsCell]
)
return [preferencesSection, systemSettingsSection]
}()

init(dcAccounts: DcAccounts) {
self.dcContext = dcAccounts.getSelected()
self.dcAccounts = dcAccounts
super.init(style: .insetGrouped)
hidesBottomBarWhenPushed = true
}

required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - lifecycle
override func viewDidLoad() {
super.viewDidLoad()
title = String.localized("pref_notifications")
tableView.rowHeight = UITableView.automaticDimension
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateCells()
}

// MARK: - UITableViewDelegate + UITableViewDatasource
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].cells.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return sections[indexPath.section].cells[indexPath.row]
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
}

override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footerTitle
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath), let cellTag = CellTags(rawValue: cell.tag) else { safe_fatalError(); return }
tableView.deselectRow(at: indexPath, animated: false)

switch cellTag {
case .systemSettings:
let urlString = if #available(iOS 16, *) {
UIApplication.openNotificationSettingsURLString
} else if #available(iOS 15.4, *) {
UIApplicationOpenNotificationSettingsURLString
} else {
UIApplication.openSettingsURLString
}

if let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
case .defaultTagValue:
break
}
}

private func updateCells() {
mentionsCell.uiSwitch.isEnabled = !dcContext.isMuted()
mentionsCell.uiSwitch.isOn = !dcContext.isMuted() && dcContext.isMentionsEnabled
}
}
34 changes: 8 additions & 26 deletions deltachat-ios/Controller/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,14 @@ internal final class SettingsViewController: UITableViewController {
return cell
}()

private lazy var notificationSwitch: UISwitch = {
let switchControl = UISwitch()
switchControl.isOn = !dcContext.isMuted()
switchControl.addTarget(self, action: #selector(handleNotificationToggle(_:)), for: .valueChanged)
return switchControl
}()

private lazy var notificationCell: UITableViewCell = {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
cell.tag = CellTags.notifications.rawValue
cell.textLabel?.text = String.localized("pref_notifications")
if #available(iOS 16.0, *) {
cell.imageView?.image = UIImage(systemName: "bell")
}
cell.accessoryView = notificationSwitch
cell.selectionStyle = .none
cell.accessoryType = .disclosureIndicator
return cell
}()

Expand Down Expand Up @@ -220,7 +212,7 @@ internal final class SettingsViewController: UITableViewController {
case .profile: showEditSettingsController()
case .chatsAndMedia: showChatsAndMedia()
case .addAnotherDevice: showBackupProviderViewController()
case .notifications: break
case .notifications: showNotificationsViewController()
case .advanced: showAdvanced()
case .help: showHelp()
case .connectivity: showConnectivity()
Expand Down Expand Up @@ -248,26 +240,12 @@ internal final class SettingsViewController: UITableViewController {
}
}

// MARK: - actions
@objc private func handleNotificationToggle(_ sender: UISwitch) {
dcContext.setMuted(!sender.isOn)
if sender.isOn {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.registerForNotifications()
}
} else {
NotificationManager.removeAllNotifications()
}

NotificationManager.updateBadgeCounters()
NotificationCenter.default.post(name: Event.messagesChanged, object: nil, userInfo: ["message_id": Int(0), "chat_id": Int(0)])
}

// MARK: - updates
private func updateCells() {
profileCell.updateCell(cellViewModel: ProfileViewModel(context: dcContext))
connectivityCell.detailTextLabel?.text = DcUtils.getConnectivityString(dcContext: dcContext,
connectedString: String.localized("connectivity_connected"))
notificationCell.detailTextLabel?.text = String.localized(dcContext.isMuted() ? "off" : "on")
}

// MARK: - coordinator
Expand All @@ -280,6 +258,10 @@ internal final class SettingsViewController: UITableViewController {
navigationController?.pushViewController(ChatsAndMediaViewController(dcAccounts: dcAccounts), animated: true)
}

private func showNotificationsViewController() {
navigationController?.pushViewController(NotificationsViewController(dcAccounts: dcAccounts), animated: true)
}

private func showBackupProviderViewController() {
let alert = UIAlertController(title: String.localized("multidevice_title"), message: String.localized("multidevice_this_creates_a_qr_code"), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
Expand Down
8 changes: 8 additions & 0 deletions deltachat-ios/DC/DcContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,14 @@ public class DcContext {
setConfigBool("is_muted", muted)
}

public var isMentionsEnabled: Bool {
return !getConfigBool("ui.mute_mentions_if_muted")
}

public func setMentionsEnabled(_ enabled: Bool) {
setConfigBool("ui.mute_mentions_if_muted", !enabled)
}

public func getUnreadMessages(chatId: Int) -> Int {
return Int(dc_get_fresh_msg_cnt(contextPointer, UInt32(chatId)))
}
Expand Down
8 changes: 8 additions & 0 deletions deltachat-ios/DC/DcMsg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ public class DcMsg {
}
}

public var isReplyToSelf: Bool {
if let quoteMessage {
return quoteMessage.isFromCurrentSender
} else {
return false
}
}

public var parent: DcMsg? {
guard let msgpointer = dc_msg_get_parent(messagePointer) else { return nil }
return DcMsg(pointer: msgpointer)
Expand Down
8 changes: 4 additions & 4 deletions deltachat-ios/Helper/NotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,16 @@
}

DispatchQueue.global().async { [weak self] in
guard let self, let ui = notification.userInfo, let accountId = ui["account_id"] as? Int else { return }

Check warning on line 96 in deltachat-ios/Helper/NotificationManager.swift

View workflow job for this annotation

GitHub Actions / build

capture of 'notification' with non-sendable type 'Notification' in a `@Sendable` closure; this is an error in the Swift 6 language mode
let eventContext = dcAccounts.get(id: accountId)
if let chatId = ui["chat_id"] as? Int,
let messageId = ui["message_id"] as? Int,
!eventContext.isMuted() {

let chat = eventContext.getChat(chatId: chatId)
let msg = eventContext.getMessage(id: messageId)

if !chat.isMuted {
let msg = eventContext.getMessage(id: messageId)
if !chat.isMuted || (chat.isGroup && msg.isReplyToSelf && eventContext.isMentionsEnabled) {
let fromContact = eventContext.getContact(id: msg.fromContactId)
let sender = msg.getSenderName(fromContact)
let content = UNMutableNotificationContent()
Expand Down Expand Up @@ -131,12 +131,12 @@
}

DispatchQueue.global().async { [weak self] in
guard let self, let ui = notification.userInfo else { return }

Check warning on line 134 in deltachat-ios/Helper/NotificationManager.swift

View workflow job for this annotation

GitHub Actions / build

capture of 'notification' with non-sendable type 'Notification' in a `@Sendable` closure; this is an error in the Swift 6 language mode
let eventContext = dcAccounts.get(id: ui["account_id"] as? Int ?? 0)
if !eventContext.isMuted() {
let msg = eventContext.getMessage(id: ui["msg_id"] as? Int ?? 0)
let chat = eventContext.getChat(chatId: msg.chatId)
if !chat.isMuted {
if !chat.isMuted || (chat.isGroup && eventContext.isMentionsEnabled) {
let contact = eventContext.getContact(id: ui["contact_id"] as? Int ?? 0)
let summary = (msg.summary(chars: 80) ?? "")
let reaction = ui["reaction"] as? String ?? ""
Expand All @@ -163,12 +163,12 @@
}

DispatchQueue.global().async { [weak self] in
guard let self, let ui = notification.userInfo else { return }

Check warning on line 166 in deltachat-ios/Helper/NotificationManager.swift

View workflow job for this annotation

GitHub Actions / build

capture of 'notification' with non-sendable type 'Notification' in a `@Sendable` closure; this is an error in the Swift 6 language mode
let eventContext = dcAccounts.get(id: ui["account_id"] as? Int ?? 0)
if !eventContext.isMuted() {
let msg = eventContext.getMessage(id: ui["msg_id"] as? Int ?? 0)
let chat = eventContext.getChat(chatId: msg.chatId)
if !chat.isMuted {
if !chat.isMuted || (chat.isGroup && eventContext.isMentionsEnabled) {
let content = UNMutableNotificationContent()
content.title = chat.name
content.body = msg.getWebxdcAppName() + ": " + (ui["text"] as? String ?? "")
Expand Down
2 changes: 2 additions & 0 deletions deltachat-ios/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1073,3 +1073,5 @@
"ios_widget_apps_description" = "Shortcuts to In-Chat Utilities and Games";
"ios_remove_from_home_screen" = "Remove from Homescreen";
"ios_add_to_home_screen" = "Add to Homescreen";
"system_settings" = "System Settings";
"system_settings_notify_explain_ios" = "Edit type, badges, preview and more";
2 changes: 2 additions & 0 deletions scripts/untranslated.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@
<string name="ios_widget_apps_description">Shortcuts to In-Chat Utilities and Games</string>
<string name="ios_remove_from_home_screen">Remove from Homescreen</string>
<string name="ios_add_to_home_screen">Add to Homescreen</string>
<string name="system_settings">System Settings</string>
<string name="system_settings_notify_explain_ios">Edit type, badges, preview and more</string>
</resources>
Loading