Browse Source

Merge pull request #819 from deltachat/ephemeral_messages_ui

Ephemeral messages ui
cyBerta 5 years ago
parent
commit
44f472fa82

+ 8 - 0
DcCore/DcCore/DC/Wrapper.swift

@@ -298,6 +298,14 @@ public class DcContext {
         dc_set_chat_mute_duration(self.contextPointer, UInt32(chatId), Int64(duration))
     }
 
+    public func setChatEphemeralTimer(chatId: Int, duration: Int) {
+        dc_set_chat_ephemeral_timer(self.contextPointer, UInt32(chatId), UInt32(duration))
+    }
+
+    public func getChatEphemeralTimer(chatId: Int) -> Int {
+        return Int(dc_get_chat_ephemeral_timer(self.contextPointer, UInt32(chatId)))
+    }
+
     public func getConfig(_ key: String) -> String? {
         guard let cString = dc_get_config(self.contextPointer, key) else { return nil }
         let value = String(cString: cString)

+ 4 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -100,6 +100,7 @@
 		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		30A2EC36247D72720024ADD8 /* AnimatedImageMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A2EC35247D72720024ADD8 /* AnimatedImageMessageCell.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
+		30B0ACFA24AB5B99004D5E29 /* SettingsEphemeralMessageController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B0ACF924AB5B99004D5E29 /* SettingsEphemeralMessageController.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
 		30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2122447285600CE2C90 /* ShareViewController.swift */; };
 		30E8F2162447285600CE2C90 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 30E8F2142447285600CE2C90 /* MainInterface.storyboard */; };
@@ -386,6 +387,7 @@
 		30A2EC35247D72720024ADD8 /* AnimatedImageMessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedImageMessageCell.swift; sourceTree = "<group>"; };
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
 		30AC265E237F1807002A943F /* AvatarHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHelper.swift; sourceTree = "<group>"; };
+		30B0ACF924AB5B99004D5E29 /* SettingsEphemeralMessageController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsEphemeralMessageController.swift; sourceTree = "<group>"; };
 		30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateCheckController.swift; sourceTree = "<group>"; };
 		30E8F2102447285600CE2C90 /* DcShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DcShare.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		30E8F2122447285600CE2C90 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
@@ -928,6 +930,7 @@
 				AECEF03D244F2D55006C90DA /* QrPageController.swift */,
 				30149D9222F21129003C12B5 /* QrViewController.swift */,
 				B21005DA23383664004C70C5 /* SettingsClassicViewController.swift */,
+				30B0ACF924AB5B99004D5E29 /* SettingsEphemeralMessageController.swift */,
 				78E45E3921D3CFBC00D4B15E /* SettingsController.swift */,
 				B20462E32440A4A600367A57 /* SettingsAutodelOverviewController.swift */,
 				B20462E52440C99600367A57 /* SettingsAutodelSetController.swift */,
@@ -1472,6 +1475,7 @@
 				305961E22346125100C80F33 /* MessagesDisplayDelegate.swift in Sources */,
 				305962092346125100C80F33 /* AudioMessageSizeCalculator.swift in Sources */,
 				305961DB2346125100C80F33 /* AudioItem.swift in Sources */,
+				30B0ACFA24AB5B99004D5E29 /* SettingsEphemeralMessageController.swift in Sources */,
 				305962012346125100C80F33 /* PlayButtonView.swift in Sources */,
 				308FEA4C2462F11300FCEAD6 /* FileView.swift in Sources */,
 				B20462E42440A4A600367A57 /* SettingsAutodelOverviewController.swift in Sources */,

+ 18 - 0
deltachat-ios/Controller/ContactDetailViewController.swift

@@ -17,6 +17,7 @@ class ContactDetailViewController: UITableViewController {
         return cell
     }()
 
