diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 3b89e7edc..6aecc3fc4 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -7173,7 +7173,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 387; + CURRENT_PROJECT_VERSION = 388; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7212,7 +7212,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 2.2.2; + MARKETING_VERSION = 2.2.3; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; @@ -7245,7 +7245,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 387; + CURRENT_PROJECT_VERSION = 388; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7284,7 +7284,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 2.2.2; + MARKETING_VERSION = 2.2.3; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; PRODUCT_NAME = Session; diff --git a/Session/Calls/CallVC.swift b/Session/Calls/CallVC.swift index 652c87888..adb72deb1 100644 --- a/Session/Calls/CallVC.swift +++ b/Session/Calls/CallVC.swift @@ -28,7 +28,8 @@ final class CallVC: UIViewController, VideoPreviewDelegate { result.clipsToBounds = true result.themeBackgroundColor = .backgroundSecondary result.isHidden = !call.isVideoEnabled - result.layer.cornerRadius = 10 + result.layer.cornerRadius = UIDevice.current.isIPad ? 20 : 10 + result.layer.masksToBounds = true result.set(.width, to: LocalVideoView.width) result.set(.height, to: LocalVideoView.height) result.makeViewDraggable() @@ -463,6 +464,8 @@ final class CallVC: UIViewController, VideoPreviewDelegate { } @objc func didChangeDeviceOrientation(notification: Notification) { + if UIDevice.current.isIPad { return } + func rotateAllButtons(rotationAngle: CGFloat) { let transform = CGAffineTransform(rotationAngle: rotationAngle) diff --git a/Session/Calls/Views & Modals/CallVideoView.swift b/Session/Calls/Views & Modals/CallVideoView.swift index 7bf90dcf1..899732f66 100644 --- a/Session/Calls/Views & Modals/CallVideoView.swift +++ b/Session/Calls/Views & Modals/CallVideoView.swift @@ -16,6 +16,16 @@ class RemoteVideoView: TargetView { override func renderFrame(_ frame: RTCVideoFrame?) { super.renderFrame(frame) guard let frame = frame else { return } + if UIDevice.current.isIPad { + DispatchMainThreadSafe { +#if targetEnvironment(simulator) + self.contentMode = .scaleAspectFit +#else + self.videoContentMode = .scaleAspectFit +#endif + } + return + } DispatchMainThreadSafe { let frameRatio = Double(frame.height) / Double(frame.width) @@ -78,8 +88,8 @@ class RemoteVideoView: TargetView { class LocalVideoView: TargetView { - static let width: CGFloat = 80 - static let height: CGFloat = 173 + static let width: CGFloat = UIDevice.current.isIPad ? 160 : 80 + static let height: CGFloat = UIDevice.current.isIPad ? 346: 173 override func renderFrame(_ frame: RTCVideoFrame?) { super.renderFrame(frame) diff --git a/Session/Calls/Views & Modals/MiniCallView.swift b/Session/Calls/Views & Modals/MiniCallView.swift index 55c545d83..8d060eed4 100644 --- a/Session/Calls/Views & Modals/MiniCallView.swift +++ b/Session/Calls/Views & Modals/MiniCallView.swift @@ -8,7 +8,8 @@ final class MiniCallView: UIView, RTCVideoViewDelegate { var callVC: CallVC // MARK: UI - private static let defaultSize: CGFloat = 100 + private static let defaultSize: CGFloat = UIDevice.current.isIPad ? 200 : 100 + private static let defaultVideoSize: CGFloat = UIDevice.current.isIPad ? 320 : 160 private let topMargin = (UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0) + Values.veryLargeSpacing private let bottomMargin = (UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0) @@ -102,7 +103,7 @@ final class MiniCallView: UIView, RTCVideoViewDelegate { private func setUpViewHierarchy() { self.clipsToBounds = true - self.layer.cornerRadius = 10 + self.layer.cornerRadius = UIDevice.current.isIPad ? 20 : 10 self.width = self.set(.width, to: MiniCallView.defaultSize) self.height = self.set(.height, to: MiniCallView.defaultSize) @@ -190,8 +191,8 @@ final class MiniCallView: UIView, RTCVideoViewDelegate { func videoView(_ videoView: RTCVideoRenderer, didChangeVideoSize size: CGSize) { let newSize = CGSize( - width: min(160.0, 160.0 * size.width / size.height), - height: min(160.0, 160.0 * size.height / size.width) + width: min(Self.defaultVideoSize, Self.defaultVideoSize * size.width / size.height), + height: min(Self.defaultVideoSize, Self.defaultVideoSize * size.height / size.width) ) persistCurrentPosition(newSize: newSize) self.width?.constant = newSize.width diff --git a/Session/Closed Groups/NewClosedGroupVC.swift b/Session/Closed Groups/NewClosedGroupVC.swift index 073f3e076..efadfcde6 100644 --- a/Session/Closed Groups/NewClosedGroupVC.swift +++ b/Session/Closed Groups/NewClosedGroupVC.swift @@ -32,7 +32,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate ] private var selectedContacts: Set = [] private var searchText: String = "" - + // MARK: - Components private static let textFieldHeight: CGFloat = 50 diff --git a/Session/Conversations/Context Menu/ContextMenuVC.swift b/Session/Conversations/Context Menu/ContextMenuVC.swift index 659686c7f..d68851588 100644 --- a/Session/Conversations/Context Menu/ContextMenuVC.swift +++ b/Session/Conversations/Context Menu/ContextMenuVC.swift @@ -284,6 +284,11 @@ final class ContextMenuVC: UIViewController { } } + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + snDismiss() + } + func calculateFrame(menuHeight: CGFloat, spacing: CGFloat) -> CGRect { var finalFrame: CGRect = frame let ratio: CGFloat = (frame.width / frame.height) diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index bb70bea7f..afd586d78 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -1578,6 +1578,11 @@ extension ConversationVC: case .typingIndicator, .dateHeader: break case .textOnlyMessage: + if cellViewModel.body == nil, let linkPreview: LinkPreview = cellViewModel.linkPreview { + UIPasteboard.general.string = linkPreview.url + return + } + UIPasteboard.general.string = cellViewModel.body case .audio, .genericAttachment, .mediaMessage: diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index e05b70794..b6bc805e8 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -40,6 +40,8 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl var audioRecorder: AVAudioRecorder? var audioTimer: Timer? + private var searchBarWidth: NSLayoutConstraint? + // Context menu var contextMenuWindow: ContextMenuWindow? var contextMenuVC: ContextMenuVC? @@ -490,6 +492,12 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl stopObservingChanges() } + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + searchBarWidth?.constant = size.width - 32 + tableView.reloadData() + } + // MARK: - Updating private func startObservingChanges(didReturnFromBackground: Bool = false) { @@ -1513,7 +1521,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl searchBar.sizeToFit() searchBar.layoutMargins = UIEdgeInsets.zero searchBarContainer.set(.height, to: 44) - searchBarContainer.set(.width, to: UIScreen.main.bounds.width - 32) + searchBarWidth = searchBarContainer.set(.width, to: UIScreen.main.bounds.width - 32) searchBarContainer.addSubview(searchBar) navigationItem.titleView = searchBarContainer diff --git a/Session/Conversations/Message Cells/Content Views/ReactionContainerView.swift b/Session/Conversations/Message Cells/Content Views/ReactionContainerView.swift index 83db36b13..94f52b6d5 100644 --- a/Session/Conversations/Message Cells/Content Views/ReactionContainerView.swift +++ b/Session/Conversations/Message Cells/Content Views/ReactionContainerView.swift @@ -13,7 +13,6 @@ final class ReactionContainerView: UIView { private var collapsedCount: Int = 0 private var showingAllReactions: Bool = false private var showNumbers: Bool = true - private var maxEmojisPerLine = isIPhone6OrSmaller ? 5 : 6 private var oldSize: CGSize = .zero var reactions: [ReactionViewModel] = [] @@ -65,7 +64,7 @@ final class ReactionContainerView: UIView { textLabel.setContentCompressionResistancePriority(.required, for: .vertical) textLabel.setContentCompressionResistancePriority(.required, for: .horizontal) textLabel.font = .systemFont(ofSize: Values.verySmallFontSize) - textLabel.text = "Show less" + textLabel.text = "EMOJI_REACTS_SHOW_LESS".localized() textLabel.themeTextColor = .textPrimary let result: UIView = UIView() diff --git a/Session/Conversations/Message Cells/VisibleMessageCell.swift b/Session/Conversations/Message Cells/VisibleMessageCell.swift index 442a7e9aa..2c47807d8 100644 --- a/Session/Conversations/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations/Message Cells/VisibleMessageCell.swift @@ -168,7 +168,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { var result = groupThreadHSpacing + profilePictureSize + groupThreadHSpacing if UIDevice.current.isIPad { - result += CGFloat(UIScreen.main.bounds.width / 2 - 88) + result += 168 } return result @@ -503,7 +503,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { let quoteView: QuoteView = QuoteView( for: .regular, authorId: quote.authorId, - quotedText: quote.body, + quotedText: quote.body ?? "QUOTED_MESSAGE_NOT_FOUND".localized(), threadVariant: cellViewModel.threadVariant, currentUserPublicKey: cellViewModel.currentUserPublicKey, currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey, @@ -1025,11 +1025,12 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { static func getMaxWidth(for cellViewModel: MessageViewModel, includingOppositeGutter: Bool = true) -> CGFloat { let screen: CGRect = UIScreen.main.bounds + let width: CGFloat = UIDevice.current.isIPad ? screen.width * 0.75 : screen.width let oppositeEdgePadding: CGFloat = (includingOppositeGutter ? gutterSize : contactThreadHSpacing) switch cellViewModel.variant { case .standardOutgoing: - return (screen.width - contactThreadHSpacing - oppositeEdgePadding) + return (width - contactThreadHSpacing - oppositeEdgePadding) case .standardIncoming, .standardIncomingDeleted: let isGroupThread = ( @@ -1038,7 +1039,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { ) let leftGutterSize = (isGroupThread ? leftGutterSize : contactThreadHSpacing) - return (screen.width - leftGutterSize - oppositeEdgePadding) + return (width - leftGutterSize - oppositeEdgePadding) default: preconditionFailure() } diff --git a/Session/Conversations/Settings/ThreadSettingsViewModel.swift b/Session/Conversations/Settings/ThreadSettingsViewModel.swift index e771e26fb..fe23f06f8 100644 --- a/Session/Conversations/Settings/ThreadSettingsViewModel.swift +++ b/Session/Conversations/Settings/ThreadSettingsViewModel.swift @@ -241,7 +241,24 @@ class ThreadSettingsViewModel: SessionTableViewModel) { - let threadId: String = self.threadId + private func addUsersToOpenGoup(threadViewModel: SessionThreadViewModel, selectedUsers: Set) { + guard + let name: String = threadViewModel.openGroupName, + let server: String = threadViewModel.openGroupServer, + let roomToken: String = threadViewModel.openGroupRoomToken, + let publicKey: String = threadViewModel.openGroupPublicKey + else { return } dependencies.storage.writeAsync { db in - guard let openGroup: OpenGroup = try OpenGroup.fetchOne(db, id: threadId) else { return } - - let urlString: String = "\(openGroup.server)/\(openGroup.roomToken)?public_key=\(openGroup.publicKey)" + let urlString: String = OpenGroup.urlFor( + server: server, + roomToken: roomToken, + publicKey: publicKey + ) try selectedUsers.forEach { userId in let thread: SessionThread = try SessionThread.fetchOrCreate(db, id: userId, variant: .contact) @@ -575,7 +602,7 @@ class ThreadSettingsViewModel: SessionTableViewModel UIViewController? { @@ -351,11 +362,6 @@ private final class EnterPublicKeyVC: UIViewController { result.spacing = UIDevice.current.isIPad ? Values.iPadButtonSpacing : Values.mediumSpacing result.distribution = .fillEqually - if (UIDevice.current.isIPad) { - result.layoutMargins = UIEdgeInsets(top: 0, left: Values.iPadButtonContainerMargin, bottom: 0, right: Values.iPadButtonContainerMargin) - result.isLayoutMarginsRelativeArrangement = true - } - return result }() @@ -396,6 +402,9 @@ private final class EnterPublicKeyVC: UIViewController { return result }() + private var viewWidth: NSLayoutConstraint? + private var viewHeight: NSLayoutConstraint? + // MARK: - Lifecycle override func viewDidLoad() { @@ -428,12 +437,10 @@ private final class EnterPublicKeyVC: UIViewController { ) mainStackView.isLayoutMarginsRelativeArrangement = true view.addSubview(mainStackView) - - mainStackView.pin(.top, to: .top, of: view) - mainStackView.pin(.leading, to: .leading, of: view) - mainStackView.pin(.trailing, to: .trailing, of: view) - bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view, withInset: -bottomMargin) - + + mainStackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.top ], to: view) + bottomConstraint = mainStackView.pin(.bottom, to: .bottom, of: view, withInset: bottomMargin) + // Dismiss keyboard on tap let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tapGestureRecognizer) @@ -450,6 +457,27 @@ private final class EnterPublicKeyVC: UIViewController { // MARK: - General + func constrainSize(to size: CGSize) { + if viewWidth == nil { + viewWidth = view.set(.width, to: size.width) + } else { + viewWidth?.constant = size.width + } + + if viewHeight == nil { + viewHeight = view.set(.height, to: size.height) + } else { + viewHeight?.constant = size.height + } + + if (UIDevice.current.isIPad) { + let iPadButtonContainerMargin: CGFloat = (size.width - Values.iPadButtonSpacing) / 2 - Values.iPadButtonWidth - Values.largeSpacing + buttonContainer.layoutMargins = UIEdgeInsets(top: 0, left: iPadButtonContainerMargin, bottom: 0, right: iPadButtonContainerMargin) + buttonContainer.isLayoutMarginsRelativeArrangement = true + } + } + + func setSessionId(to sessionId: String) { publicKeyTextView.insertText(sessionId) } @@ -609,6 +637,9 @@ private final class EnterPublicKeyVC: UIViewController { private final class ScanQRCodePlaceholderVC: UIViewController { weak var newDMVC: NewDMVC! + private var viewWidth: NSLayoutConstraint? + private var viewHeight: NSLayoutConstraint? + override func viewDidLoad() { // Remove background color view.themeBackgroundColor = .clear @@ -638,10 +669,27 @@ private final class ScanQRCodePlaceholderVC: UIViewController { // Set up constraints view.addSubview(stackView) stackView.pin(.leading, to: .leading, of: view, withInset: Values.massiveSpacing) - stackView.pin(.trailing, to: .trailing, of: view, withInset: -Values.massiveSpacing) - stackView.center(.vertical, in: view, withInset: -16) // Makes things appear centered visually + view.pin(.trailing, to: .trailing, of: stackView, withInset: Values.massiveSpacing) + + let verticalCenteringConstraint = stackView.center(.vertical, in: view) + verticalCenteringConstraint.constant = -16 // Makes things appear centered visually } + func constrainSize(to size: CGSize) { + if viewWidth == nil { + viewWidth = view.set(.width, to: size.width) + } else { + viewWidth?.constant = size.width + } + + if viewHeight == nil { + viewHeight = view.set(.height, to: size.height) + } else { + viewHeight?.constant = size.height + } + } + + @objc private func requestCameraAccess() { Permissions.requestCameraPermissionIfNeeded { [weak self] in self?.newDMVC.handleCameraAccessGranted() diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 12a5b6b83..d91851597 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -191,6 +191,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - Orientation func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { + if UIDevice.current.isIPad { + return .allButUpsideDown + } + return .portrait } diff --git a/Session/Meta/Session-Info.plist b/Session/Meta/Session-Info.plist index 54e23fb8d..e186b98c9 100644 --- a/Session/Meta/Session-Info.plist +++ b/Session/Meta/Session-Info.plist @@ -144,6 +144,12 @@ UIInterfaceOrientationPortrait + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIViewControllerBasedStatusBarAppearance diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index 0088b2bec..8d583d798 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 58ae00a85..3829aceab 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index c85047355..31d0bc578 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index fc7608274..75cc6f737 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "پیام شما ارسال نشد."; "INVALID_SESSION_ID_MESSAGE" = "لطفاً شناسه Session را مجدد بررسی کنید."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "لطفاً شناسه بازیابی را مجدد بررسی کنید."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "مدیا"; "DOCUMENT_TAB_TITLE" = "مدارک"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "شما در این مکالمه هیچ مدرکی ندارید."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ به پیامی با %@ واکنش نشان می‌دهد. "; "EMOJI_REACTS_MORE_REACTORS_ONE" = "و ۱ نفر دیگر به این پیام واکنش %@ نشان داده است."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "و %@ نفر دیگر به این پیام واکنش %@ نشان داده‌اند."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "مکالمه جدید"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index 4ff458bd0..92e2e6a11 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index 881482017..17c7b0dff 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index 1cf729168..8bab51375 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index 39fd0eda0..8af92e999 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 60468ace4..5ff981ef7 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index b528d3110..f6725544a 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index f7933ef8d..6319ef7fd 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index 38b5e5143..284441372 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index 2c1ea7e14..ab8b48d0b 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index 1235bc38e..cd772f650 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index 737d22897..632e50aa4 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index 0e3bdae28..753687545 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 58db95bb6..1388f0536 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index beaf86ca9..2af704369 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index 9df4d727c..24910f380 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index 8045dc6c3..6a27510bc 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index 44d3ce9a2..d408fc648 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index a1374b94f..f391b1784 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -450,6 +450,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"QUOTED_MESSAGE_NOT_FOUND" = "Original message not found."; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; @@ -476,6 +477,7 @@ "EMOJI_REACTS_NOTIFICATION" = "%@ reacts to a message with %@."; "EMOJI_REACTS_MORE_REACTORS_ONE" = "And 1 other has reacted %@ to this message."; "EMOJI_REACTS_MORE_REACTORS_MUTIPLE" = "And %@ others have reacted %@ to this message."; +"EMOJI_REACTS_SHOW_LESS" = "Show less"; "EMOJI_REACTS_RATE_LIMIT_TOAST" = "Slow down! You've sent too many emoji reacts. Try again soon."; /* New conversation screen*/ "vc_new_conversation_title" = "New Conversation"; diff --git a/Session/Onboarding/FakeChatView.swift b/Session/Onboarding/FakeChatView.swift index f80cbd46a..9c6710e2c 100644 --- a/Session/Onboarding/FakeChatView.swift +++ b/Session/Onboarding/FakeChatView.swift @@ -26,7 +26,7 @@ final class FakeChatView: UIView { return result }() - private static let bubbleWidth = CGFloat(224) + private static let bubbleWidth = UIDevice.current.isIPad ? CGFloat(468) : CGFloat(224) private static let bubbleCornerRadius = CGFloat(10) private static let startDelay: TimeInterval = 1 private static let animationDuration: TimeInterval = 0.4 @@ -50,12 +50,12 @@ final class FakeChatView: UIView { stackView.axis = .vertical stackView.spacing = spacing stackView.alignment = .fill - stackView.set(.width, to: UIScreen.main.bounds.width) let vInset = Values.smallSpacing stackView.layoutMargins = UIEdgeInsets(top: vInset, leading: Values.veryLargeSpacing, bottom: vInset, trailing: Values.veryLargeSpacing) stackView.isLayoutMarginsRelativeArrangement = true scrollView.addSubview(stackView) stackView.pin(to: scrollView) + stackView.set(.width, to: .width, of: scrollView) addSubview(scrollView) scrollView.pin(to: self) let height = chatBubbles.reduce(0) { $0 + $1.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize).height } + CGFloat(chatBubbles.count - 1) * spacing + 2 * vInset @@ -65,7 +65,7 @@ final class FakeChatView: UIView { private func getChatBubble(withText text: String, wasSentByCurrentUser: Bool) -> UIView { let result = UIView() let bubbleView = UIView() - bubbleView.set(.width, to: FakeChatView.bubbleWidth) + bubbleView.set(.width, lessThanOrEqualTo: FakeChatView.bubbleWidth) bubbleView.themeShadowColor = .black bubbleView.layer.cornerRadius = FakeChatView.bubbleCornerRadius bubbleView.layer.shadowRadius = (ThemeManager.currentTheme.interfaceStyle == .light ? 4 : 8) diff --git a/Session/Open Groups/JoinOpenGroupVC.swift b/Session/Open Groups/JoinOpenGroupVC.swift index d46e3f9bc..2b84bb734 100644 --- a/Session/Open Groups/JoinOpenGroupVC.swift +++ b/Session/Open Groups/JoinOpenGroupVC.swift @@ -84,10 +84,22 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC pageVCView.pin(.top, to: .bottom, of: tabBar) pageVCView.pin(.trailing, to: .trailing, of: view) pageVCView.pin(.bottom, to: .bottom, of: view) + let navBarHeight: CGFloat = (navigationController?.navigationBar.frame.size.height ?? 0) - let height: CGFloat = ((navigationController?.view.bounds.height ?? 0) - navBarHeight - TabBar.snHeight) - enterURLVC.constrainHeight(to: height) - scanQRCodePlaceholderVC.constrainHeight(to: height) + let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height + let height: CGFloat = ((navigationController?.view.bounds.height ?? 0) - navBarHeight - TabBar.snHeight - statusBarHeight) + let size: CGSize = CGSize(width: UIScreen.main.bounds.width, height: height) + enterURLVC.constrainSize(to: size) + scanQRCodePlaceholderVC.constrainSize(to: size) + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + let height: CGFloat = (size.height - TabBar.snHeight) + let size: CGSize = CGSize(width: size.width, height: height) + enterURLVC.constrainSize(to: size) + scanQRCodePlaceholderVC.constrainSize(to: size) + enterURLVC.suggestionGrid.refreshLayout(with: size.width - 2 * Values.largeSpacing) } // MARK: - General @@ -243,13 +255,16 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O return result }() - private lazy var suggestionGrid: OpenGroupSuggestionGrid = { + lazy var suggestionGrid: OpenGroupSuggestionGrid = { let maxWidth: CGFloat = (UIScreen.main.bounds.width - Values.largeSpacing * 2) let result: OpenGroupSuggestionGrid = OpenGroupSuggestionGrid(maxWidth: maxWidth) result.delegate = self return result }() + + private var viewWidth: NSLayoutConstraint? + private var viewHeight: NSLayoutConstraint? // MARK: - Lifecycle @@ -298,7 +313,7 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O bottomConstraint = view.pin(.bottom, to: .bottom, of: stackView, withInset: bottomMargin) // Constraints - view.set(.width, to: UIScreen.main.bounds.width) + viewWidth = view.set(.width, to: UIScreen.main.bounds.width) // Dismiss keyboard on tap let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) @@ -325,8 +340,18 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O // MARK: - General - func constrainHeight(to height: CGFloat) { - view.set(.height, to: height) + func constrainSize(to size: CGSize) { + if viewWidth == nil { + viewWidth = view.set(.width, to: size.width) + } else { + viewWidth?.constant = size.width + } + + if viewHeight == nil { + viewHeight = view.set(.height, to: size.height) + } else { + viewHeight?.constant = size.height + } } @objc private func dismissKeyboard() { @@ -438,6 +463,9 @@ private final class EnterURLVC: UIViewController, UIGestureRecognizerDelegate, O private final class ScanQRCodePlaceholderVC: UIViewController { weak var joinOpenGroupVC: JoinOpenGroupVC? + private var viewWidth: NSLayoutConstraint? + private var viewHeight: NSLayoutConstraint? + // MARK: - Lifecycle override func viewDidLoad() { @@ -467,7 +495,6 @@ private final class ScanQRCodePlaceholderVC: UIViewController { stackView.alignment = .center // Constraints - view.set(.width, to: UIScreen.main.bounds.width) view.addSubview(stackView) stackView.pin(.leading, to: .leading, of: view, withInset: Values.massiveSpacing) view.pin(.trailing, to: .trailing, of: stackView, withInset: Values.massiveSpacing) @@ -476,8 +503,18 @@ private final class ScanQRCodePlaceholderVC: UIViewController { verticalCenteringConstraint.constant = -16 // Makes things appear centered visually } - func constrainHeight(to height: CGFloat) { - view.set(.height, to: height) + func constrainSize(to size: CGSize) { + if viewWidth == nil { + viewWidth = view.set(.width, to: size.width) + } else { + viewWidth?.constant = size.width + } + + if viewHeight == nil { + viewHeight = view.set(.height, to: size.height) + } else { + viewHeight?.constant = size.height + } } @objc private func requestCameraAccess() { diff --git a/Session/Open Groups/OpenGroupSuggestionGrid.swift b/Session/Open Groups/OpenGroupSuggestionGrid.swift index 16c76b07d..9ee5c5b67 100644 --- a/Session/Open Groups/OpenGroupSuggestionGrid.swift +++ b/Session/Open Groups/OpenGroupSuggestionGrid.swift @@ -5,7 +5,7 @@ import SessionUIKit final class OpenGroupSuggestionGrid: UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private let itemsPerSection: Int = (UIDevice.current.isIPad ? 4 : 2) - private let maxWidth: CGFloat + private var maxWidth: CGFloat private var rooms: [OpenGroupAPI.Room] = [] { didSet { update() } } private var heightConstraint: NSLayoutConstraint! @@ -162,6 +162,11 @@ final class OpenGroupSuggestionGrid: UIView, UICollectionViewDataSource, UIColle errorView.isHidden = (roomCount > 0) } + public func refreshLayout(with maxWidth: CGFloat) { + self.maxWidth = maxWidth + collectionView.collectionViewLayout.invalidateLayout() + } + // MARK: - Layout func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { diff --git a/Session/Settings/QRCodeVC.swift b/Session/Settings/QRCodeVC.swift index c0cb10efd..5884a535e 100644 --- a/Session/Settings/QRCodeVC.swift +++ b/Session/Settings/QRCodeVC.swift @@ -236,9 +236,14 @@ private final class ViewMyQRCodeVC : UIViewController { let shareButtonContainer = UIView() shareButtonContainer.addSubview(shareButton) shareButton.pin(.top, to: .top, of: shareButtonContainer) - shareButton.pin(.leading, to: .leading, of: shareButtonContainer, withInset: 80) - shareButton.pin(.trailing, to: .trailing, of: shareButtonContainer, withInset: -80) shareButton.pin(.bottom, to: .bottom, of: shareButtonContainer) + if UIDevice.current.isIPad { + shareButton.center(in: shareButtonContainer) + shareButton.set(.width, to: Values.iPadButtonWidth) + } else { + shareButton.pin(.leading, to: .leading, of: shareButtonContainer, withInset: 80) + shareButton.pin(.trailing, to: .trailing, of: shareButtonContainer, withInset: -80) + } // Set up stack view let spacing = (isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) diff --git a/Session/Shared/FullConversationCell.swift b/Session/Shared/FullConversationCell.swift index 57799cb76..521f968d1 100644 --- a/Session/Shared/FullConversationCell.swift +++ b/Session/Shared/FullConversationCell.swift @@ -190,7 +190,7 @@ public final class FullConversationCell: UITableViewCell { let labelContainerView = UIStackView(arrangedSubviews: [ topLabelStackView, bottomLabelStackView ]) labelContainerView.axis = .vertical - labelContainerView.alignment = .leading + labelContainerView.alignment = .fill labelContainerView.spacing = 6 labelContainerView.isUserInteractionEnabled = false @@ -206,12 +206,10 @@ public final class FullConversationCell: UITableViewCell { accentLineView.pin(.bottom, to: .bottom, of: contentView) timestampLabel.setContentCompressionResistancePriority(.required, for: NSLayoutConstraint.Axis.horizontal) - // HACK: The six lines below are part of a workaround for a weird layout bug - topLabelStackView.set(.width, to: UIScreen.main.bounds.width - Values.accentLineThickness - profilePictureViewSize - 3 * Values.mediumSpacing) + // HACK: The 4 lines below are part of a workaround for a weird layout bug topLabelStackView.set(.height, to: 20) topLabelSpacer.set(.height, to: 20) - bottomLabelStackView.set(.width, to: UIScreen.main.bounds.width - Values.accentLineThickness - profilePictureViewSize - 3 * Values.mediumSpacing) bottomLabelStackView.set(.height, to: 18) bottomLabelSpacer.set(.height, to: 18) @@ -223,12 +221,8 @@ public final class FullConversationCell: UITableViewCell { typingIndicatorView.pin(.leading, to: .leading, of: snippetLabelContainer) typingIndicatorView.centerYAnchor.constraint(equalTo: snippetLabel.centerYAnchor).isActive = true - stackView.pin(.leading, to: .leading, of: contentView) - stackView.pin(.top, to: .top, of: contentView) - - // HACK: The two lines below are part of a workaround for a weird layout bug - stackView.set(.width, to: UIScreen.main.bounds.width - Values.mediumSpacing) - stackView.set(.height, to: cellHeight) + stackView.pin([ UIView.VerticalEdge.bottom, UIView.VerticalEdge.top, UIView.HorizontalEdge.leading ], to: contentView) + stackView.pin(.trailing, to: .trailing, of: contentView, withInset: -Values.mediumSpacing) } // MARK: - Content diff --git a/Session/Shared/ScanQRCodeWrapperVC.swift b/Session/Shared/ScanQRCodeWrapperVC.swift index 60fe2a29f..a80b2305c 100644 --- a/Session/Shared/ScanQRCodeWrapperVC.swift +++ b/Session/Shared/ScanQRCodeWrapperVC.swift @@ -10,8 +10,6 @@ final class ScanQRCodeWrapperVC: BaseVC { private let message: String? private let scanQRCodeVC = QRCodeScanningViewController() - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } - // MARK: - Lifecycle init(message: String?) { @@ -47,7 +45,7 @@ final class ScanQRCodeWrapperVC: BaseVC { scanQRCodeVCView.autoPinEdge(.top, to: .top, of: view) if let message = message { - scanQRCodeVCView.autoPinToSquareAspectRatio() + scanQRCodeVCView.set(.height, lessThanOrEqualTo: UIScreen.main.bounds.width) // Set up bottom view let bottomView = UIView() @@ -78,8 +76,6 @@ final class ScanQRCodeWrapperVC: BaseVC { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - UIDevice.current.ows_setOrientation(.portrait) - self.scanQRCodeVC.startCapture() } diff --git a/Session/Shared/Views/SessionAvatarCell.swift b/Session/Shared/Views/SessionAvatarCell.swift index 8016641bb..3ccea1689 100644 --- a/Session/Shared/Views/SessionAvatarCell.swift +++ b/Session/Shared/Views/SessionAvatarCell.swift @@ -114,17 +114,7 @@ class SessionAvatarCell: UITableViewCell { stackView.alignment = .center stackView.distribution = .fillEqually stackView.spacing = (UIDevice.current.isIPad ? Values.iPadButtonSpacing : Values.mediumSpacing) - - if (UIDevice.current.isIPad) { - stackView.layoutMargins = UIEdgeInsets( - top: 0, - left: Values.iPadButtonContainerMargin, - bottom: 0, - right: Values.iPadButtonContainerMargin - ) - stackView.isLayoutMarginsRelativeArrangement = true - } - + return stackView }() @@ -242,6 +232,10 @@ class SessionAvatarCell: UITableViewCell { descriptionSeparator.update(title: style.separatorTitle) descriptionSeparator.isHidden = (style.separatorTitle == nil) + if (UIDevice.current.isIPad) { + descriptionActionStackView.addArrangedSubview(UIView.hStretchingSpacer()) + } + style.descriptionActions.forEach { action in let result: SessionButton = SessionButton(style: .bordered, size: .medium) result.setTitle(action.title, for: UIControl.State.normal) @@ -252,6 +246,10 @@ class SessionAvatarCell: UITableViewCell { descriptionActionStackView.addArrangedSubview(result) } + + if (UIDevice.current.isIPad) { + descriptionActionStackView.addArrangedSubview(UIView.hStretchingSpacer()) + } descriptionActionStackView.isHidden = style.descriptionActions.isEmpty } diff --git a/SessionMessagingKit/Database/Models/OpenGroup.swift b/SessionMessagingKit/Database/Models/OpenGroup.swift index cc0439472..8f91813ec 100644 --- a/SessionMessagingKit/Database/Models/OpenGroup.swift +++ b/SessionMessagingKit/Database/Models/OpenGroup.swift @@ -219,6 +219,10 @@ public extension OpenGroup { // Always force the server to lowercase return "\(server.lowercased()).\(roomToken)" } + + static func urlFor(server: String, roomToken: String, publicKey: String) -> String { + return "\(server)/\(roomToken)?public_key=\(publicKey)" + } } extension OpenGroup: CustomStringConvertible, CustomDebugStringConvertible { @@ -243,51 +247,3 @@ extension OpenGroup: CustomStringConvertible, CustomDebugStringConvertible { ].joined(separator: ", ") } } - -// MARK: - Objective-C Support - -// TODO: Remove this when possible - -@objc(SMKOpenGroup) -public class SMKOpenGroup: NSObject { - @objc(inviteUsers:toOpenGroupFor:) - public static func invite(selectedUsers: Set, openGroupThreadId: String) { - Storage.shared.write { db in - guard let openGroup: OpenGroup = try OpenGroup.fetchOne(db, id: openGroupThreadId) else { return } - - let urlString: String = "\(openGroup.server)/\(openGroup.roomToken)?public_key=\(openGroup.publicKey)" - - try selectedUsers.forEach { userId in - let thread: SessionThread = try SessionThread.fetchOrCreate(db, id: userId, variant: .contact) - - try LinkPreview( - url: urlString, - variant: .openGroupInvitation, - title: openGroup.name - ) - .save(db) - - let interaction: Interaction = try Interaction( - threadId: thread.id, - authorId: userId, - variant: .standardOutgoing, - timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000)), - expiresInSeconds: try? DisappearingMessagesConfiguration - .select(.durationSeconds) - .filter(id: userId) - .filter(DisappearingMessagesConfiguration.Columns.isEnabled == true) - .asRequest(of: TimeInterval.self) - .fetchOne(db), - linkPreviewUrl: urlString - ) - .inserted(db) - - try MessageSender.send( - db, - interaction: interaction, - in: thread - ) - } - } - } -} diff --git a/SessionMessagingKit/Database/Models/Quote.swift b/SessionMessagingKit/Database/Models/Quote.swift index 633676aa6..af92ee454 100644 --- a/SessionMessagingKit/Database/Models/Quote.swift +++ b/SessionMessagingKit/Database/Models/Quote.swift @@ -89,51 +89,7 @@ public extension Quote { self.interactionId = interactionId self.timestampMs = Int64(quoteProto.id) self.authorId = quoteProto.author - - // Prefer to generate the text snippet locally if available. - let quotedInteraction: Interaction? = try? thread - .interactions - .filter(Interaction.Columns.authorId == quoteProto.author) - .filter(Interaction.Columns.timestampMs == Double(quoteProto.id)) - .fetchOne(db) - - if let quotedInteraction: Interaction = quotedInteraction, quotedInteraction.body?.isEmpty == false { - self.body = quotedInteraction.body - } - else if let body: String = quoteProto.text, !body.isEmpty { - self.body = body - } - else { - self.body = nil - } - - // We only use the first attachment - if let attachment = quoteProto.attachments.first(where: { $0.thumbnail != nil })?.thumbnail { - self.attachmentId = try quotedInteraction - .map { quotedInteraction -> Attachment? in - // If the quotedInteraction has an attachment then try clone it - if let attachment: Attachment = try? quotedInteraction.attachments.fetchOne(db) { - return attachment.cloneAsQuoteThumbnail() - } - - // Otherwise if the quotedInteraction has a link preview, try clone that - return try? quotedInteraction.linkPreview - .fetchOne(db)? - .attachment - .fetchOne(db)? - .cloneAsQuoteThumbnail() - } - .defaulting(to: Attachment(proto: attachment)) - .inserted(db) - .id - } - else { - self.attachmentId = nil - } - - // Make sure the quote is valid before completing - if self.body == nil && self.attachmentId == nil { - return nil - } + self.body = nil + self.attachmentId = nil } } diff --git a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift index a2d7bf096..72289c200 100644 --- a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift +++ b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift @@ -42,7 +42,13 @@ extension ConfigurationMessage { .filter(OpenGroup.Columns.roomToken != "") .filter(OpenGroup.Columns.isActive) .fetchAll(db) - .map { "\($0.server)/\($0.roomToken)?public_key=\($0.publicKey)" } + .map { openGroup in + OpenGroup.urlFor( + server: openGroup.server, + roomToken: openGroup.roomToken, + publicKey: openGroup.publicKey + ) + } .asSet() let contacts: Set = try Contact .filter(Contact.Columns.id != currentUserProfile.id) diff --git a/SessionMessagingKit/Shared Models/MessageViewModel.swift b/SessionMessagingKit/Shared Models/MessageViewModel.swift index 2cacba1a3..81c452039 100644 --- a/SessionMessagingKit/Shared Models/MessageViewModel.swift +++ b/SessionMessagingKit/Shared Models/MessageViewModel.swift @@ -633,6 +633,7 @@ public extension MessageViewModel { let disappearingMessagesConfig: TypedTableAlias = TypedTableAlias() let profile: TypedTableAlias = TypedTableAlias() let quote: TypedTableAlias = TypedTableAlias() + let interactionAttachment: TypedTableAlias = TypedTableAlias() let linkPreview: TypedTableAlias = TypedTableAlias() let threadProfileTableLiteral: SQL = SQL(stringLiteral: "threadProfile") @@ -708,7 +709,19 @@ public extension MessageViewModel { LEFT JOIN \(DisappearingMessagesConfiguration.self) ON \(disappearingMessagesConfig[.threadId]) = \(interaction[.threadId]) LEFT JOIN \(OpenGroup.self) ON \(openGroup[.threadId]) = \(interaction[.threadId]) LEFT JOIN \(Profile.self) ON \(profile[.id]) = \(interaction[.authorId]) - LEFT JOIN \(Quote.self) ON \(quote[.interactionId]) = \(interaction[.id]) + LEFT JOIN ( + SELECT \(quote[.interactionId]), + \(quote[.authorId]), + \(quote[.timestampMs]), + \(interaction[.body]) AS \(Quote.Columns.body), + \(interactionAttachment[.attachmentId]) AS \(Quote.Columns.attachmentId) + FROM \(Quote.self) + LEFT JOIN \(Interaction.self) ON ( + \(quote[.authorId]) = \(interaction[.authorId]) AND + \(quote[.timestampMs]) = \(interaction[.timestampMs]) + ) + LEFT JOIN \(InteractionAttachment.self) ON \(interaction[.id]) = \(interactionAttachment[.interactionId]) + ) AS \(ViewModel.quoteKey) ON \(quote[.interactionId]) = \(interaction[.id]) LEFT JOIN \(Attachment.self) AS \(ViewModel.quoteAttachmentKey) ON \(ViewModel.quoteAttachmentKey).\(attachmentIdColumnLiteral) = \(quote[.attachmentId]) LEFT JOIN \(LinkPreview.self) ON ( \(linkPreview[.url]) = \(interaction[.linkPreviewUrl]) AND diff --git a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift index 2280a8542..616b7c15a 100644 --- a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift +++ b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift @@ -43,6 +43,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat public static let openGroupNameKey: SQL = SQL(stringLiteral: CodingKeys.openGroupName.stringValue) public static let openGroupServerKey: SQL = SQL(stringLiteral: CodingKeys.openGroupServer.stringValue) public static let openGroupRoomTokenKey: SQL = SQL(stringLiteral: CodingKeys.openGroupRoomToken.stringValue) + public static let openGroupPublicKeyKey: SQL = SQL(stringLiteral: CodingKeys.openGroupPublicKey.stringValue) public static let openGroupProfilePictureDataKey: SQL = SQL(stringLiteral: CodingKeys.openGroupProfilePictureData.stringValue) public static let openGroupUserCountKey: SQL = SQL(stringLiteral: CodingKeys.openGroupUserCount.stringValue) public static let openGroupPermissionsKey: SQL = SQL(stringLiteral: CodingKeys.openGroupPermissions.stringValue) @@ -117,6 +118,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat public let openGroupName: String? public let openGroupServer: String? public let openGroupRoomToken: String? + public let openGroupPublicKey: String? public let openGroupProfilePictureData: Data? private let openGroupUserCount: Int? private let openGroupPermissions: OpenGroup.Permissions? @@ -274,6 +276,7 @@ public extension SessionThreadViewModel { self.openGroupName = nil self.openGroupServer = nil self.openGroupRoomToken = nil + self.openGroupPublicKey = nil self.openGroupProfilePictureData = nil self.openGroupUserCount = nil self.openGroupPermissions = nil @@ -334,6 +337,7 @@ public extension SessionThreadViewModel { openGroupName: self.openGroupName, openGroupServer: self.openGroupServer, openGroupRoomToken: self.openGroupRoomToken, + openGroupPublicKey: self.openGroupPublicKey, openGroupProfilePictureData: self.openGroupProfilePictureData, openGroupUserCount: self.openGroupUserCount, openGroupPermissions: self.openGroupPermissions, @@ -387,6 +391,7 @@ public extension SessionThreadViewModel { openGroupName: self.openGroupName, openGroupServer: self.openGroupServer, openGroupRoomToken: self.openGroupRoomToken, + openGroupPublicKey: self.openGroupPublicKey, openGroupProfilePictureData: self.openGroupProfilePictureData, openGroupUserCount: self.openGroupUserCount, openGroupPermissions: self.openGroupPermissions, @@ -753,6 +758,7 @@ public extension SessionThreadViewModel { \(openGroup[.name]) AS \(ViewModel.openGroupNameKey), \(openGroup[.server]) AS \(ViewModel.openGroupServerKey), \(openGroup[.roomToken]) AS \(ViewModel.openGroupRoomTokenKey), + \(openGroup[.publicKey]) AS \(ViewModel.openGroupPublicKeyKey), \(openGroup[.userCount]) AS \(ViewModel.openGroupUserCountKey), \(openGroup[.permissions]) AS \(ViewModel.openGroupPermissionsKey), @@ -847,6 +853,9 @@ public extension SessionThreadViewModel { \(closedGroup[.name]) AS \(ViewModel.closedGroupNameKey), (\(groupMember[.profileId]) IS NOT NULL) AS \(ViewModel.currentUserIsClosedGroupMemberKey), \(openGroup[.name]) AS \(ViewModel.openGroupNameKey), + \(openGroup[.server]) AS \(ViewModel.openGroupServerKey), + \(openGroup[.roomToken]) AS \(ViewModel.openGroupRoomTokenKey), + \(openGroup[.publicKey]) AS \(ViewModel.openGroupPublicKeyKey), \(openGroup[.imageData]) AS \(ViewModel.openGroupProfilePictureDataKey), \(SQL("\(userPublicKey)")) AS \(ViewModel.currentUserPublicKeyKey) diff --git a/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift b/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift index b6083b47a..5225a130a 100644 --- a/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift +++ b/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift @@ -79,6 +79,32 @@ class OpenGroupSpec: QuickSpec { .to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0, pollFailureCount: 0, permissions: ---)")) } } + + context("when generating an id") { + it("generates correctly") { + expect(OpenGroup.idFor(roomToken: "room", server: "server")).to(equal("server.room")) + } + + it("converts the server to lowercase") { + expect(OpenGroup.idFor(roomToken: "room", server: "SeRVeR")).to(equal("server.room")) + } + + it("maintains the casing of the roomToken") { + expect(OpenGroup.idFor(roomToken: "RoOM", server: "server")).to(equal("server.RoOM")) + } + } + + context("when generating a url") { + it("generates the url correctly") { + expect(OpenGroup.urlFor(server: "server", roomToken: "room", publicKey: "key")) + .to(equal("server/room?public_key=key")) + } + + it("maintains the casing provided") { + expect(OpenGroup.urlFor(server: "SeRVer", roomToken: "RoOM", publicKey: "KEy")) + .to(equal("SeRVer/RoOM?public_key=KEy")) + } + } } } } diff --git a/SessionUIKit/Components/Separator.swift b/SessionUIKit/Components/Separator.swift index f4d8957c3..178c27748 100644 --- a/SessionUIKit/Components/Separator.swift +++ b/SessionUIKit/Components/Separator.swift @@ -25,6 +25,7 @@ public final class Separator: UIView { private lazy var titleLabel: UILabel = { let result = UILabel() + result.setContentCompressionResistancePriority(.required, for: .vertical) result.font = .systemFont(ofSize: Values.smallFontSize) result.themeTextColor = .textSecondary result.textAlignment = .center diff --git a/SessionUIKit/Style Guide/Values.swift b/SessionUIKit/Style Guide/Values.swift index fd03c9dcb..fd12c70c9 100644 --- a/SessionUIKit/Style Guide/Values.swift +++ b/SessionUIKit/Style Guide/Values.swift @@ -57,5 +57,4 @@ public final class Values : NSObject { @objc public static let iPadButtonWidth = CGFloat(196) @objc public static let iPadButtonSpacing = CGFloat(32) @objc public static let iPadUserSessionIdContainerWidth = iPadButtonWidth * 2 + iPadButtonSpacing - @objc public static let iPadButtonContainerMargin = (UIScreen.main.bounds.width - iPadButtonSpacing) / 2 - iPadButtonWidth - largeSpacing } diff --git a/SessionUIKit/Utilities/UIView+Constraints.swift b/SessionUIKit/Utilities/UIView+Constraints.swift index 8c043fbae..2ab9187d2 100644 --- a/SessionUIKit/Utilities/UIView+Constraints.swift +++ b/SessionUIKit/Utilities/UIView+Constraints.swift @@ -176,4 +176,17 @@ public extension UIView { constraint.isActive = true return constraint } + + @discardableResult + func set(_ dimension: Dimension, lessThanOrEqualTo size: CGFloat) -> NSLayoutConstraint { + translatesAutoresizingMaskIntoConstraints = false + let constraint: NSLayoutConstraint = { + switch dimension { + case .width: return widthAnchor.constraint(lessThanOrEqualToConstant: size) + case .height: return heightAnchor.constraint(lessThanOrEqualToConstant: size) + } + }() + constraint.isActive = true + return constraint + } } diff --git a/SessionUtilitiesKit/Database/Utilities/Database+Utilities.swift b/SessionUtilitiesKit/Database/Utilities/Database+Utilities.swift index d736dfc35..b8e849c74 100644 --- a/SessionUtilitiesKit/Database/Utilities/Database+Utilities.swift +++ b/SessionUtilitiesKit/Database/Utilities/Database+Utilities.swift @@ -32,6 +32,8 @@ public extension Database { } func interrupt() { + guard sqliteConnection != nil else { return } + sqlite3_interrupt(sqliteConnection) } }