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

improve saved messages #2527

Merged
merged 9 commits into from
Jan 30, 2025
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Add option to "Save" a message to the messages' context menu (#2527)
- Saved messages are marked by a little star (#2527)
- In the "Saved Messages" chat, messages are shown in context and have an option to go to the original (#2527)
- New group consistency algorithm (#2564)
- The app now requires less storage deduplicating newly received/sent files (#2564)
- Show 'unconnected' and 'updating' states in account switcher (#2553)
Expand Down
2 changes: 2 additions & 0 deletions DcCore/DcCore/Helper/DcColors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public struct DcColors {
public static let providerBrokenBackground = UIColor.systemRed
public static let systemMessageBackgroundColor = UIColor.init(hexString: "65444444")
public static let systemMessageFontColor = UIColor.white
public static let gotoButtonBackgroundColor = UIColor.init(hexString: "65666666")
public static let gotoButtonFontColor = UIColor.init(hexString: "eeeeee")
public static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "f2f2f6"), dark: UIColor.init(hexString: "1a1a1c"))
public static let selectBackground = UIColor.themeColor(light: UIColor.init(hexString: "d2d2d6"), dark: UIColor.init(hexString: "3a3a3c"))
public static let accountSwitchBackgroundColor = UIColor.themeColor(light: UIColor.init(hexString: "65CCCCCC"), dark: UIColor.init(hexString: "65444444"))
Expand Down
51 changes: 44 additions & 7 deletions deltachat-ios/Chat/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate {
private var reactionMessageId: Int?
private var contextMenuVisible = false

private lazy var isGroupChat: Bool = {
return dcContext.getChat(chatId: chatId).isGroup
}()

private lazy var draft: DraftModel = {
return DraftModel(dcContext: dcContext, chatId: chatId)
}()
Expand Down Expand Up @@ -629,11 +625,14 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate {
cell = dequeueCell(ofType: TextMessageCell.self)
}

var showAvatar = isGroupChat && !message.isFromCurrentSender
var showName = isGroupChat
if message.overrideSenderName != nil {
let showAvatar: Bool
let showName: Bool
if message.overrideSenderName != nil || dcChat.isSelfTalk {
showAvatar = !message.isFromCurrentSender
showName = true
} else {
showAvatar = dcChat.isGroup && !message.isFromCurrentSender
showName = dcChat.isGroup
}

cell.baseDelegate = self
Expand Down Expand Up @@ -1690,6 +1689,21 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate {
replyToMessage(at: indexPath)
}

private func toggleSave(at indexPath: IndexPath) {
let message = dcContext.getMessage(id: messageIds[indexPath.row])
if dcChat.isSelfTalk {
if message.originalMessageId != 0 {
dcContext.deleteMessage(msgId: message.id)
} else {
askToDeleteMessage(id: message.id)
}
} else if message.savedMessageId != 0 {
dcContext.deleteMessage(msgId: message.savedMessageId)
} else {
dcContext.saveMessages(with: [messageIds[indexPath.row]])
}
}

private func copyTextToClipboard(at indexPath: IndexPath) {
copyTextToClipboard(ids: [self.messageIds[indexPath.row]])
}
Expand Down Expand Up @@ -1916,6 +1930,16 @@ extension ChatViewController {
UIAction.menuAction(localizationKey: "forward", image: image, indexPath: indexPath, action: forward)
)

if dcChat.isSelfTalk || message.savedMessageId != 0 {
children.append(
UIAction.menuAction(localizationKey: "unsave", systemImageName: "star.slash", indexPath: indexPath, action: toggleSave)
)
} else {
children.append(
UIAction.menuAction(localizationKey: "save_desktop", systemImageName: "star", indexPath: indexPath, action: toggleSave)
)
}

if let link = isLinkTapped(indexPath: indexPath, point: point) {
children.append(
UIAction.menuAction(localizationKey: "menu_copy_link_to_clipboard", systemImageName: "link", indexPath: indexPath, action: { _ in
Expand Down Expand Up @@ -2203,6 +2227,19 @@ extension ChatViewController: BaseMessageCellDelegate {
}
}

@objc func gotoOriginal(indexPath: IndexPath) {
if handleSelection(indexPath: indexPath) { return }

guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let savedMessage = dcContext.getMessage(id: messageIds[indexPath.row])

let originalMessageId = savedMessage.originalMessageId
if originalMessageId != 0 {
let originalMessage = dcContext.getMessage(id: originalMessageId)
appDelegate.appCoordinator.showChat(chatId: originalMessage.chatId, msgId: originalMessageId, animated: true, clearViewControllerStack: true)
}
}

@objc func quoteTapped(indexPath: IndexPath) {
if handleSelection(indexPath: indexPath) { return }

Expand Down
43 changes: 42 additions & 1 deletion deltachat-ios/Chat/Views/Cells/BaseMessageCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ public class BaseMessageCell: UITableViewCell {
private var trailingConstraint: NSLayoutConstraint?
private var trailingConstraintEditingMode: NSLayoutConstraint?
private var leadingConstraintGroup: NSLayoutConstraint?
private var gotoOriginalLeftConstraint: NSLayoutConstraint?

// horizontal message constraints for sent messages
private var leadingConstraintCurrentSender: NSLayoutConstraint?
private var leadingConstraintCurrentSenderEditingMode: NSLayoutConstraint?
private var trailingConstraintCurrentSender: NSLayoutConstraint?
private var gotoOriginalRightConstraint: NSLayoutConstraint?

private var mainContentBelowTopLabelConstraint: NSLayoutConstraint?
private var mainContentUnderTopLabelConstraint: NSLayoutConstraint?
Expand Down Expand Up @@ -158,6 +161,24 @@ public class BaseMessageCell: UITableViewCell {
return button
}()

private let gotoOriginalWidth = CGFloat(32)
lazy var gotoOriginalButton: UIButton = {
let button = UIButton(frame: .zero)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.constraintHeightTo(gotoOriginalWidth),
button.constraintWidthTo(gotoOriginalWidth)
])
button.addTarget(self, action: #selector(onGotoOriginal), for: .touchUpInside)
button.backgroundColor = DcColors.gotoButtonBackgroundColor
button.setImage(UIImage(systemName: "chevron.right")?.sd_tintedImage(with: DcColors.gotoButtonFontColor), for: .normal)
button.layer.cornerRadius = gotoOriginalWidth / 2
button.layer.masksToBounds = true
button.accessibilityLabel = String.localized("show_in_chat")

return button
}()

let statusView = StatusView()

lazy var messageBackgroundContainer: BackgroundContainer = {
Expand Down Expand Up @@ -205,6 +226,7 @@ public class BaseMessageCell: UITableViewCell {
messageBackgroundContainer.addSubview(actionButton)
messageBackgroundContainer.addSubview(statusView)
contentView.addSubview(avatarView)
contentView.addSubview(gotoOriginalButton)

contentView.addConstraints([
avatarView.constraintAlignLeadingTo(contentView, paddingLeading: 2),
Expand All @@ -219,9 +241,15 @@ public class BaseMessageCell: UITableViewCell {
statusView.constraintAlignLeadingMaxTo(messageBackgroundContainer, paddingLeading: 8),
statusView.constraintAlignTrailingTo(messageBackgroundContainer, paddingTrailing: 8),
statusView.constraintToBottomOf(actionButton, paddingTop: 8, priority: .defaultHigh),
statusView.constraintAlignBottomTo(messageBackgroundContainer, paddingBottom: 6)
statusView.constraintAlignBottomTo(messageBackgroundContainer, paddingBottom: 6),
gotoOriginalButton.constraintCenterYTo(messageBackgroundContainer),
])

gotoOriginalLeftConstraint = gotoOriginalButton.constraintAlignLeadingTo(messageBackgroundContainer, paddingLeading: -(gotoOriginalWidth+8))
gotoOriginalLeftConstraint?.isActive = false
gotoOriginalRightConstraint = gotoOriginalButton.constraintToTrailingOf(contentView, paddingLeading: -(gotoOriginalWidth+8))
gotoOriginalRightConstraint?.isActive = false

leadingConstraint = messageBackgroundContainer.constraintAlignLeadingTo(contentView, paddingLeading: 6)
bottomConstraint = messageBackgroundContainer.constraintAlignBottomTo(contentView, paddingBottom: 3)
bottomConstraint?.isActive = true
Expand Down Expand Up @@ -291,6 +319,12 @@ public class BaseMessageCell: UITableViewCell {
}
}

@objc func onGotoOriginal() {
if let tableView = self.superview as? UITableView, let indexPath = tableView.indexPath(for: self) {
baseDelegate?.gotoOriginal(indexPath: indexPath)
}
}

@objc func onQuoteTapped() {
if let tableView = self.superview as? UITableView, let indexPath = tableView.indexPath(for: self) {
baseDelegate?.quoteTapped(indexPath: indexPath)
Expand Down Expand Up @@ -360,6 +394,8 @@ public class BaseMessageCell: UITableViewCell {
leadingConstraintCurrentSender?.isActive = !isEditing
leadingConstraintCurrentSenderEditingMode?.isActive = isEditing
trailingConstraintCurrentSender?.isActive = true
gotoOriginalLeftConstraint?.isActive = true
gotoOriginalRightConstraint?.isActive = false
} else {
topLabel.text = msg.isForwarded ? String.localized("forwarded_message") :
showName ? msg.getSenderName(fromContact, markOverride: true) : nil
Expand Down Expand Up @@ -388,6 +424,8 @@ public class BaseMessageCell: UITableViewCell {
}
trailingConstraint?.isActive = !isEditing
trailingConstraintEditingMode?.isActive = isEditing
gotoOriginalLeftConstraint?.isActive = false
gotoOriginalRightConstraint?.isActive = true
}

if showAvatar {
Expand All @@ -401,6 +439,8 @@ public class BaseMessageCell: UITableViewCell {
avatarView.isHidden = true
}

gotoOriginalButton.isHidden = msg.originalMessageId == 0

let downloadState = msg.downloadState
let hasHtml = msg.hasHtml
let hasWebxdc = msg.type == DC_MSG_WEBXDC
Expand Down Expand Up @@ -657,5 +697,6 @@ public protocol BaseMessageCellDelegate: AnyObject {
func textTapped(indexPath: IndexPath)
func quoteTapped(indexPath: IndexPath)
func actionButtonTapped(indexPath: IndexPath)
func gotoOriginal(indexPath: IndexPath)
func reactionsTapped(indexPath: IndexPath)
}
4 changes: 4 additions & 0 deletions deltachat-ios/Chat/Views/StatusView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public class StatusView: UIView {
dateLabel.text = message.formattedSentDate()
dateLabel.textColor = tintColor

if message.savedMessageId != 0 || message.originalMessageId != 0 {
dateLabel.text = "★ " + (dateLabel.text ?? "")
}

if message.showPadlock() {
padlockView.image = UIImage(named: "ic_lock")?.maskWithColor(color: tintColor)
padlockView.isHidden = false
Expand Down
4 changes: 4 additions & 0 deletions deltachat-ios/DC/DcContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,10 @@ public class DcContext {
dc_forward_msgs(contextPointer, msgIds.compactMap { UInt32($0) }, Int32(msgIds.count), UInt32(chat))
}

public func saveMessages(with msgIds: [Int]) {
dc_save_msgs(contextPointer, msgIds.compactMap { UInt32($0) }, Int32(msgIds.count))
}

public func sendTextInChat(id: Int, message: String) {
dc_send_text_msg(contextPointer, UInt32(id), message)
}
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 @@ -48,6 +48,14 @@ public class DcMsg {
return Int(dc_msg_get_from_id(messagePointer))
}

public var originalMessageId: Int {
return Int(dc_msg_get_original_msg_id(messagePointer))
}

public var savedMessageId: Int {
return Int(dc_msg_get_saved_msg_id(messagePointer))
}

public var isFromCurrentSender: Bool {
return fromContactId == DC_CONTACT_ID_SELF
}
Expand Down
Loading