+
     private lazy var startChatCell: ActionCell = {
         let cell = ActionCell()
         cell.actionColor = SystemColor.blue.uiColor
@@ -25,6 +26,14 @@ class ContactDetailViewController: UITableViewController {
         return cell
     }()
 
+    private lazy var ephemeralMessagesCell: UITableViewCell = {
+        let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
+        cell.textLabel?.text = String.localized("pref_ephemeral_messages")
+        cell.selectionStyle = .none
+        cell.accessoryType = .disclosureIndicator
+        return cell
+    }()
+
     private lazy var blockContactCell: ActionCell = {
         let cell = ActionCell()
         cell.actionTitle = viewModel.contact.isBlocked ? String.localized("menu_unblock_contact") : String.localized("menu_block_contact")
@@ -136,6 +145,8 @@ class ContactDetailViewController: UITableViewController {
             }
         case .chatActions:
             switch viewModel.chatActionFor(row: row) {
+            case .ephemeralMessages:
+                return ephemeralMessagesCell
             case .muteChat:
                 return muteChatCell
             case .archiveChat:
@@ -205,6 +216,8 @@ class ContactDetailViewController: UITableViewController {
     private func handleCellAction(for index: Int) {
         let action = viewModel.chatActionFor(row: index)
         switch action {
+        case .ephemeralMessages:
+            showEphemeralMessagesController()
         case .muteChat:
             if viewModel.chatIsMuted {
                 self.viewModel.context.setChatMuteDuration(chatId: self.viewModel.chatId, duration: 0)
@@ -265,6 +278,11 @@ class ContactDetailViewController: UITableViewController {
         self.present(alert, animated: true, completion: nil)
     }
 
+    private func showEphemeralMessagesController() {
+        let ephemeralMessagesController = SettingsEphemeralMessageController(dcContext: viewModel.context, chatId: viewModel.chatId)
+        navigationController?.pushViewController(ephemeralMessagesController, animated: true)
+    }
+
     private func showMuteAlert() {
         let alert = UIAlertController(title: String.localized("mute"), message: nil, preferredStyle: .safeActionSheet)
         let forever = -1

+ 44 - 13
deltachat-ios/Controller/GroupChatDetailViewController.swift

@@ -9,15 +9,27 @@ class GroupChatDetailViewController: UIViewController {
         case chatActions
     }
 
+    enum ChatAction {
+        case ephemeralMessages
+        case muteChat
+        case archiveChat
+        case leaveGroup
+        case deleteChat
+    }
+
+    private lazy var chatActions: [ChatAction] = {
+        var actions: [ChatAction] = [.muteChat, .archiveChat, .leaveGroup, .deleteChat]
+        if UserDefaults.standard.bool(forKey: "ephemeral_messages") || dcContext.getChatEphemeralTimer(chatId: chatId) > 0 {
+            actions.insert(.ephemeralMessages, at: 0)
+        }
+        return actions
+    }()
+
     private let attachmentsRowGallery = 0
     private let attachmentsRowDocuments = 1
     private let membersRowAddMembers = 0
     private let membersRowQrInvite = 1
     private let memberManagementRows = 2
-    private let chatActionsRowMuteChat = 0
-    private let chatActionsRowArchiveChat = 1
-    private let chatActionsRowLeaveGroup = 2
-    private let chatActionsRowDeleteChat = 3
 
     private let dcContext: DcContext
 
@@ -71,6 +83,14 @@ class GroupChatDetailViewController: UIViewController {
         return header
     }()
 
+    private lazy var ephemeralMessagesCell: UITableViewCell = {
+        let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
+        cell.textLabel?.text = String.localized("pref_ephemeral_messages")
+        cell.selectionStyle = .none
+        cell.accessoryType = .disclosureIndicator
+        return cell
+    }()
+
     private lazy var muteChatCell: ActionCell = {
         let cell = ActionCell()
         cell.actionTitle = self.chat.isMuted ? String.localized("menu_unmute") :  String.localized("menu_mute")
@@ -272,7 +292,7 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
         case .members:
             return groupMemberIds.count + memberManagementRows
         case .chatActions:
-            return 4
+            return chatActions.count
         }
     }
 
@@ -331,13 +351,16 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
             contactCell.updateCell(cellViewModel: cellViewModel)
             return contactCell
         case .chatActions:
-            if row == chatActionsRowMuteChat {
+            switch chatActions[row] {
+            case .ephemeralMessages:
+                return ephemeralMessagesCell
+            case .muteChat:
                 return muteChatCell
-            } else if row == chatActionsRowArchiveChat {
+            case .archiveChat:
                 return archiveChatCell
-            } else if row == chatActionsRowLeaveGroup {
+            case .leaveGroup:
                 return leaveGroupCell
-            } else if row == chatActionsRowDeleteChat {
+            case .deleteChat:
                 return deleteChatCell
             }
         }
@@ -366,18 +389,21 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
                 showContactDetail(of: member.id)
             }
         case .chatActions:
-            if row == chatActionsRowMuteChat {
+            switch chatActions[row] {
+            case .ephemeralMessages:
+                showEphemeralMessagesController()
+            case .muteChat:
                 if chat.isMuted {
                     dcContext.setChatMuteDuration(chatId: chatId, duration: 0)
                     muteChatCell.actionTitle = String.localized("menu_mute")
                 } else {
                     showMuteAlert()
                 }
-            } else if row == chatActionsRowArchiveChat {
+            case .archiveChat:
                 toggleArchiveChat()
-            } else if row == chatActionsRowLeaveGroup {
+            case .leaveGroup:
                 showLeaveGroupConfirmationAlert()
-            } else if row == chatActionsRowDeleteChat {
+            case .deleteChat:
                 showDeleteChatConfirmationAlert()
             }
         }
@@ -447,6 +473,11 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou
         self.tableView.deleteRows(at: [indexPath], with: .automatic)
         updateHeader()  // to display correct group size
     }
+
+    private func showEphemeralMessagesController() {
+        let ephemeralMessagesController = SettingsEphemeralMessageController(dcContext: dcContext, chatId: chatId)
+        navigationController?.pushViewController(ephemeralMessagesController, animated: true)
+    }
 }
 
 // MARK: - alerts

+ 7 - 0
deltachat-ios/Controller/SettingsController.swift

@@ -383,6 +383,13 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
             UserDefaults.standard.set(!locationStreaming, forKey: "location_streaming")
         }))
 
+        let ephemeralMessages = UserDefaults.standard.bool(forKey: "ephemeral_messages")
+        let ephemeralTitle = ephemeralMessages ?
+            "Disable ephemeral messages" : "Enable ephemeral messages"
+        alert.addAction(UIAlertAction(title: ephemeralTitle, style: .default, handler: { _ in
+            UserDefaults.standard.set(!ephemeralMessages, forKey: "ephemeral_messages")
+        }))
+
         let logAction = UIAlertAction(title: String.localized("pref_view_log"), style: .default, handler: { [weak self] _ in
             self?.showDebugToolkit()
         })

+ 110 - 0
deltachat-ios/Controller/SettingsEphemeralMessageController.swift

@@ -0,0 +1,110 @@
+import UIKit
+import DcCore
+class SettingsEphemeralMessageController: UITableViewController {
+
+    var dcContext: DcContext
+    var chatId: Int
+    var currentIndex: Int = 0
+
+    private lazy var options: [Int] = {
+        return [0, Time.thirtySeconds, Time.oneMinute, Time.oneHour, Time.oneDay, Time.oneWeek, Time.fourWeeks]
+    }()
+
+    private lazy var cancelButton: UIBarButtonItem = {
+        let button =  UIBarButtonItem(title: String.localized("cancel"), style: .plain, target: self, action: #selector(cancelButtonPressed))
+        return button
+    }()
+
+    private lazy var okButton: UIBarButtonItem = {
+        let button =  UIBarButtonItem(title: String.localized("ok"), style: .done, target: self, action: #selector(okButtonPressed))
+        return button
+    }()
+
+    private var staticCells: [UITableViewCell] {
+        return options.map({
+            let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
+            cell.textLabel?.text = SettingsEphemeralMessageController.getValString(val: $0)
+            cell.selectionStyle = .none
+            return cell
+        })
+    }
+
+    init(dcContext: DcContext, chatId: Int) {
+        self.dcContext = dcContext
+        self.chatId = chatId
+        super.init(style: .grouped)
+        self.currentIndex = self.options.index(of: dcContext.getChatEphemeralTimer(chatId: chatId)) ?? 0
+        self.title = String.localized("pref_ephemeral_messages")
+        hidesBottomBarWhenPushed = true
+
+    }
+
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        navigationItem.leftBarButtonItem = cancelButton
+        navigationItem.rightBarButtonItem = okButton
+    }
+
+    public static func getValString(val: Int) -> String {
+        switch val {
+        case 0:
+            return String.localized("off")
+        case Time.thirtySeconds:
+            return String.localized("after_30_seconds")
+        case Time.oneMinute:
+            return String.localized("after_1_minute")
+        case Time.oneHour:
+            return String.localized("autodel_after_1_hour")
+        case Time.oneDay:
+            return String.localized("autodel_after_1_day")
+        case Time.oneWeek:
+            return String.localized("autodel_after_1_week")
+        case Time.fourWeeks:
+            return String.localized("autodel_after_4_weeks")
+        default:
+            return "Err"
+        }
+    }
+
+    @objc private func cancelButtonPressed() {
+        navigationController?.popViewController(animated: true)
+    }
+
+    @objc private func okButtonPressed() {
+        dcContext.setChatEphemeralTimer(chatId: chatId, duration: options[currentIndex])
+        navigationController?.popViewController(animated: true)
+    }
+
+
+    // MARK: - Table view data source
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        return 1
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return options.count
+    }
+
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let oldSelectedCell = tableView.cellForRow(at: IndexPath.init(row: currentIndex, section: 0))
+        oldSelectedCell?.accessoryType = .none
+
+        let newSelectedCell = tableView.cellForRow(at: IndexPath.init(row: indexPath.row, section: 0))
+        newSelectedCell?.accessoryType = .checkmark
+
+        currentIndex = indexPath.row
+    }
+
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = staticCells[indexPath.row]
+        if currentIndex == indexPath.row {
+            cell.accessoryType = .checkmark
+        }
+        return cell
+    }
+}

