From 61df9e2cf11e6895561850bff95322542dc807bd Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Fri, 27 Oct 2023 15:11:52 +1100 Subject: [PATCH] refactor disappearing timer view from objc to swift --- Session.xcodeproj/project.pbxproj | 4 + .../DisappearingMessageTimerView.swift | 85 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 Session/Conversations/Message Cells/Content Views/DisappearingMessageTimerView.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 3b3290ee9..40fc9c79c 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -137,6 +137,7 @@ 7B9F71D42852EEE2006DFE7B /* Emoji+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71CF2852EEE2006DFE7B /* Emoji+Name.swift */; }; 7B9F71D72853100A006DFE7B /* Emoji+Available.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71D528531009006DFE7B /* Emoji+Available.swift */; }; 7B9F71D82853100A006DFE7B /* EmojiWithSkinTones.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71D628531009006DFE7B /* EmojiWithSkinTones.swift */; }; + 7BA37AFB2AEB64CA002438F8 /* DisappearingMessageTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA37AFA2AEB64CA002438F8 /* DisappearingMessageTimerView.swift */; }; 7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA68908272A27BE00EFC32F /* SessionCall.swift */; }; 7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */; }; 7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */; }; @@ -1274,6 +1275,7 @@ 7B9F71CF2852EEE2006DFE7B /* Emoji+Name.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Emoji+Name.swift"; sourceTree = ""; }; 7B9F71D528531009006DFE7B /* Emoji+Available.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Emoji+Available.swift"; sourceTree = ""; }; 7B9F71D628531009006DFE7B /* EmojiWithSkinTones.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiWithSkinTones.swift; sourceTree = ""; }; + 7BA37AFA2AEB64CA002438F8 /* DisappearingMessageTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisappearingMessageTimerView.swift; sourceTree = ""; }; 7BA68908272A27BE00EFC32F /* SessionCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCall.swift; sourceTree = ""; }; 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXCallController.swift"; sourceTree = ""; }; 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXProvider.swift"; sourceTree = ""; }; @@ -2512,6 +2514,7 @@ 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */, 7B7037422834B81F000DCF35 /* ReactionContainerView.swift */, 7B7037442834BCC0000DCF35 /* ReactionView.swift */, + 7BA37AFA2AEB64CA002438F8 /* DisappearingMessageTimerView.swift */, ); path = "Content Views"; sourceTree = ""; @@ -6203,6 +6206,7 @@ B877E24626CA13BA0007970A /* CallVC+Camera.swift in Sources */, 454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */, 451A13B11E13DED2000A50FD /* AppNotifications.swift in Sources */, + 7BA37AFB2AEB64CA002438F8 /* DisappearingMessageTimerView.swift in Sources */, FD12A8492AD63C4700EEBA0D /* SessionNavItem.swift in Sources */, FD12A83D2AD63BCC00EEBA0D /* EditableState.swift in Sources */, 34D99CE4217509C2000AFB39 /* AppEnvironment.swift in Sources */, diff --git a/Session/Conversations/Message Cells/Content Views/DisappearingMessageTimerView.swift b/Session/Conversations/Message Cells/Content Views/DisappearingMessageTimerView.swift new file mode 100644 index 000000000..db9b3f73c --- /dev/null +++ b/Session/Conversations/Message Cells/Content Views/DisappearingMessageTimerView.swift @@ -0,0 +1,85 @@ +// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. + +import UIKit +import SessionSnodeKit + +class DisappearingMessageTimerView: UIView { + private var initialDurationSeconds: Int32 = 0 + private var expirationTimestampMs: Int64 = 0 + + // MARK: - Animation + private var animationTimer: Timer? + private var progress: Int = 12 // 0 == about to expire, 12 == just started countdown. + + // MARK: - UI + private var iconImageView: UIImageView = { + let result: UIImageView = UIImageView() + result.set(.width, to: 9) + result.set(.height, to: 9) + return result + }() + + // MARK: - Lifecycle + + init() { + super.init(frame: CGRect.zero) + + self.addSubview(iconImageView) + iconImageView.pin(to: self) + } + + override init(frame: CGRect) { + preconditionFailure("Use init(viewItem:textColor:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(viewItem:textColor:) instead.") + } + + public func configure(expirationTimestampMs: Int64, initialDurationSeconds: Int32) { + self.expirationTimestampMs = expirationTimestampMs + self.initialDurationSeconds = initialDurationSeconds + + self.updateProgress() + self.startAnimation() + } + + @objc private func updateProgress() { + guard self.expirationTimestampMs > 0 else { + self.progress = 12 + return + } + + let timestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs() + let secondsLeft: Double = max(Double(self.expirationTimestampMs - timestampMs) / 1000, 0) + let progressRatio: Double = self.initialDurationSeconds > 0 ? secondsLeft / Double(self.initialDurationSeconds) : 0 + + self.progress = Int(round(min(progressRatio, 1) * 12)) + self.updateIcon() + } + + private func updateIcon() { + let imageName: String = "disappearing_message_\(String(format: "%02d", 5 * self.progress))" + self.iconImageView.image = UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate) + } + + private func startAnimation() { + self.clearAnimation() + self.animationTimer = Timer.weakScheduledTimer( + withTimeInterval: 0.1, + target: self, + selector: #selector(updateProgress), + userInfo: nil, + repeats: true + ) + } + + private func clearAnimation() { + self.animationTimer?.invalidate() + self.animationTimer = nil + } + + public func prepareForReuse() { + self.clearAnimation() + } +}