+ 3 - 0
deltachat-ios/Helper/Constants.swift

@@ -22,6 +22,8 @@ struct Constants {
 }
 
 struct Time {
+    static let thirtySeconds = 30
+    static let oneMinute = 60
     static let twoMinutes = 2 * 60
     static let fiveMinutes = 5 * 60
     static let thirtyMinutes = 30 * 6
@@ -30,4 +32,5 @@ struct Time {
     static let sixHours = 6 * 60 * 60
     static let oneDay = 24 * 60 * 60
     static let oneWeek = 7 * 24 * 60 * 60
+    static let fourWeeks = 4 * 7 * 24 * 60 * 60
 }

+ 4 - 0
deltachat-ios/ViewModel/ContactDetailViewModel.swift

@@ -13,6 +13,7 @@ class ContactDetailViewModel {
     }
 
     enum ChatAction {
+        case ephemeralMessages
         case muteChat
         case archiveChat
         case blockContact
@@ -51,6 +52,9 @@ class ContactDetailViewModel {
 
         if chatId != 0 {
             chatActions = [.muteChat, .archiveChat, .blockContact, .deleteChat]
+            if UserDefaults.standard.bool(forKey: "ephemeral_messages") || dcContext.getChatEphemeralTimer(chatId: chatId) > 0 {
+                chatActions.insert(.ephemeralMessages, at: 0)
+            }
         } else {
             chatActions = [.blockContact]
         }

+ 3 - 0
deltachat-ios/en.lproj/Localizable.strings

@@ -656,3 +656,6 @@
 "a11y_voice_message_hint_ios" = "After recording double-tap to send. To discard the recording scrub left-right with two fingers.";
 "login_error_no_internet_connection" = "No internet connection, can\'t log in to your server.";
 "share_account_not_configured" = "Account is not configured.";
+"pref_ephemeral_messages" = "Self-destructing messages";
+"after_30_seconds" = "After 30 seconds";
+"after_1_minute" = "After 1 minute";

+ 1 - 1
deltachat-ios/libraries/deltachat-core-rust

@@ -1 +1 @@
-Subproject commit 6a4b6fddac15f425e0037ebc5c0358e61a8ba269
+Subproject commit 56518420bced2472e18fc093b1a6816c19ce6a1b

+ 3 - 0
tools/untranslated.xml

@@ -8,4 +8,7 @@
     <string name="a11y_voice_message_hint_ios">After recording double-tap to send. To discard the recording scrub left-right with two fingers.</string>
     <string name="login_error_no_internet_connection">No internet connection, can\'t log in to your server.</string>
     <string name="share_account_not_configured">Account is not configured.</string>
+    <string name="pref_ephemeral_messages">Self-destructing messages</string>
+    <string name="after_30_seconds">After 30 seconds</string>
+    <string name="after_1_minute">After 1 minute</string>
 </resources>