From 90e53e5cef77e9c36365f3661b7c7c4946e8e32b Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 29 Jan 2021 11:46:32 +1100 Subject: [PATCH] Redesign conversation screen part 1 --- Podfile | 11 - Podfile.lock | 3 +- Session.xcodeproj/project.pbxproj | 476 +++++------------- Session/Basic Chats/NewPrivateChatVC.swift | 2 +- Session/Closed Groups/EditClosedGroupVC.swift | 4 +- Session/Closed Groups/GroupMembersVC.swift | 2 +- .../Context Menu/ContextMenuVC+Action.swift | 82 +++ .../ContextMenuVC+ActionView.swift | 62 +++ .../Context Menu/ContextMenuVC.swift | 117 +++++ .../Context Menu/ContextMenuWindow.swift | 28 ++ .../ConversationVC+Interaction.swift | 192 +++++++ .../ConversationVC+ScrollToBottomButton.swift | 70 +++ Session/Conversations V2/ConversationVC.swift | 344 +++++++++++++ .../Input View/InputTextView.swift | 72 +++ .../Input View/InputView.swift | 110 ++++ .../Input View/InputViewButton.swift | 90 ++++ .../Content Views/DocumentView.swift | 51 ++ .../Content Views/MediaAlbumView.swift} | 58 ++- .../Content Views/MediaLoaderView.swift | 78 +++ .../Content Views/MediaTextOverlayView.swift | 78 +++ .../Content Views/MediaView.swift} | 67 +-- .../Content Views/QuoteView.swift | 150 ++++++ .../Content Views}/TypingIndicatorView.swift | 0 .../Content Views/VoiceMessageViewV2.swift | 170 +++++++ .../Message Cells/InfoMessageCell.swift | 71 +++ .../Message Cells/MessageCell.swift | 58 +++ .../Message Cells/TypingIndicatorCellV2.swift | 85 ++++ .../Message Cells/VisibleMessageCell.swift | 395 +++++++++++++++ .../Conversations/ConversationInputTextView.m | 2 +- .../Conversations/ConversationInputToolbar.m | 2 +- .../Conversations/ConversationScrollButton.m | 2 +- Session/Conversations/ConversationViewItem.h | 10 +- Session/Conversations/ConversationViewItem.m | 20 +- Session/Conversations/ConversationViewModel.m | 43 +- .../MenuActionsViewController.swift | 4 +- .../Views & Cells/LinkPreviewView.swift | 2 +- .../Views & Cells/OWSMessageBubbleView.m | 45 +- .../Views & Cells/OWSMessageCell.m | 6 +- .../Views & Cells/OWSQuotedMessageView.m | 2 +- .../Views & Cells/VoiceMessageView.swift | 2 +- .../Views & Cells/VoiceNoteLock.swift | 2 +- Session/Home/HomeVC.swift | 8 +- Session/Home/NewConversationButtonSet.swift | 30 +- Session/Meta/Images.xcassets/Contents.json | 6 +- .../Loki V2/Gear.imageset/Contents.json | 12 - .../Loki V2/Gear.imageset/Gear.pdf | Bin 7822 -> 0 bytes .../AddPerson.imageset/AddPerson.pdf | Bin .../AddPerson.imageset/Contents.json | 0 .../Session/ArrowUp.imageset/ArrowUp.pdf | 125 +++++ .../Session/ArrowUp.imageset/Contents.json | 12 + .../ArrowUpDarkMode.imageset/ArrowUp.pdf | Bin .../ArrowUpDarkMode.imageset/Contents.json | 0 .../ArrowUpLightMode.pdf | Bin .../ArrowUpLightMode.imageset/Contents.json | 0 .../ChatBubbles.imageset/ChatBubbles.pdf | Bin .../ChatBubbles.imageset/Contents.json | 0 .../Check.imageset/Check.pdf | Bin .../Check.imageset/Contents.json | 0 .../Circle.imageset/Circle.pdf | Bin .../Circle.imageset/Contents.json | 0 .../CircleCheck.imageset/CircleCheck.pdf | Bin .../CircleCheck.imageset/Contents.json | 0 .../CircleDotDotDot.pdf | Bin .../CircleDotDotDot.imageset/Contents.json | 0 .../CirclePause.imageset/CirclePause.pdf | Bin .../CirclePause.imageset/Contents.json | 0 .../CirclePlay.imageset/CirclePlay.pdf | Bin .../CirclePlay.imageset/Contents.json | 0 .../CirclePlus.imageset/CirclePlus.pdf | Bin .../CirclePlus.imageset/Contents.json | 0 .../{Loki V2 => Session}/Contents.json | 0 .../Contents.json | 0 .../FilledCircleCheckDarkMode.pdf | Bin .../Contents.json | 0 .../FilledCircleCheckLightMode.pdf | Bin .../Flag.imageset/Contents.json | 0 .../Flag.imageset/Flag.pdf | Bin .../Session/Gear.imageset/Contents.json | 12 + .../Session/Gear.imageset/Gear.pdf | 203 ++++++++ .../Globe.imageset/Contents.json | 0 .../Globe.imageset/Globe.pdf | Bin .../Group.imageset/Contents.json | 0 .../Group.imageset/Group.pdf | Bin .../Key.imageset/Contents.json | 0 .../{Loki V2 => Session}/Key.imageset/Key.pdf | Bin .../MagnifyingGlass.imageset/Contents.json | 0 .../MagnifyingGlass.pdf | Bin .../Message.imageset/Contents.json | 0 .../Message.imageset/Message.pdf | Bin .../Microphone.imageset/Contents.json | 0 .../Microphone.imageset/Microphone.pdf | Bin .../Mute.imageset/Contents.json | 0 .../Mute.imageset/Mute.pdf | 0 .../Pause.imageset/Contents.json | 0 .../Pause.imageset/Pause.pdf | 0 .../People.imageset/Contents.json | 0 .../People.imageset/People.pdf | Bin .../Play.imageset/Contents.json | 0 .../Play.imageset/Play.pdf | 0 .../Plus.imageset/Contents.json | 0 .../Plus.imageset/Plus.pdf | Bin .../QRCode.imageset/Contents.json | 0 .../QRCode.imageset/QRCodeFilled.pdf | Bin .../QuestionMark.imageset/Contents.json | 0 .../QuestionMark.imageset/QuestionMark.pdf | Bin .../SessionGreen32.imageset/Contents.json | 0 .../SessionGreen32.png | Bin .../SessionGreen32@2x.png | Bin .../SessionGreen32@3x.png | Bin .../SessionGreen64.imageset/Contents.json | 0 .../SessionGreen64.png | Bin .../SessionGreen64@2x.png | Bin .../SessionGreen64@3x.png | Bin .../SessionWhite16.imageset/Contents.json | 0 .../SessionWhite16.png | Bin .../SessionWhite16@2x.png | Bin .../SessionWhite16@3x.png | Bin .../SessionWhite24.imageset/Contents.json | 0 .../SessionWhite24.png | Bin .../SessionWhite24@2x.png | Bin .../SessionWhite24@3x.png | Bin .../SessionWhite40.imageset/Contents.json | 0 .../SessionWhite40.png | Bin .../SessionWhite40@2x.png | Bin .../SessionWhite40@3x.png | Bin .../Shield.imageset/Contents.json | 0 .../Shield.imageset/Shield.pdf | Bin .../Star.imageset/Contents.json | 0 .../Star.imageset/StarOutline.pdf | Bin .../Sun.imageset/Contents.json | 0 .../{Loki V2 => Session}/Sun.imageset/Sun.pdf | Bin .../X.imageset/Contents.json | 0 .../{Loki V2 => Session}/X.imageset/X.pdf | Bin Session/Meta/Signal-Bridging-Header.h | 2 + Session/Onboarding/PNOptionView.swift | 4 +- Session/Onboarding/RegisterVC.swift | 4 +- Session/Onboarding/SeedReminderView.swift | 2 +- Session/Onboarding/SeedVC.swift | 6 +- ...blicChatVC.swift => JoinOpenGroupVC.swift} | 6 +- Session/Path/PathVC.swift | 2 +- Session/Settings/AboutTableViewController.h | 9 - Session/Settings/AboutTableViewController.m | 162 ------ Session/Settings/MultiDeviceVC.swift | 4 +- Session/Settings/NukeDataModal.swift | 2 +- .../PrivacySettingsTableViewController.m | 47 -- Session/Settings/SeedModal.swift | 6 +- Session/Settings/SettingsVC.swift | 2 +- Session/Shared/BaseVC.swift | 2 +- Session/Shared/ConversationCell.swift | 11 +- Session/Shared/LoadingViewController.swift | 2 +- Session/Shared/UserCell.swift | 2 +- .../KeyPairMigrationSuccessSheet.swift | 4 +- Session/Sheets & Modals/Modal.swift | 4 +- Session/Sheets & Modals/Sheet.swift | 8 +- Session/Utilities/MentionUtilities.swift | 14 +- SessionMessagingKit/Configuration.swift | 1 - .../Database/Storage+ClosedGroups.swift | 79 +-- .../Database/Storage+Contacts.swift | 1 - .../Database/Storage+Messaging.swift | 4 - SessionMessagingKit/Database/TSDatabaseView.m | 1 - .../ClosedGroupControlMessage.swift | 1 - .../Control Messages/ControlMessage.swift | 1 - .../Messages/Signal/TSIncomingMessage.m | 1 - .../Messages/Signal/TSInfoMessage.m | 1 - .../Signal}/TypingIndicatorInteraction.swift | 0 .../Expiration/OWSDisappearingMessagesJob.m | 1 - .../MessageReceiver+Decryption.swift | 1 - .../MessageReceiver+Handling.swift | 1 - .../MessageSender+ClosedGroups.swift | 1 - .../MessageSender+Encryption.swift | 1 - .../Read Tracking/OWSReadReceiptManager.m | 17 +- SessionMessagingKit/Storage.swift | 9 +- SessionMessagingKit/Utilities/DotNetAPI.swift | 1 - .../Utilities/OWSAudioPlayer.h | 16 +- .../Utilities/OWSAudioPlayer.m | 25 +- .../Utilities/OWSBackgroundTask.m | 1 - .../Utilities/OWSDisappearingMessagesFinder.m | 1 - .../Utilities/OWSIdentityManager.m | 1 - .../Utilities/OWSIncomingMessageFinder.m | 1 - .../Utilities/OWSMediaGalleryFinder.m | 1 - .../Utilities/SSKEnvironment.m | 1 - .../Utilities/YapDatabaseConnection+OWS.m | 1 - .../Utilities/YapDatabaseTransaction+OWS.m | 1 - SessionProtocolKit/ClosedGroupRatchet.swift | 45 -- SessionProtocolKit/ClosedGroupSenderKey.swift | 47 -- SessionProtocolKit/Configuration.swift | 14 - SessionProtocolKit/Meta/Info.plist | 22 - SessionProtocolKit/Meta/SessionProtocolKit.h | 4 - SessionProtocolKit/SharedSenderKeys.swift | 164 ------ SessionProtocolKit/Storage.swift | 14 - SessionUIKit/Components/Button.swift | 2 +- SessionUIKit/Components/Separator.swift | 2 +- SessionUIKit/Components/TabBar.swift | 4 +- SessionUIKit/Components/TextField.swift | 13 +- SessionUIKit/Components/TextView.swift | 10 +- SessionUIKit/Style Guide/Colors.swift | 2 +- .../Contents.json | 0 .../Contents.json | 6 +- .../Contents.json | 10 +- SessionUIKit/Style Guide/Gradients.swift | 2 +- SessionUIKit/Style Guide/Values.swift | 26 +- SessionUtilitiesKit/General/General.swift | 2 + SignalUtilitiesKit/Configuration.swift | 9 - .../Migrations/ClosedGroupsV2Migration.swift | 38 -- .../Migrations/OWSDatabaseMigrationRunner.m | 3 +- .../Database/Storage+Conformances.swift | 2 +- .../MediaMessageView.swift | 4 + .../Messaging/ConversationStyle.swift | 21 +- .../MessageSender+Convenience.swift | 1 - .../Meta/SignalUtilitiesKit-Prefix.pch | 1 - SignalUtilitiesKit/Meta/SignalUtilitiesKit.h | 1 - .../Profile Pictures/ProfilePictureView.swift | 2 +- SignalUtilitiesKit/Utilities/AppSetup.m | 1 - SignalUtilitiesKit/Utilities/AppVersion.m | 1 - SignalUtilitiesKit/Utilities/ByteParser.m | 1 - SignalUtilitiesKit/Utilities/FunctionalUtil.m | 1 - SignalUtilitiesKit/Utilities/NSArray+OWS.m | 1 - SignalUtilitiesKit/Utilities/OWSError.m | 1 - SignalUtilitiesKit/Utilities/OWSOperation.m | 1 - SignalUtilitiesKit/Utilities/SignalAccount.m | 1 - .../UIViewController+Utilities.swift | 2 +- 221 files changed, 3071 insertions(+), 1362 deletions(-) create mode 100644 Session/Conversations V2/Context Menu/ContextMenuVC+Action.swift create mode 100644 Session/Conversations V2/Context Menu/ContextMenuVC+ActionView.swift create mode 100644 Session/Conversations V2/Context Menu/ContextMenuVC.swift create mode 100644 Session/Conversations V2/Context Menu/ContextMenuWindow.swift create mode 100644 Session/Conversations V2/ConversationVC+Interaction.swift create mode 100644 Session/Conversations V2/ConversationVC+ScrollToBottomButton.swift create mode 100644 Session/Conversations V2/ConversationVC.swift create mode 100644 Session/Conversations V2/Input View/InputTextView.swift create mode 100644 Session/Conversations V2/Input View/InputView.swift create mode 100644 Session/Conversations V2/Input View/InputViewButton.swift create mode 100644 Session/Conversations V2/Message Cells/Content Views/DocumentView.swift rename Session/{Conversations/Views & Cells/MediaAlbumCellView.swift => Conversations V2/Message Cells/Content Views/MediaAlbumView.swift} (82%) create mode 100644 Session/Conversations V2/Message Cells/Content Views/MediaLoaderView.swift create mode 100644 Session/Conversations V2/Message Cells/Content Views/MediaTextOverlayView.swift rename Session/{Conversations/Views & Cells/ConversationMediaView.swift => Conversations V2/Message Cells/Content Views/MediaView.swift} (87%) create mode 100644 Session/Conversations V2/Message Cells/Content Views/QuoteView.swift rename Session/{Conversations/Views & Cells => Conversations V2/Message Cells/Content Views}/TypingIndicatorView.swift (100%) create mode 100644 Session/Conversations V2/Message Cells/Content Views/VoiceMessageViewV2.swift create mode 100644 Session/Conversations V2/Message Cells/InfoMessageCell.swift create mode 100644 Session/Conversations V2/Message Cells/MessageCell.swift create mode 100644 Session/Conversations V2/Message Cells/TypingIndicatorCellV2.swift create mode 100644 Session/Conversations V2/Message Cells/VisibleMessageCell.swift delete mode 100644 Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Contents.json delete mode 100644 Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Gear.pdf rename Session/Meta/Images.xcassets/{Loki V2 => Session}/AddPerson.imageset/AddPerson.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/AddPerson.imageset/Contents.json (100%) create mode 100644 Session/Meta/Images.xcassets/Session/ArrowUp.imageset/ArrowUp.pdf create mode 100644 Session/Meta/Images.xcassets/Session/ArrowUp.imageset/Contents.json rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ArrowUpDarkMode.imageset/ArrowUp.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ArrowUpDarkMode.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ArrowUpLightMode.imageset/ArrowUpLightMode.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ArrowUpLightMode.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ChatBubbles.imageset/ChatBubbles.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/ChatBubbles.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Check.imageset/Check.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Check.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Circle.imageset/Circle.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Circle.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CircleCheck.imageset/CircleCheck.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CircleCheck.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CircleDotDotDot.imageset/CircleDotDotDot.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CircleDotDotDot.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePause.imageset/CirclePause.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePause.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePlay.imageset/CirclePlay.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePlay.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePlus.imageset/CirclePlus.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/CirclePlus.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/FilledCircleCheckDarkMode.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/FilledCircleCheckDarkMode.imageset/FilledCircleCheckDarkMode.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/FilledCircleCheckLightMode.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/FilledCircleCheckLightMode.imageset/FilledCircleCheckLightMode.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Flag.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Flag.imageset/Flag.pdf (100%) create mode 100644 Session/Meta/Images.xcassets/Session/Gear.imageset/Contents.json create mode 100644 Session/Meta/Images.xcassets/Session/Gear.imageset/Gear.pdf rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Globe.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Globe.imageset/Globe.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Group.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Group.imageset/Group.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Key.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Key.imageset/Key.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/MagnifyingGlass.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/MagnifyingGlass.imageset/MagnifyingGlass.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Message.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Message.imageset/Message.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Microphone.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Microphone.imageset/Microphone.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Mute.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Mute.imageset/Mute.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Pause.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Pause.imageset/Pause.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/People.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/People.imageset/People.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Play.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Play.imageset/Play.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Plus.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Plus.imageset/Plus.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/QRCode.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/QRCode.imageset/QRCodeFilled.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/QuestionMark.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/QuestionMark.imageset/QuestionMark.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen32.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen32.imageset/SessionGreen32.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen32.imageset/SessionGreen32@2x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen32.imageset/SessionGreen32@3x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen64.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen64.imageset/SessionGreen64.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen64.imageset/SessionGreen64@2x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionGreen64.imageset/SessionGreen64@3x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite16.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite16.imageset/SessionWhite16.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite16.imageset/SessionWhite16@2x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite16.imageset/SessionWhite16@3x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite24.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite24.imageset/SessionWhite24.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite24.imageset/SessionWhite24@2x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite24.imageset/SessionWhite24@3x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite40.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite40.imageset/SessionWhite40.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite40.imageset/SessionWhite40@2x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/SessionWhite40.imageset/SessionWhite40@3x.png (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Shield.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Shield.imageset/Shield.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Star.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Star.imageset/StarOutline.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Sun.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/Sun.imageset/Sun.pdf (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/X.imageset/Contents.json (100%) rename Session/Meta/Images.xcassets/{Loki V2 => Session}/X.imageset/X.pdf (100%) rename Session/Open Groups/{JoinPublicChatVC.swift => JoinOpenGroupVC.swift} (98%) delete mode 100644 Session/Settings/AboutTableViewController.h delete mode 100644 Session/Settings/AboutTableViewController.m rename {Session/Conversations/Views & Cells => SessionMessagingKit/Messages/Signal}/TypingIndicatorInteraction.swift (100%) delete mode 100644 SessionProtocolKit/ClosedGroupRatchet.swift delete mode 100644 SessionProtocolKit/ClosedGroupSenderKey.swift delete mode 100644 SessionProtocolKit/Configuration.swift delete mode 100644 SessionProtocolKit/Meta/Info.plist delete mode 100644 SessionProtocolKit/Meta/SessionProtocolKit.h delete mode 100644 SessionProtocolKit/SharedSenderKeys.swift delete mode 100644 SessionProtocolKit/Storage.swift rename SessionUIKit/Style Guide/Colors.xcassets/{session_new_conversation_button_shadow.colorset => session_expanded_button_glow_color.colorset}/Contents.json (100%) delete mode 100644 SignalUtilitiesKit/Database/Migrations/ClosedGroupsV2Migration.swift diff --git a/Podfile b/Podfile index bca1c9dc7..10ffca28a 100644 --- a/Podfile +++ b/Podfile @@ -74,17 +74,6 @@ target 'SessionMessagingKit' do pod 'YapDatabase/SQLCipher', :git => 'https://github.com/loki-project/session-ios-yap-database.git', branch: 'signal-release', :inhibit_warnings => true end -target 'SessionProtocolKit' do - pod 'CocoaLumberjack', :inhibit_warnings => true - pod 'CryptoSwift', :inhibit_warnings => true - pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit.git', :inhibit_warnings => true - pod 'GRKOpenSSLFramework', :inhibit_warnings => true - pod 'HKDFKit', :inhibit_warnings => true - pod 'PromiseKit', :inhibit_warnings => true - pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true - pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true -end - target 'SessionSnodeKit' do pod 'CryptoSwift', :inhibit_warnings => true pod 'Curve25519Kit', git: 'https://github.com/signalapp/Curve25519Kit.git', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index 78a63d0c5..3fc70a96b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -124,7 +124,6 @@ PODS: DEPENDENCIES: - AFNetworking - - CocoaLumberjack - CryptoSwift - Curve25519Kit (from `https://github.com/signalapp/Curve25519Kit.git`) - FeedKit @@ -217,6 +216,6 @@ SPEC CHECKSUMS: YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: bb4f6cffd6e7c08814b945e1787d01d639036b1e +PODFILE CHECKSUM: 2fca3f32c171e1324c9e3809b96a32d4a929d05c COCOAPODS: 1.10.0.rc.1 diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index e34c89a74..a4f93004c 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */; }; 340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC886204DAC8C007AEB0F /* AddToBlockListViewController.m */; }; 340FC8B4204DAC8D007AEB0F /* OWSBackupSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */; }; - 340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */; }; 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */; }; 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC89A204DAC8D007AEB0F /* OWSConversationSettingsViewController.m */; }; 34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34129B8521EF8779005457A8 /* LinkPreviewView.swift */; }; @@ -43,7 +42,7 @@ 347850551FD749C0007B8332 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; }; 347850571FD86544007B8332 /* SAEFailedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347850561FD86544007B8332 /* SAEFailedViewController.swift */; }; 348570A820F67575004FF32B /* OWSMessageHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 348570A620F67574004FF32B /* OWSMessageHeaderView.m */; }; - 3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3488F9352191CC4000E524CC /* ConversationMediaView.swift */; }; + 3488F9362191CC4000E524CC /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3488F9352191CC4000E524CC /* MediaView.swift */; }; 3496744D2076768700080B5F /* OWSMessageBubbleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496744C2076768700080B5F /* OWSMessageBubbleView.m */; }; 3496744F2076ACD000080B5F /* LongTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496744E2076ACCE00080B5F /* LongTextViewController.swift */; }; 3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34969559219B605E00DCFE74 /* ImagePickerController.swift */; }; @@ -58,12 +57,11 @@ 3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956A21A301A100DCFE74 /* OWSBackupJob.m */; }; 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */; }; 34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */; }; - 34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */; }; + 34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */; }; 34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34ABC0E321DD20C500ED9469 /* ConversationMessageMapping.swift */; }; 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AC0A21211C829E00997B47 /* OWSLabel.m */; }; 34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; }; 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */; }; - 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */; }; 34B6A907218B5241007C4606 /* TypingIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A906218B5240007C4606 /* TypingIndicatorCell.swift */; }; 34B6A90B218BA1D1007C4606 /* typing-animation.gif in Resources */ = {isa = PBXBuildFile; fileRef = 34B6A90A218BA1D0007C4606 /* typing-animation.gif */; }; 34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2D1F7ABCE000D7438D /* GifPickerViewController.swift */; }; @@ -189,7 +187,6 @@ A163E8AB16F3F6AA0094D68B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A163E8AA16F3F6A90094D68B /* Security.framework */; }; A1C32D5017A06538000A904E /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4F17A06537000A904E /* AddressBookUI.framework */; }; A1C32D5117A06544000A904E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4D17A0652C000A904E /* AddressBook.framework */; }; - A33A4BA9D050805FE156E3ED /* Pods_SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2183DCA28E0620BC73FCC554 /* Pods_SessionProtocolKit.framework */; }; A5509ECA1A69AB8B00ABA4BC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A5509EC91A69AB8B00ABA4BC /* Main.storyboard */; }; AD83FF3F1A73426500B5C81A /* audio_pause_button_blue.png in Resources */ = {isa = PBXBuildFile; fileRef = AD83FF381A73426500B5C81A /* audio_pause_button_blue.png */; }; AD83FF401A73426500B5C81A /* audio_pause_button_blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AD83FF391A73426500B5C81A /* audio_pause_button_blue@2x.png */; }; @@ -216,13 +213,22 @@ B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6B226961BE4B7D200860F4D /* ContactsUI.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; B6F509971AA53F760068F56A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; }; B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */; }; + B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */; }; + B8041AA725C90927003C2166 /* TypingIndicatorCellV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8041AA625C90927003C2166 /* TypingIndicatorCellV2.swift */; }; B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */; }; + B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D2825C7A4B400488AB4 /* InputView.swift */; }; + B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3225C7A8C600488AB4 /* InputViewButton.swift */; }; + B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3C25C7B34D00488AB4 /* InputTextView.swift */; }; B82B40882399EB0E00A248E7 /* LandingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40872399EB0E00A248E7 /* LandingVC.swift */; }; B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40892399EC0600A248E7 /* FakeChatView.swift */; }; B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408B239A068800A248E7 /* RegisterVC.swift */; }; B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; }; B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; }; B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.swift */; }; + B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835246D25C38ABF0089A44F /* ConversationVC.swift */; }; + B835247925C38D880089A44F /* MessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835247825C38D880089A44F /* MessageCell.swift */; }; + B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835249A25C3AB650089A44F /* VisibleMessageCell.swift */; }; + B83524A525C3BA4B0089A44F /* InfoMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83524A425C3BA4B0089A44F /* InfoMessageCell.swift */; }; B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */; }; B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; @@ -232,6 +238,9 @@ B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; }; B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; }; B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; }; + B8569AD325CBA13D00DBA3DB /* MediaTextOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */; }; + B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; }; B85A68B12587141A008CC492 /* Storage+Resetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85A68B02587141A008CC492 /* Storage+Resetting.swift */; }; B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; @@ -266,8 +275,10 @@ B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; }; B886B4A92398BA1500211ABE /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A82398BA1500211ABE /* QRCode.swift */; }; B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */; }; + B88A1AC725C90A4700E6D421 /* TypingIndicatorInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */; }; B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */; }; B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B894D0742339EDCF00B4D94D /* NukeDataModal.swift */; }; + B897621C25D201F7004F83B2 /* ConversationVC+ScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B897621B25D201F7004F83B2 /* ConversationVC+ScrollToBottomButton.swift */; }; B8A14D702589CE9000E70D57 /* KeyPairMigrationSuccessSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A14D6F2589CE9000E70D57 /* KeyPairMigrationSuccessSheet.swift */; }; B8AE75A425A6C6A6001A84D2 /* Data+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8AE75A325A6C6A6001A84D2 /* Data+Trimming.swift */; }; B8AE760B25ABFB5A001A84D2 /* GeneralUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B8AE760A25ABFB5A001A84D2 /* GeneralUtilities.m */; }; @@ -284,23 +295,15 @@ B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C2B2C72563685C00551B4D /* CircleView.swift */; }; B8C2B332256376F000551B4D /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B8C2B331256376F000551B4D /* ThreadUtil.m */; }; B8C2B3442563782400551B4D /* ThreadUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = B8C2B33B2563770800551B4D /* ThreadUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B8CA010125A293260091AF73 /* ClosedGroupSenderKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA010025A293260091AF73 /* ClosedGroupSenderKey.swift */; }; - B8CA010B25A293530091AF73 /* ClosedGroupRatchet.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA010A25A293530091AF73 /* ClosedGroupRatchet.swift */; }; - B8CA011525A293800091AF73 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011425A293800091AF73 /* Configuration.swift */; }; - B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */; }; - B8CA014125A293EE0091AF73 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CA014025A293EE0091AF73 /* Storage.swift */; }; B8CADAE925AFADF400AAFA15 /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3AAFFCB25AE92150089E6DD /* OpenGroupManager.swift */; }; B8CCF6352396005F0091D419 /* SpaceMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */; }; B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */; }; - B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */; }; + B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinOpenGroupVC.swift */; }; B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF6422397711F0091D419 /* SettingsVC.swift */; }; B8D64FBB25BA78310029CFC0 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; }; - B8D64FBC25BA78310029CFC0 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; B8D64FBD25BA78310029CFC0 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; B8D64FBE25BA78310029CFC0 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; B8D64FC725BA78520029CFC0 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; }; - B8D64FC825BA78520029CFC0 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; - B8D64FC925BA78520029CFC0 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; B8D64FCB25BA78A90029CFC0 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; }; B8FF8DAE25C0D00F004D1F22 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; }; B8FF8DAF25C0D00F004D1F22 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; @@ -323,12 +326,15 @@ C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; }; C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; }; C31FFE57254A5FFE00F19441 /* KeyPairUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */; }; - C32A025A25A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32A025925A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift */; }; + C32824D325C9F9790062D0A7 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; }; + C328250F25CA06020062D0A7 /* VoiceMessageViewV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328250E25CA06020062D0A7 /* VoiceMessageViewV2.swift */; }; + C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328251E25CA3A900062D0A7 /* QuoteView.swift */; }; + C328253025CA55370062D0A7 /* ContextMenuWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328252F25CA55360062D0A7 /* ContextMenuWindow.swift */; }; + C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328253F25CA55880062D0A7 /* ContextMenuVC.swift */; }; + C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328254825CA60E60062D0A7 /* ContextMenuVC+Action.swift */; }; + C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */; }; C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; }; C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; - C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; - C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; }; C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; }; C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; }; C32C59C0256DB41F003C73A2 /* TSThread.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAD3255A580300E217F9 /* TSThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -754,11 +760,8 @@ C3C2A7712553A41E00C340D1 /* ControlMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A7702553A41E00C340D1 /* ControlMessage.swift */; }; C3C2A7842553AAF300C340D1 /* SNProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A7822553AAF200C340D1 /* SNProto.swift */; }; C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A7832553AAF300C340D1 /* SessionProtos.pb.swift */; }; - C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C3C2A8642553B41A00C340D1 /* SessionProtocolKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C3C2A86A2553B41A00C340D1 /* SessionProtocolKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; C3C2ABD22553C6C900C340D1 /* Data+SecureRandom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */; }; C3C2AC2E2553CBEB00C340D1 /* String+Trimming.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */; }; - C3C2AC372553CCE600C340D1 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; C3CA3AA2255CDADA00F4C6D4 /* english.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3AA1255CDADA00F4C6D4 /* english.txt */; }; C3CA3AB4255CDAE600F4C6D4 /* japanese.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3AB3255CDAE600F4C6D4 /* japanese.txt */; }; C3CA3ABE255CDB0D00F4C6D4 /* portuguese.txt in Resources */ = {isa = PBXBuildFile; fileRef = C3CA3ABD255CDB0D00F4C6D4 /* portuguese.txt */; }; @@ -838,13 +841,6 @@ remoteGlobalIDString = C3C2A6EF25539DE700C340D1; remoteInfo = SessionMessagingKit; }; - B8D64FB525BA78270029CFC0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D221A080169C9E5E00537ABF /* Project object */; - proxyType = 1; - remoteGlobalIDString = C3C2A8612553B41A00C340D1; - remoteInfo = SessionProtocolKit; - }; B8D64FB725BA78270029CFC0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D221A080169C9E5E00537ABF /* Project object */; @@ -866,13 +862,6 @@ remoteGlobalIDString = C3C2A6EF25539DE700C340D1; remoteInfo = SessionMessagingKit; }; - B8D64FC125BA784A0029CFC0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D221A080169C9E5E00537ABF /* Project object */; - proxyType = 1; - remoteGlobalIDString = C3C2A8612553B41A00C340D1; - remoteInfo = SessionProtocolKit; - }; B8D64FC325BA784A0029CFC0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D221A080169C9E5E00537ABF /* Project object */; @@ -922,13 +911,6 @@ remoteGlobalIDString = C3C2A6EF25539DE700C340D1; remoteInfo = SessionMessagingKit; }; - C3C2A8672553B41A00C340D1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D221A080169C9E5E00537ABF /* Project object */; - proxyType = 1; - remoteGlobalIDString = C3C2A8612553B41A00C340D1; - remoteInfo = SessionProtocolKit; - }; C3D90A5425773A1A002C9DF5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D221A080169C9E5E00537ABF /* Project object */; @@ -971,7 +953,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - C3C2A86A2553B41A00C340D1 /* SessionProtocolKit.framework in Embed Frameworks */, C3C2A681255388CC00C340D1 /* SessionUtilitiesKit.framework in Embed Frameworks */, C33FD9B3255A548A00E217F9 /* SignalUtilitiesKit.framework in Embed Frameworks */, C3C2A5A7255385C100C340D1 /* SessionSnodeKit.framework in Embed Frameworks */, @@ -998,7 +979,6 @@ 340FC87E204DAC8C007AEB0F /* PrivacySettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrivacySettingsTableViewController.m; sourceTree = ""; }; 340FC87F204DAC8C007AEB0F /* OWSBackupSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupSettingsViewController.h; sourceTree = ""; }; 340FC883204DAC8C007AEB0F /* OWSSoundSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSSoundSettingsViewController.m; sourceTree = ""; }; - 340FC884204DAC8C007AEB0F /* AboutTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutTableViewController.h; sourceTree = ""; }; 340FC886204DAC8C007AEB0F /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = ""; }; 340FC888204DAC8C007AEB0F /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = ""; }; 340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = ""; }; @@ -1006,7 +986,6 @@ 340FC88E204DAC8C007AEB0F /* OWSBackupSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackupSettingsViewController.m; sourceTree = ""; }; 340FC88F204DAC8C007AEB0F /* PrivacySettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivacySettingsTableViewController.h; sourceTree = ""; }; 340FC892204DAC8C007AEB0F /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = ""; }; - 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutTableViewController.m; sourceTree = ""; }; 340FC894204DAC8C007AEB0F /* OWSSoundSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSSoundSettingsViewController.h; sourceTree = ""; }; 340FC896204DAC8C007AEB0F /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = ""; }; 340FC899204DAC8D007AEB0F /* OWSConversationSettingsViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsViewDelegate.h; sourceTree = ""; }; @@ -1045,7 +1024,7 @@ 347850561FD86544007B8332 /* SAEFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEFailedViewController.swift; sourceTree = ""; }; 348570A620F67574004FF32B /* OWSMessageHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageHeaderView.m; sourceTree = ""; }; 348570A720F67574004FF32B /* OWSMessageHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageHeaderView.h; sourceTree = ""; }; - 3488F9352191CC4000E524CC /* ConversationMediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationMediaView.swift; sourceTree = ""; }; + 3488F9352191CC4000E524CC /* MediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = ""; }; 3496744B2076768600080B5F /* OWSMessageBubbleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageBubbleView.h; sourceTree = ""; }; 3496744C2076768700080B5F /* OWSMessageBubbleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessageBubbleView.m; sourceTree = ""; }; 3496744E2076ACCE00080B5F /* LongTextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LongTextViewController.swift; sourceTree = ""; }; @@ -1066,7 +1045,7 @@ 3496956C21A301A100DCFE74 /* OWSBackupImportJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupImportJob.h; sourceTree = ""; }; 3496956D21A301A100DCFE74 /* OWSBackupIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupIO.h; sourceTree = ""; }; 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSImagePickerController.swift; sourceTree = ""; }; - 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaAlbumCellView.swift; sourceTree = ""; }; + 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaAlbumView.swift; sourceTree = ""; }; 34ABC0E321DD20C500ED9469 /* ConversationMessageMapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationMessageMapping.swift; sourceTree = ""; }; 34AC0A21211C829E00997B47 /* OWSLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSLabel.m; sourceTree = ""; }; 34AC0A22211C829E00997B47 /* OWSLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLabel.h; sourceTree = ""; }; @@ -1278,13 +1257,22 @@ B69CD25019773E79005CE69A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; B6B226961BE4B7D200860F4D /* ContactsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ContactsUI.framework; path = System/Library/Frameworks/ContactsUI.framework; sourceTree = SDKROOT; }; B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; }; + B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderView.swift; sourceTree = ""; }; + B8041AA625C90927003C2166 /* TypingIndicatorCellV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorCellV2.swift; sourceTree = ""; }; B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewClosedGroupVC.swift; sourceTree = ""; }; + B8269D2825C7A4B400488AB4 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; + B8269D3225C7A8C600488AB4 /* InputViewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputViewButton.swift; sourceTree = ""; }; + B8269D3C25C7B34D00488AB4 /* InputTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = ""; }; B82B40872399EB0E00A248E7 /* LandingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingVC.swift; sourceTree = ""; }; B82B40892399EC0600A248E7 /* FakeChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeChatView.swift; sourceTree = ""; }; B82B408B239A068800A248E7 /* RegisterVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterVC.swift; sourceTree = ""; }; B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = ""; }; B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = ""; }; B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = ""; }; + B835246D25C38ABF0089A44F /* ConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationVC.swift; sourceTree = ""; }; + B835247825C38D880089A44F /* MessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCell.swift; sourceTree = ""; }; + B835249A25C3AB650089A44F /* VisibleMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleMessageCell.swift; sourceTree = ""; }; + B83524A425C3BA4B0089A44F /* InfoMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoMessageCell.swift; sourceTree = ""; }; B837867F2586D296003CE78E /* KeyPairMigrationSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairMigrationSheet.swift; sourceTree = ""; }; B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationButtonSet.swift; sourceTree = ""; }; B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scaling.swift"; sourceTree = ""; }; @@ -1297,6 +1285,9 @@ B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = ""; }; B8544E3223D50E4900299F14 /* AppearanceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceUtilities.swift; sourceTree = ""; }; B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OWSLinkPreview+Conversion.swift"; sourceTree = ""; }; + B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+Interaction.swift"; sourceTree = ""; }; + B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTextOverlayView.swift; sourceTree = ""; }; + B8569AE225CBB19A00DBA3DB /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = ""; }; B85A68B02587141A008CC492 /* Storage+Resetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Resetting.swift"; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; @@ -1310,6 +1301,7 @@ B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMembersVC.swift; sourceTree = ""; }; B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeWrapperVC.swift; sourceTree = ""; }; B894D0742339EDCF00B4D94D /* NukeDataModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NukeDataModal.swift; sourceTree = ""; }; + B897621B25D201F7004F83B2 /* ConversationVC+ScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+ScrollToBottomButton.swift"; sourceTree = ""; }; B8A14D6F2589CE9000E70D57 /* KeyPairMigrationSuccessSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairMigrationSuccessSheet.swift; sourceTree = ""; }; B8AE75A325A6C6A6001A84D2 /* Data+Trimming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Trimming.swift"; sourceTree = ""; }; B8AE760925ABFB00001A84D2 /* GeneralUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneralUtilities.h; sourceTree = ""; }; @@ -1336,15 +1328,10 @@ B8C2B331256376F000551B4D /* ThreadUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadUtil.m; sourceTree = ""; }; B8C2B33B2563770800551B4D /* ThreadUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadUtil.h; sourceTree = ""; }; B8C9689023FA1401005F64E0 /* AppMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMode.swift; sourceTree = ""; }; - B8CA010025A293260091AF73 /* ClosedGroupSenderKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedGroupSenderKey.swift; sourceTree = ""; }; - B8CA010A25A293530091AF73 /* ClosedGroupRatchet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedGroupRatchet.swift; sourceTree = ""; }; - B8CA011425A293800091AF73 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; - B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedSenderKeys.swift; sourceTree = ""; }; - B8CA014025A293EE0091AF73 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Regular.ttf"; sourceTree = ""; }; B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPrivateChatVC.swift; sourceTree = ""; }; B8CCF638239721E20091D419 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = ""; }; - B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinPublicChatVC.swift; sourceTree = ""; }; + B8CCF63E23975CFB0091D419 /* JoinOpenGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinOpenGroupVC.swift; sourceTree = ""; }; B8CCF6422397711F0091D419 /* SettingsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsVC.swift; sourceTree = ""; }; B8D8F1372566120F0092EF10 /* Storage+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+ClosedGroups.swift"; sourceTree = ""; }; B8D8F17625661AFA0092EF10 /* Storage+Jobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+Jobs.swift"; sourceTree = ""; }; @@ -1375,7 +1362,12 @@ C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = ""; }; C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+VolumeSamples.swift"; sourceTree = ""; }; C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPairUtilities.swift; sourceTree = ""; }; - C32A025925A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedGroupsV2Migration.swift; sourceTree = ""; }; + C328250E25CA06020062D0A7 /* VoiceMessageViewV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageViewV2.swift; sourceTree = ""; }; + C328251E25CA3A900062D0A7 /* QuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView.swift; sourceTree = ""; }; + C328252F25CA55360062D0A7 /* ContextMenuWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuWindow.swift; sourceTree = ""; }; + C328253F25CA55880062D0A7 /* ContextMenuVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuVC.swift; sourceTree = ""; }; + C328254825CA60E60062D0A7 /* ContextMenuVC+Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContextMenuVC+Action.swift"; sourceTree = ""; }; + C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContextMenuVC+ActionView.swift"; sourceTree = ""; }; C32C5A87256DBCF9003C73A2 /* MessageReceiver+Handling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageReceiver+Handling.swift"; sourceTree = ""; }; C32C5B3E256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSQuotedMessage+Conversion.swift"; sourceTree = ""; }; C32C5FD5256E0346003C73A2 /* Notification+Thread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Thread.swift"; sourceTree = ""; }; @@ -1827,9 +1819,6 @@ C3C2A7702553A41E00C340D1 /* ControlMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlMessage.swift; sourceTree = ""; }; C3C2A7822553AAF200C340D1 /* SNProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNProto.swift; sourceTree = ""; }; C3C2A7832553AAF300C340D1 /* SessionProtos.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionProtos.pb.swift; sourceTree = ""; }; - C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SessionProtocolKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C3C2A8642553B41A00C340D1 /* SessionProtocolKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SessionProtocolKit.h; sourceTree = ""; }; - C3C2A8652553B41A00C340D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+SecureRandom.swift"; sourceTree = ""; }; C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Trimming.swift"; sourceTree = ""; }; C3C3CF8824D8EED300E1CCE7 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; @@ -1892,9 +1881,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C32824D325C9F9790062D0A7 /* SessionSnodeKit.framework in Frameworks */, B8D64FC725BA78520029CFC0 /* SessionMessagingKit.framework in Frameworks */, - B8D64FC825BA78520029CFC0 /* SessionProtocolKit.framework in Frameworks */, - B8D64FC925BA78520029CFC0 /* SessionSnodeKit.framework in Frameworks */, C3D90A5C25773A25002C9DF5 /* SessionUtilitiesKit.framework in Frameworks */, C3402FE52559036600EA6424 /* SessionUIKit.framework in Frameworks */, B8D64FCB25BA78A90029CFC0 /* SignalUtilitiesKit.framework in Frameworks */, @@ -1907,7 +1895,6 @@ buildActionMask = 2147483647; files = ( B8D64FBB25BA78310029CFC0 /* SessionMessagingKit.framework in Frameworks */, - B8D64FBC25BA78310029CFC0 /* SessionProtocolKit.framework in Frameworks */, B8D64FBD25BA78310029CFC0 /* SessionSnodeKit.framework in Frameworks */, B8D64FBE25BA78310029CFC0 /* SessionUtilitiesKit.framework in Frameworks */, C38EF00C255B61CC007E1867 /* SignalUtilitiesKit.framework in Frameworks */, @@ -1927,7 +1914,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C32A027E25A80428000ED5D4 /* SessionProtocolKit.framework in Frameworks */, C38EF48A255B7E3F007E1867 /* SessionUIKit.framework in Frameworks */, C33FD9C2255A54EF00E217F9 /* SessionMessagingKit.framework in Frameworks */, C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */, @@ -1957,28 +1943,17 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C32A027D25A80423000ED5D4 /* SessionProtocolKit.framework in Frameworks */, 5DF9AB212C6DB1E8BE70EFF6 /* Pods_SessionMessagingKit.framework in Frameworks */, C3C2A70B25539E1E00C340D1 /* SessionSnodeKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - C3C2A85F2553B41A00C340D1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A33A4BA9D050805FE156E3ED /* Pods_SessionProtocolKit.framework in Frameworks */, - C3C2AC372553CCE600C340D1 /* SessionUtilitiesKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D221A086169C9E5E00537ABF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( B8FF8DAE25C0D00F004D1F22 /* SessionMessagingKit.framework in Frameworks */, B8FF8DAF25C0D00F004D1F22 /* SessionUtilitiesKit.framework in Frameworks */, - C32A027F25A80432000ED5D4 /* SessionProtocolKit.framework in Frameworks */, C37F54DC255BB84A002AEA92 /* SessionSnodeKit.framework in Frameworks */, C37F5414255BAFA7002AEA92 /* SignalUtilitiesKit.framework in Frameworks */, 455A16DD1F1FEA0000F86704 /* Metal.framework in Frameworks */, @@ -2236,6 +2211,56 @@ path = ..; sourceTree = ""; }; + B8041A7325C8F758003C2166 /* Content Views */ = { + isa = PBXGroup; + children = ( + 34A8B3502190A40E00218A25 /* MediaAlbumView.swift */, + B8569AD225CBA13D00DBA3DB /* MediaTextOverlayView.swift */, + 3488F9352191CC4000E524CC /* MediaView.swift */, + B8041A9425C8FA1D003C2166 /* MediaLoaderView.swift */, + C328251E25CA3A900062D0A7 /* QuoteView.swift */, + 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */, + C328250E25CA06020062D0A7 /* VoiceMessageViewV2.swift */, + B8569AE225CBB19A00DBA3DB /* DocumentView.swift */, + ); + path = "Content Views"; + sourceTree = ""; + }; + B835246C25C38AA20089A44F /* Conversations V2 */ = { + isa = PBXGroup; + children = ( + B835246D25C38ABF0089A44F /* ConversationVC.swift */, + B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */, + B897621B25D201F7004F83B2 /* ConversationVC+ScrollToBottomButton.swift */, + B887C38125C7C79700E11DAE /* Input View */, + B835247725C38D190089A44F /* Message Cells */, + C328252E25CA54F70062D0A7 /* Context Menu */, + ); + path = "Conversations V2"; + sourceTree = ""; + }; + B835247725C38D190089A44F /* Message Cells */ = { + isa = PBXGroup; + children = ( + B835247825C38D880089A44F /* MessageCell.swift */, + B835249A25C3AB650089A44F /* VisibleMessageCell.swift */, + B83524A425C3BA4B0089A44F /* InfoMessageCell.swift */, + B8041AA625C90927003C2166 /* TypingIndicatorCellV2.swift */, + B8041A7325C8F758003C2166 /* Content Views */, + ); + path = "Message Cells"; + sourceTree = ""; + }; + B887C38125C7C79700E11DAE /* Input View */ = { + isa = PBXGroup; + children = ( + B8269D2825C7A4B400488AB4 /* InputView.swift */, + B8269D3225C7A8C600488AB4 /* InputViewButton.swift */, + B8269D3C25C7B34D00488AB4 /* InputTextView.swift */, + ); + path = "Input View"; + sourceTree = ""; + }; B8A582AB258C64E800AFD84C /* Database */ = { isa = PBXGroup; children = ( @@ -2477,6 +2502,17 @@ path = Meta; sourceTree = ""; }; + C328252E25CA54F70062D0A7 /* Context Menu */ = { + isa = PBXGroup; + children = ( + C328252F25CA55360062D0A7 /* ContextMenuWindow.swift */, + C328253F25CA55880062D0A7 /* ContextMenuVC.swift */, + C328254825CA60E60062D0A7 /* ContextMenuVC+Action.swift */, + C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */, + ); + path = "Context Menu"; + sourceTree = ""; + }; C32B405424A961E1001117B5 /* Dependencies */ = { isa = PBXGroup; children = ( @@ -2537,6 +2573,7 @@ C33FDB48255A580C00E217F9 /* TSOutgoingMessage.h */, C33FDB56255A580D00E217F9 /* TSOutgoingMessage.m */, B84072952565E9F50037CB17 /* TSOutgoingMessage+Conversion.swift */, + 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */, ); path = Signal; sourceTree = ""; @@ -2790,8 +2827,6 @@ C360969125AD1765008B62B2 /* Settings */ = { isa = PBXGroup; children = ( - 340FC884204DAC8C007AEB0F /* AboutTableViewController.h */, - 340FC893204DAC8C007AEB0F /* AboutTableViewController.m */, 340FC88B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.h */, 340FC87B204DAC8C007AEB0F /* NotificationSettingsOptionsViewController.m */, 340FC88A204DAC8C007AEB0F /* NotificationSettingsViewController.h */, @@ -2821,7 +2856,7 @@ C360969B25AD180B008B62B2 /* Open Groups */ = { isa = PBXGroup; children = ( - B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */, + B8CCF63E23975CFB0091D419 /* JoinOpenGroupVC.swift */, ); path = "Open Groups"; sourceTree = ""; @@ -2895,15 +2930,12 @@ isa = PBXGroup; children = ( 457F671A20746193000EABCD /* QuotedReplyPreview.swift */, - 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */, 4C043929220A9EC800BAEA63 /* VoiceNoteLock.swift */, 34129B8521EF8779005457A8 /* LinkPreviewView.swift */, 34D1F0BB1F8D108C0066283D /* AttachmentUploadView.h */, 34D1F0BC1F8D108C0066283D /* AttachmentUploadView.m */, - 3488F9352191CC4000E524CC /* ConversationMediaView.swift */, 34D1F0961F867BFC0066283D /* ConversationViewCell.h */, 34D1F0971F867BFC0066283D /* ConversationViewCell.m */, - 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */, 34EA693F2194933900702471 /* MediaDownloadView.swift */, 34EA69412194DE7F00702471 /* MediaUploadView.swift */, B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, @@ -2932,7 +2964,6 @@ 34D1F0A51F867BFC0066283D /* OWSSystemMessageCell.h */, 34D1F0A61F867BFC0066283D /* OWSSystemMessageCell.m */, 34B6A906218B5240007C4606 /* TypingIndicatorCell.swift */, - 34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */, C364534F252449260045C478 /* VoiceMessageView.swift */, ); path = "Views & Cells"; @@ -3084,7 +3115,6 @@ isa = PBXGroup; children = ( B8B32044258C117C0020074B /* ContactsMigration.swift */, - C32A025925A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift */, C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */, C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */, C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */, @@ -3404,28 +3434,6 @@ path = Generated; sourceTree = ""; }; - C3C2A8632553B41A00C340D1 /* SessionProtocolKit */ = { - isa = PBXGroup; - children = ( - C3C2A8762553B42C00C340D1 /* Meta */, - B8CA010A25A293530091AF73 /* ClosedGroupRatchet.swift */, - B8CA010025A293260091AF73 /* ClosedGroupSenderKey.swift */, - B8CA011425A293800091AF73 /* Configuration.swift */, - B8CA011E25A2939F0091AF73 /* SharedSenderKeys.swift */, - B8CA014025A293EE0091AF73 /* Storage.swift */, - ); - path = SessionProtocolKit; - sourceTree = ""; - }; - C3C2A8762553B42C00C340D1 /* Meta */ = { - isa = PBXGroup; - children = ( - C3C2A8642553B41A00C340D1 /* SessionProtocolKit.h */, - C3C2A8652553B41A00C340D1 /* Info.plist */, - ); - path = Meta; - sourceTree = ""; - }; C3CA3AA0255CDA7000F4C6D4 /* Mnemonic */ = { isa = PBXGroup; children = ( @@ -3581,7 +3589,6 @@ C33FD9AC255A548A00E217F9 /* SignalUtilitiesKit */, C331FF1C2558F9D300070591 /* SessionUIKit */, C3C2A6F125539DE700C340D1 /* SessionMessagingKit */, - C3C2A8632553B41A00C340D1 /* SessionProtocolKit */, C3C2A5A0255385C100C340D1 /* SessionSnodeKit */, C3C2A67A255388CC00C340D1 /* SessionUtilitiesKit */, D221A08C169C9E5E00537ABF /* Frameworks */, @@ -3599,7 +3606,6 @@ C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */, C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */, C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */, - C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */, C331FF1B2558F9D300070591 /* SessionUIKit.framework */, C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */, ); @@ -3666,6 +3672,7 @@ C36096A525AD18D7008B62B2 /* Basic Chats */, C360969C25AD18BA008B62B2 /* Closed Groups */, C36096AE25AD1909008B62B2 /* Conversations */, + B835246C25C38AA20089A44F /* Conversations V2 */, C32C5D49256DD522003C73A2 /* Database */, C32B405424A961E1001117B5 /* Dependencies */, C360968E25AD16E8008B62B2 /* Home */, @@ -3850,14 +3857,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C3C2A85D2553B41A00C340D1 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - C3C2A8662553B41A00C340D1 /* SessionProtocolKit.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -3874,7 +3873,6 @@ ); dependencies = ( B8D64FC025BA784A0029CFC0 /* PBXTargetDependency */, - B8D64FC225BA784A0029CFC0 /* PBXTargetDependency */, B8D64FC425BA784A0029CFC0 /* PBXTargetDependency */, B8D64FC625BA784A0029CFC0 /* PBXTargetDependency */, C3D90A5525773A1A002C9DF5 /* PBXTargetDependency */, @@ -3898,7 +3896,6 @@ ); dependencies = ( B8D64FB425BA78270029CFC0 /* PBXTargetDependency */, - B8D64FB625BA78270029CFC0 /* PBXTargetDependency */, B8D64FB825BA78270029CFC0 /* PBXTargetDependency */, B8D64FBA25BA78270029CFC0 /* PBXTargetDependency */, C3D90A7125773A44002C9DF5 /* PBXTargetDependency */, @@ -4003,25 +4000,6 @@ productReference = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; productType = "com.apple.product-type.framework"; }; - C3C2A8612553B41A00C340D1 /* SessionProtocolKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = C3C2A86B2553B41A00C340D1 /* Build configuration list for PBXNativeTarget "SessionProtocolKit" */; - buildPhases = ( - 099772F07D67DC2A83009D2F /* [CP] Check Pods Manifest.lock */, - C3C2A85D2553B41A00C340D1 /* Headers */, - C3C2A85E2553B41A00C340D1 /* Sources */, - C3C2A85F2553B41A00C340D1 /* Frameworks */, - C3C2A8602553B41A00C340D1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SessionProtocolKit; - productName = SessionProtocolKit; - productReference = C3C2A8622553B41A00C340D1 /* SessionProtocolKit.framework */; - productType = "com.apple.product-type.framework"; - }; D221A088169C9E5E00537ABF /* Session */ = { isa = PBXNativeTarget; buildConfigurationList = D221A0BC169C9E5F00537ABF /* Build configuration list for PBXNativeTarget "Session" */; @@ -4042,7 +4020,6 @@ C3C2A5A5255385C100C340D1 /* PBXTargetDependency */, C3C2A67F255388CC00C340D1 /* PBXTargetDependency */, C3C2A6F625539DE700C340D1 /* PBXTargetDependency */, - C3C2A8682553B41A00C340D1 /* PBXTargetDependency */, C331FF212558F9D300070591 /* PBXTargetDependency */, C33FD9B1255A548A00E217F9 /* PBXTargetDependency */, ); @@ -4117,12 +4094,6 @@ LastSwiftMigration = 1210; ProvisioningStyle = Automatic; }; - C3C2A8612553B41A00C340D1 = { - CreatedOnToolsVersion = 12.1; - DevelopmentTeam = SUQ8J2PCT7; - LastSwiftMigration = 1220; - ProvisioningStyle = Automatic; - }; D221A088169C9E5E00537ABF = { DevelopmentTeam = SUQ8J2PCT7; LastSwiftMigration = 1020; @@ -4187,7 +4158,6 @@ C33FD9AA255A548A00E217F9 /* SignalUtilitiesKit */, C331FF1A2558F9D300070591 /* SessionUIKit */, C3C2A6EF25539DE700C340D1 /* SessionMessagingKit */, - C3C2A8612553B41A00C340D1 /* SessionProtocolKit */, C3C2A59E255385C100C340D1 /* SessionSnodeKit */, C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */, ); @@ -4253,13 +4223,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C3C2A8602553B41A00C340D1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D221A087169C9E5E00537ABF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -4346,28 +4309,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 099772F07D67DC2A83009D2F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-SessionProtocolKit-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 1460156AE01E0DB0949D61FE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -4759,7 +4700,6 @@ C38EF3F9255B6DF7007E1867 /* OWSLayerView.swift in Sources */, C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */, B8B3204E258C15C80020074B /* ContactsMigration.swift in Sources */, - C32A025A25A7FC55000ED5D4 /* ClosedGroupsV2Migration.swift in Sources */, C38EF3B9255B6DE7007E1867 /* ImageEditorPinchGestureRecognizer.swift in Sources */, C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */, C33FDC27255A581F00E217F9 /* YapDatabase+Promise.swift in Sources */, @@ -4967,6 +4907,7 @@ C352A349255781F400338F3E /* AttachmentDownloadJob.swift in Sources */, C352A30925574D8500338F3E /* Message+Destination.swift in Sources */, C300A5E72554B07300555489 /* ExpirationTimerUpdate.swift in Sources */, + B88A1AC725C90A4700E6D421 /* TypingIndicatorInteraction.swift in Sources */, C3D9E3C025676AD70040E4F3 /* TSAttachment.m in Sources */, C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */, C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */, @@ -5002,22 +4943,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C3C2A85E2553B41A00C340D1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B8CA010125A293260091AF73 /* ClosedGroupSenderKey.swift in Sources */, - B8CA011525A293800091AF73 /* Configuration.swift in Sources */, - B8CA011F25A2939F0091AF73 /* SharedSenderKeys.swift in Sources */, - B8CA010B25A293530091AF73 /* ClosedGroupRatchet.swift in Sources */, - B8CA014125A293EE0091AF73 /* Storage.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D221A085169C9E5E00537ABF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B8041AA725C90927003C2166 /* TypingIndicatorCellV2.swift in Sources */, B8CCF63723961D6D0091D419 /* NewPrivateChatVC.swift in Sources */, 34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */, 452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */, @@ -5037,6 +4967,7 @@ 34D1F0871F8678AA0066283D /* ConversationViewItem.m in Sources */, 451A13B11E13DED2000A50FD /* AppNotifications.swift in Sources */, 34D99CE4217509C2000AFB39 /* AppEnvironment.swift in Sources */, + C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */, 348570A820F67575004FF32B /* OWSMessageHeaderView.m in Sources */, 450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */, 34B6A907218B5241007C4606 /* TypingIndicatorCell.swift in Sources */, @@ -5047,6 +4978,7 @@ 34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */, EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */, 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */, + B83524A525C3BA4B0089A44F /* InfoMessageCell.swift in Sources */, 34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */, 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */, 451166C01FD86B98000739BA /* AccountManager.swift in Sources */, @@ -5059,7 +4991,7 @@ B886B4A92398BA1500211ABE /* QRCode.swift in Sources */, 3496955D219B605E00DCFE74 /* PhotoCollectionPickerController.swift in Sources */, 34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */, - 34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */, + 34A8B3512190A40E00218A25 /* MediaAlbumView.swift in Sources */, 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */, 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */, @@ -5071,6 +5003,7 @@ B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */, D221A09A169C9E5E00537ABF /* main.m in Sources */, 3496957221A301A100DCFE74 /* OWSBackup.m in Sources */, + B835247925C38D880089A44F /* MessageCell.swift in Sources */, C35D0DA125AE582D00B6BF49 /* MultiDeviceVC.swift in Sources */, B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */, 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */, @@ -5089,9 +5022,11 @@ 45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */, 34D1F0881F8678AA0066283D /* ConversationViewLayout.m in Sources */, B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */, + B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */, 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */, 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */, 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */, + C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */, 4542DF54208D40AC007B4E76 /* LoadingViewController.swift in Sources */, 34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */, 34D1F0B71F87F8850066283D /* OWSGenericAttachmentView.m in Sources */, @@ -5099,9 +5034,9 @@ 341341EF2187467A00192D59 /* ConversationViewModel.m in Sources */, 4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */, C331FFF32558FF0300070591 /* PathStatusView.swift in Sources */, + B8569AD325CBA13D00DBA3DB /* MediaTextOverlayView.swift in Sources */, 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */, 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */, - 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */, 34EA69402194933900702471 /* MediaDownloadView.swift in Sources */, B8544E3323D50E4900299F14 /* AppearanceUtilities.swift in Sources */, @@ -5112,7 +5047,7 @@ 45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */, C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */, B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */, - B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */, + B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */, 34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */, 34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */, 34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */, @@ -5125,18 +5060,22 @@ 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */, 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */, 4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */, + B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */, 45F32C232057297A00A300D5 /* MediaPageViewController.swift in Sources */, B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */, 34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */, 4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */, + C328253025CA55370062D0A7 /* ContextMenuWindow.swift in Sources */, 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */, 34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */, B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */, 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */, 34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */, + C328250F25CA06020062D0A7 /* VoiceMessageViewV2.swift in Sources */, B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */, - 3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */, + 3488F9362191CC4000E524CC /* MediaView.swift in Sources */, 45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */, + B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */, B8A14D702589CE9000E70D57 /* KeyPairMigrationSuccessSheet.swift in Sources */, 3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */, 34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */, @@ -5148,12 +5087,13 @@ 34DBF004206BD5A500025978 /* OWSBubbleView.m in Sources */, 3496957021A301A100DCFE74 /* OWSBackupIO.m in Sources */, 34AC0A23211C829F00997B47 /* OWSLabel.m in Sources */, + B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */, 34EA69422194DE8000702471 /* MediaUploadView.swift in Sources */, + B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */, 76EB054018170B33006006FC /* AppDelegate.m in Sources */, 34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */, 340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */, 4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */, - 340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */, C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */, C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */, B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */, @@ -5161,22 +5101,28 @@ C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */, 4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */, 340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */, + B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */, B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */, B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */, B83786802586D296003CE78E /* KeyPairMigrationSheet.swift in Sources */, C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */, + B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */, 340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */, 340FC8B0204DAC8D007AEB0F /* AddToBlockListViewController.m in Sources */, 3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */, B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */, + B897621C25D201F7004F83B2 /* ConversationVC+ScrollToBottomButton.swift in Sources */, 346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */, 45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */, 34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */, + C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */, B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */, 34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */, + C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */, 3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */, B90418E6183E9DD40038554A /* DateUtil.m in Sources */, C33100092558FF6D00070591 /* UserCell.swift in Sources */, + B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */, C3645350252449260045C478 /* VoiceMessageView.swift in Sources */, 3496956F21A301A100DCFE74 /* OWSBackupLazyRestore.swift in Sources */, ); @@ -5200,11 +5146,6 @@ target = C3C2A6EF25539DE700C340D1 /* SessionMessagingKit */; targetProxy = B8D64FB325BA78270029CFC0 /* PBXContainerItemProxy */; }; - B8D64FB625BA78270029CFC0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C3C2A8612553B41A00C340D1 /* SessionProtocolKit */; - targetProxy = B8D64FB525BA78270029CFC0 /* PBXContainerItemProxy */; - }; B8D64FB825BA78270029CFC0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C3C2A59E255385C100C340D1 /* SessionSnodeKit */; @@ -5220,11 +5161,6 @@ target = C3C2A6EF25539DE700C340D1 /* SessionMessagingKit */; targetProxy = B8D64FBF25BA784A0029CFC0 /* PBXContainerItemProxy */; }; - B8D64FC225BA784A0029CFC0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C3C2A8612553B41A00C340D1 /* SessionProtocolKit */; - targetProxy = B8D64FC125BA784A0029CFC0 /* PBXContainerItemProxy */; - }; B8D64FC425BA784A0029CFC0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C3C2A59E255385C100C340D1 /* SessionSnodeKit */; @@ -5260,11 +5196,6 @@ target = C3C2A6EF25539DE700C340D1 /* SessionMessagingKit */; targetProxy = C3C2A6F525539DE700C340D1 /* PBXContainerItemProxy */; }; - C3C2A8682553B41A00C340D1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C3C2A8612553B41A00C340D1 /* SessionProtocolKit */; - targetProxy = C3C2A8672553B41A00C340D1 /* PBXContainerItemProxy */; - }; C3D90A5525773A1A002C9DF5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C331FF1A2558F9D300070591 /* SessionUIKit */; @@ -6257,136 +6188,6 @@ }; name = "App Store Release"; }; - C3C2A86C2553B41A00C340D1 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AEA8083C060FF9BAFF6E0C9F /* Pods-SessionProtocolKit.debug.xcconfig */; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = SUQ8J2PCT7; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - INFOPLIST_FILE = SessionProtocolKit/Meta/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.SessionProtocolKit"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - C3C2A86D2553B41A00C340D1 /* App Store Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 174BD0AE74771D02DAC2B7A9 /* Pods-SessionProtocolKit.app store release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - APPLICATION_EXTENSION_API_ONLY = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = SUQ8J2PCT7; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = SessionProtocolKit/Meta/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.SessionProtocolKit"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = "App Store Release"; - }; D221A0BA169C9E5F00537ABF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6736,15 +6537,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = "App Store Release"; }; - C3C2A86B2553B41A00C340D1 /* Build configuration list for PBXNativeTarget "SessionProtocolKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C3C2A86C2553B41A00C340D1 /* Debug */, - C3C2A86D2553B41A00C340D1 /* App Store Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = "App Store Release"; - }; D221A083169C9E5E00537ABF /* Build configuration list for PBXProject "Session" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Session/Basic Chats/NewPrivateChatVC.swift b/Session/Basic Chats/NewPrivateChatVC.swift index 4f8dcfabc..3e5e8c96c 100644 --- a/Session/Basic Chats/NewPrivateChatVC.swift +++ b/Session/Basic Chats/NewPrivateChatVC.swift @@ -183,7 +183,7 @@ private final class EnterPublicKeyVC : UIViewController { view.backgroundColor = .clear // Explanation label let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) explanationLabel.font = .systemFont(ofSize: Values.verySmallFontSize) explanationLabel.text = NSLocalizedString("vc_enter_public_key_explanation", comment: "") explanationLabel.numberOfLines = 0 diff --git a/Session/Closed Groups/EditClosedGroupVC.swift b/Session/Closed Groups/EditClosedGroupVC.swift index ec89fa71b..15ce000d1 100644 --- a/Session/Closed Groups/EditClosedGroupVC.swift +++ b/Session/Closed Groups/EditClosedGroupVC.swift @@ -100,7 +100,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega let hasContactsToAdd = !Set(ContactUtilities.getAllContacts()).subtracting(self.members).isEmpty if (!hasContactsToAdd) { addMembersButton.isUserInteractionEnabled = false - let disabledColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + let disabledColor = Colors.text.withAlphaComponent(Values.mediumOpacity) addMembersButton.layer.borderColor = disabledColor.cgColor addMembersButton.setTitleColor(disabledColor, for: UIControl.State.normal) } @@ -238,7 +238,7 @@ final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelega self.members = members.sorted { getDisplayName(for: $0) < getDisplayName(for: $1) } let hasContactsToAdd = !Set(ContactUtilities.getAllContacts()).subtracting(self.members).isEmpty self.addMembersButton.isUserInteractionEnabled = hasContactsToAdd - let color = hasContactsToAdd ? Colors.accent : Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + let color = hasContactsToAdd ? Colors.accent : Colors.text.withAlphaComponent(Values.mediumOpacity) self.addMembersButton.layer.borderColor = color.cgColor self.addMembersButton.setTitleColor(color, for: UIControl.State.normal) } diff --git a/Session/Closed Groups/GroupMembersVC.swift b/Session/Closed Groups/GroupMembersVC.swift index 9f0542c03..cb279dfbb 100644 --- a/Session/Closed Groups/GroupMembersVC.swift +++ b/Session/Closed Groups/GroupMembersVC.swift @@ -38,7 +38,7 @@ final class GroupMembersVC : BaseVC, UITableViewDataSource { setNavBarTitle("Group Members") // Set up explanation label let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.text = "The ability to add members to a closed group is coming soon." explanationLabel.numberOfLines = 0 diff --git a/Session/Conversations V2/Context Menu/ContextMenuVC+Action.swift b/Session/Conversations V2/Context Menu/ContextMenuVC+Action.swift new file mode 100644 index 000000000..901e5e50c --- /dev/null +++ b/Session/Conversations V2/Context Menu/ContextMenuVC+Action.swift @@ -0,0 +1,82 @@ + +extension ContextMenuVC { + + struct Action { + let icon: UIImage + let title: String + let work: () -> Void + + static func reply(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Reply" + return Action(icon: UIImage(named: "ic_reply")!, title: title) { delegate.reply(viewItem) } + } + + static func copy(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Copy" + return Action(icon: UIImage(named: "ic_copy")!, title: title) { delegate.copy(viewItem) } + } + + static func copySessionID(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Copy Session ID" + return Action(icon: UIImage(named: "ic_copy")!, title: title) { delegate.copySessionID(viewItem) } + } + + static func delete(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Delete" + return Action(icon: UIImage(named: "ic_trash")!, title: title) { delegate.delete(viewItem) } + } + + static func save(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Save" + return Action(icon: UIImage(named: "ic_download")!, title: title) { delegate.save(viewItem) } + } + + static func ban(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate) -> Action { + let title = "Ban User" + return Action(icon: UIImage(named: "ic_block")!, title: title) { delegate.ban(viewItem) } + } + } + + static func actions(for viewItem: ConversationViewItem, delegate: ContextMenuActionDelegate) -> [Action] { + func isReplyingAllowed() -> Bool { + guard let message = viewItem.interaction as? TSOutgoingMessage else { return true } + switch message.messageState { + case .failed, .sending: return false + default: return true + } + } + switch viewItem.messageCellType { + case .textOnlyMessage: + var result: [Action] = [] + if isReplyingAllowed() { result.append(Action.reply(viewItem, delegate)) } + result.append(Action.copy(viewItem, delegate)) + let isGroup = viewItem.isGroupThread + if isGroup && viewItem.interaction is TSIncomingMessage { result.append(Action.copySessionID(viewItem, delegate)) } + if !isGroup || viewItem.userCanDeleteGroupMessage { result.append(Action.delete(viewItem, delegate)) } + if isGroup && viewItem.interaction is TSIncomingMessage && viewItem.userHasModerationPermission { result.append(Action.ban(viewItem, delegate)) } + return result + case .mediaMessage, .audio, .genericAttachment: + var result: [Action] = [] + if isReplyingAllowed() { result.append(Action.reply(viewItem, delegate)) } + if viewItem.canCopyMedia() { result.append(Action.copy(viewItem, delegate)) } + if viewItem.canSaveMedia() { result.append(Action.save(viewItem, delegate)) } + let isGroup = viewItem.isGroupThread + if isGroup && viewItem.interaction is TSIncomingMessage { result.append(Action.copySessionID(viewItem, delegate)) } + if !isGroup || viewItem.userCanDeleteGroupMessage { result.append(Action.delete(viewItem, delegate)) } + if isGroup && viewItem.interaction is TSIncomingMessage && viewItem.userHasModerationPermission { result.append(Action.ban(viewItem, delegate)) } + return result + default: return [] + } + } +} + +// MARK: Delegate +protocol ContextMenuActionDelegate { + + func reply(_ viewItem: ConversationViewItem) + func copy(_ viewItem: ConversationViewItem) + func copySessionID(_ viewItem: ConversationViewItem) + func delete(_ viewItem: ConversationViewItem) + func save(_ viewItem: ConversationViewItem) + func ban(_ viewItem: ConversationViewItem) +} diff --git a/Session/Conversations V2/Context Menu/ContextMenuVC+ActionView.swift b/Session/Conversations V2/Context Menu/ContextMenuVC+ActionView.swift new file mode 100644 index 000000000..0f0e99ffc --- /dev/null +++ b/Session/Conversations V2/Context Menu/ContextMenuVC+ActionView.swift @@ -0,0 +1,62 @@ + +extension ContextMenuVC { + + final class ActionView : UIView { + private let action: Action + private let dismiss: () -> Void + + // MARK: Settings + private static let iconSize: CGFloat = 16 + private static let iconImageViewSize: CGFloat = 24 + + // MARK: Lifecycle + init(for action: Action, dismiss: @escaping () -> Void) { + self.action = action + self.dismiss = dismiss + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(for:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(for:) instead.") + } + + private func setUpViewHierarchy() { + // Icon + let iconSize = ActionView.iconSize + let iconImageView = UIImageView(image: action.icon.resizedImage(to: CGSize(width: iconSize, height: iconSize))!.withTint(Colors.text)) + let iconImageViewSize = ActionView.iconImageViewSize + iconImageView.set(.width, to: iconImageViewSize) + iconImageView.set(.height, to: iconImageViewSize) + iconImageView.contentMode = .center + // Title + let titleLabel = UILabel() + titleLabel.text = action.title + titleLabel.textColor = Colors.text + titleLabel.font = .systemFont(ofSize: Values.mediumFontSize) + // Stack view + let stackView = UIStackView(arrangedSubviews: [ iconImageView, titleLabel ]) + stackView.axis = .horizontal + stackView.spacing = Values.smallSpacing + stackView.alignment = .center + stackView.isLayoutMarginsRelativeArrangement = true + let smallSpacing = Values.smallSpacing + stackView.layoutMargins = UIEdgeInsets(top: smallSpacing, leading: smallSpacing, bottom: smallSpacing, trailing: Values.mediumSpacing) + addSubview(stackView) + stackView.pin(to: self) + // Tap gesture recognizer + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + addGestureRecognizer(tapGestureRecognizer) + } + + // MARK: Interaction + @objc private func handleTap() { + action.work() + dismiss() + } + } +} diff --git a/Session/Conversations V2/Context Menu/ContextMenuVC.swift b/Session/Conversations V2/Context Menu/ContextMenuVC.swift new file mode 100644 index 000000000..76469c115 --- /dev/null +++ b/Session/Conversations V2/Context Menu/ContextMenuVC.swift @@ -0,0 +1,117 @@ + +final class ContextMenuVC : UIViewController { + private let snapshot: UIView + private let viewItem: ConversationViewItem + private let frame: CGRect + private let delegate: ContextMenuActionDelegate + private let dismiss: () -> Void + + // MARK: UI Components + private lazy var blurView = UIVisualEffectView(effect: nil) + + private lazy var menuView: UIView = { + let result = UIView() + result.layer.shadowColor = UIColor.black.cgColor + result.layer.shadowOffset = CGSize.zero + result.layer.shadowOpacity = 0.4 + result.layer.shadowRadius = 4 + return result + }() + + // MARK: Settings + private static let actionViewHeight: CGFloat = 40 + + // MARK: Lifecycle + init(snapshot: UIView, viewItem: ConversationViewItem, frame: CGRect, delegate: ContextMenuActionDelegate, dismiss: @escaping () -> Void) { + self.snapshot = snapshot + self.viewItem = viewItem + self.frame = frame + self.delegate = delegate + self.dismiss = dismiss + super.init(nibName: nil, bundle: nil) + } + + override init(nibName: String?, bundle: Bundle?) { + preconditionFailure("Use init(snapshot:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(coder:) instead.") + } + + override func viewDidLoad() { + super.viewDidLoad() + // Background color + view.backgroundColor = .clear + // Blur + view.addSubview(blurView) + blurView.pin(to: view) + // Snapshot + snapshot.layer.shadowColor = UIColor.black.cgColor + snapshot.layer.shadowOffset = CGSize.zero + snapshot.layer.shadowOpacity = 0.4 + snapshot.layer.shadowRadius = 4 + view.addSubview(snapshot) + snapshot.pin(.left, to: .left, of: view, withInset: frame.origin.x) + snapshot.pin(.top, to: .top, of: view, withInset: frame.origin.y) + snapshot.set(.width, to: frame.width) + snapshot.set(.height, to: frame.height) + // Menu + let menuBackgroundView = UIView() + menuBackgroundView.backgroundColor = Colors.receivedMessageBackground + menuBackgroundView.layer.cornerRadius = Values.messageBubbleCornerRadius + menuBackgroundView.layer.masksToBounds = true + menuView.addSubview(menuBackgroundView) + menuBackgroundView.pin(to: menuView) + let actionViews = ContextMenuVC.actions(for: viewItem, delegate: delegate).map { ActionView(for: $0, dismiss: snDismiss) } + let menuStackView = UIStackView(arrangedSubviews: actionViews) + menuStackView.axis = .vertical + menuView.addSubview(menuStackView) + menuStackView.pin(to: menuView) + view.addSubview(menuView) + let menuHeight = CGFloat(actionViews.count) * ContextMenuVC.actionViewHeight + let spacing = Values.smallSpacing + let margin = max(UIApplication.shared.keyWindow!.safeAreaInsets.bottom, Values.mediumSpacing) + if frame.maxY + spacing + menuHeight > UIScreen.main.bounds.height - margin { + menuView.pin(.bottom, to: .top, of: snapshot, withInset: -spacing) + } else { + menuView.pin(.top, to: .bottom, of: snapshot, withInset: spacing) + } + switch viewItem.interaction.interactionType() { + case .outgoingMessage: menuView.pin(.right, to: .right, of: snapshot) + case .incomingMessage: menuView.pin(.left, to: .left, of: snapshot) + default: break // Should never occur + } + // Tap gesture + let mainTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + view.addGestureRecognizer(mainTapGestureRecognizer) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + UIView.animate(withDuration: 0.25) { + self.blurView.effect = UIBlurEffect(style: .regular) + self.menuView.alpha = 1 + } + } + + // MARK: Updating + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + menuView.layer.shadowPath = UIBezierPath(roundedRect: menuView.bounds, cornerRadius: Values.messageBubbleCornerRadius).cgPath + } + + // MARK: Interaction + @objc private func handleTap() { + snDismiss() + } + + func snDismiss() { + UIView.animate(withDuration: 0.25, animations: { + self.blurView.effect = nil + self.menuView.alpha = 0 + }, completion: { _ in + self.dismiss() + }) + } +} diff --git a/Session/Conversations V2/Context Menu/ContextMenuWindow.swift b/Session/Conversations V2/Context Menu/ContextMenuWindow.swift new file mode 100644 index 000000000..9cd7fe4ff --- /dev/null +++ b/Session/Conversations V2/Context Menu/ContextMenuWindow.swift @@ -0,0 +1,28 @@ + +final class ContextMenuWindow : UIWindow { + + override var windowLevel: UIWindow.Level { + get { return UIWindow.Level(rawValue: CGFloat.greatestFiniteMagnitude - 1) } + set { /* Do nothing */ } + } + + override init(frame: CGRect) { + super.init(frame: frame) + initialize() + } + + @available(iOS 13.0, *) + override init(windowScene: UIWindowScene) { + super.init(windowScene: windowScene) + initialize() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + initialize() + } + + private func initialize() { + backgroundColor = .clear + } +} diff --git a/Session/Conversations V2/ConversationVC+Interaction.swift b/Session/Conversations V2/ConversationVC+Interaction.swift new file mode 100644 index 000000000..c9e7afb8b --- /dev/null +++ b/Session/Conversations V2/ConversationVC+Interaction.swift @@ -0,0 +1,192 @@ + +extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuActionDelegate, ScrollToBottomButtonDelegate { + + @objc func openSettings() { + let settingsVC = OWSConversationSettingsViewController() + settingsVC.configure(with: thread, uiDatabaseConnection: OWSPrimaryStorage.shared().uiDatabaseConnection) + navigationController!.pushViewController(settingsVC, animated: true, completion: nil) + } + + func handleCameraButtonTapped() { + // TODO: Implement + } + + func handleLibraryButtonTapped() { + // TODO: Implement + } + + func handleGIFButtonTapped() { + // TODO: Implement + } + + func handleDocumentButtonTapped() { + // TODO: Implement + } + + func handleSendButtonTapped() { + // TODO: Attachments + let text = snInputView.text.trimmingCharacters(in: .whitespacesAndNewlines) + let thread = self.thread + // TODO: Blocking + guard !text.isEmpty else { return } + let message = VisibleMessage() + message.sentTimestamp = NSDate.millisecondTimestamp() + message.text = text + // TODO: Quotes + // TODO: Link previews + let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread) + viewModel.appendUnsavedOutgoingTextMessage(tsMessage) + Storage.shared.write(with: { transaction in + // TODO: Link previews + }, completion: { [weak self] in + // TODO: Link previews + Storage.shared.write { transaction in + tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction) + } + Storage.shared.write { transaction in + MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction) + } + // TODO: Sent handling + self?.snInputView.text = "" + // TODO: Reset mentions + }) + } + + func handleViewItemLongPressed(_ viewItem: ConversationViewItem) { + guard let index = viewItems.firstIndex(where: { $0 === viewItem }), + let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell, + let snapshot = cell.bubbleView.snapshotView(afterScreenUpdates: false), contextMenuWindow == nil, + !ContextMenuVC.actions(for: viewItem, delegate: self).isEmpty else { return } + UIImpactFeedbackGenerator(style: .heavy).impactOccurred() + let frame = cell.convert(cell.bubbleView.frame, to: UIApplication.shared.keyWindow!) + let window = ContextMenuWindow() + let contextMenuVC = ContextMenuVC(snapshot: snapshot, viewItem: viewItem, frame: frame, delegate: self) { + window.isHidden = true + self.contextMenuVC = nil + self.contextMenuWindow = nil + } + self.contextMenuVC = contextMenuVC + contextMenuWindow = window + window.rootViewController = contextMenuVC + window.makeKeyAndVisible() + window.backgroundColor = .clear + } + + func handleViewItemTapped(_ viewItem: ConversationViewItem, gestureRecognizer: UITapGestureRecognizer) { + switch viewItem.messageCellType { + case .audio: playOrPauseAudio(for: viewItem) + case .mediaMessage: + guard let index = viewItems.firstIndex(where: { $0 === viewItem }), + let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell, let albumView = cell.albumView else { return } + let locationInCell = gestureRecognizer.location(in: cell) + if let overlayView = cell.mediaTextOverlayView { + let locationInOverlayView = cell.convert(locationInCell, to: overlayView) + if let readMoreButton = overlayView.readMoreButton, readMoreButton.frame.contains(locationInOverlayView) { + return showFullText(viewItem) // FIXME: Bit of a hack to do it this way + } + } + let locationInAlbumView = cell.convert(locationInCell, to: albumView) + guard let mediaView = albumView.mediaView(forLocation: locationInAlbumView) else { return } + if albumView.isMoreItemsView(mediaView: mediaView) && viewItem.mediaAlbumHasFailedAttachment() { + // TODO: Tapped a failed incoming attachment + } + let attachment = mediaView.attachment + if let pointer = attachment as? TSAttachmentPointer { + if pointer.state == .failed { + // TODO: Tapped a failed incoming attachment + } + } + guard let stream = attachment as? TSAttachmentStream else { return } + let gallery = MediaGallery(thread: thread, options: [ .sliderEnabled, .showAllMediaButton ]) + gallery.presentDetailView(fromViewController: self, mediaAttachment: stream, replacingView: mediaView) + case .genericAttachment: + guard let url = viewItem.attachmentStream?.originalMediaURL else { return } + let shareVC = UIActivityViewController(activityItems: [ url ], applicationActivities: nil) + navigationController!.present(shareVC, animated: true, completion: nil) + default: break + } + } + + func handleViewItemDoubleTapped(_ viewItem: ConversationViewItem) { + switch viewItem.messageCellType { + case .audio: speedUpAudio(for: viewItem) + default: break + } + } + + func showFullText(_ viewItem: ConversationViewItem) { + let longMessageVC = LongTextViewController(viewItem: viewItem) + navigationController!.pushViewController(longMessageVC, animated: true) + } + + func playOrPauseAudio(for viewItem: ConversationViewItem) { + guard let attachment = viewItem.attachmentStream else { return } + let fileManager = FileManager.default + guard let path = attachment.originalFilePath, fileManager.fileExists(atPath: path), + let url = attachment.originalMediaURL else { return } + if let audioPlayer = audioPlayer { + if let owner = audioPlayer.owner as? ConversationViewItem, owner === viewItem { + audioPlayer.playbackRate = 1 + audioPlayer.togglePlayState() + return + } else { + audioPlayer.stop() + self.audioPlayer = nil + } + } + let audioPlayer = OWSAudioPlayer(mediaUrl: url, audioBehavior: .audioMessagePlayback, delegate: viewItem) + self.audioPlayer = audioPlayer + audioPlayer.owner = viewItem + audioPlayer.play() + audioPlayer.setCurrentTime(Double(viewItem.audioProgressSeconds)) + } + + func speedUpAudio(for viewItem: ConversationViewItem) { + guard let audioPlayer = audioPlayer, let owner = audioPlayer.owner as? ConversationViewItem, owner === viewItem, audioPlayer.isPlaying else { return } + audioPlayer.playbackRate = 1.5 + viewItem.lastAudioMessageView?.showSpeedUpLabel() + } + + func reply(_ viewItem: ConversationViewItem) { + // TODO: Implement + } + + func copy(_ viewItem: ConversationViewItem) { + if viewItem.canCopyMedia() { + viewItem.copyMediaAction() + } else { + viewItem.copyTextAction() + } + } + + func copySessionID(_ viewItem: ConversationViewItem) { + guard let message = viewItem.interaction as? TSIncomingMessage else { return } + UIPasteboard.general.string = message.authorId + } + + func delete(_ viewItem: ConversationViewItem) { + viewItem.deleteAction() + } + + func save(_ viewItem: ConversationViewItem) { + guard viewItem.canSaveMedia() else { return } + viewItem.saveMediaAction() + } + + func ban(_ viewItem: ConversationViewItem) { + guard let message = viewItem.interaction as? TSIncomingMessage, message.isOpenGroupMessage else { return } + let alert = UIAlertController(title: "Ban This User?", message: nil, preferredStyle: .alert) + let threadID = thread.uniqueId! + alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in + guard let openGroup = Storage.shared.getOpenGroup(for: threadID) else { return } + let publicKey = message.authorId + OpenGroupAPI.ban(publicKey, from: openGroup.server).retainUntilComplete() + })) + alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil)) + present(alert, animated: true, completion: nil) + } + + func handleScrollToBottomButtonTapped() { + scrollToBottom(isAnimated: true) + } +} diff --git a/Session/Conversations V2/ConversationVC+ScrollToBottomButton.swift b/Session/Conversations V2/ConversationVC+ScrollToBottomButton.swift new file mode 100644 index 000000000..aa754d540 --- /dev/null +++ b/Session/Conversations V2/ConversationVC+ScrollToBottomButton.swift @@ -0,0 +1,70 @@ + +extension ConversationVC { + + final class ScrollToBottomButton : UIView { + private let delegate: ScrollToBottomButtonDelegate + + // MARK: Settings + private static let size: CGFloat = 40 + private static let iconSize: CGFloat = 16 + + // MARK: Lifecycle + init(delegate: ScrollToBottomButtonDelegate) { + self.delegate = delegate + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(delegate:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(delegate:) instead.") + } + + private func setUpViewHierarchy() { + // Background & blur + let backgroundView = UIView() + backgroundView.backgroundColor = isLightMode ? .white : .black + backgroundView.alpha = Values.lowOpacity + addSubview(backgroundView) + backgroundView.pin(to: self) + let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .regular)) + addSubview(blurView) + blurView.pin(to: self) + // Size & shape + let size = ScrollToBottomButton.size + set(.width, to: size) + set(.height, to: size) + layer.cornerRadius = size / 2 + layer.masksToBounds = true + // Border + layer.borderWidth = 1 + let borderColor = (isLightMode ? UIColor.black : UIColor.white).withAlphaComponent(Values.veryLowOpacity) + layer.borderColor = borderColor.cgColor + // Icon + let tint = isLightMode ? UIColor.black : UIColor.white + let icon = UIImage(named: "ic_chevron_down")!.withTint(tint) + let iconImageView = UIImageView(image: icon) + iconImageView.set(.width, to: ScrollToBottomButton.iconSize) + iconImageView.set(.height, to: ScrollToBottomButton.iconSize) + iconImageView.contentMode = .scaleAspectFit + addSubview(iconImageView) + iconImageView.center(in: self) + // Gesture recognizer + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + addGestureRecognizer(tapGestureRecognizer) + } + + // MARK: Interaction + @objc private func handleTap() { + delegate.handleScrollToBottomButtonTapped() + } + } +} + +protocol ScrollToBottomButtonDelegate { + + func handleScrollToBottomButtonTapped() +} diff --git a/Session/Conversations V2/ConversationVC.swift b/Session/Conversations V2/ConversationVC.swift new file mode 100644 index 000000000..c594651b0 --- /dev/null +++ b/Session/Conversations V2/ConversationVC.swift @@ -0,0 +1,344 @@ + +// TODO +// • Tapping replies +// • Mentions +// • Remaining send logic +// • Paging +// • Blocking +// • Subtitle +// • Resending failed messages +// • Linkification +// • Link previews + +final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewDataSource, UITableViewDelegate { + let thread: TSThread + private let focusedMessageID: String? + var audioPlayer: OWSAudioPlayer? + private var didConstrainScrollButton = false + // Context menu + var contextMenuWindow: ContextMenuWindow? + var contextMenuVC: ContextMenuVC? + // Scrolling & paging + private var isUserScrolling = false { didSet { autoLoadMoreIfNeeded() } } + private var hasPerformedInitialScroll = false + private var isLoadingMore = false + private var contentOffsetYBeforeUpdate: CGFloat? + private var contentHeightBeforeUpdate: CGFloat? + + + private var dbConnection: YapDatabaseConnection { OWSPrimaryStorage.shared().uiDatabaseConnection } + var viewItems: [ConversationViewItem] { viewModel.viewState.viewItems } + func conversationStyle() -> ConversationStyle { return ConversationStyle(thread: thread) } + override var inputAccessoryView: UIView? { snInputView } + override var canBecomeFirstResponder: Bool { true } + + private var lastPageTop: CGFloat { + let bottomInset = -messagesTableView.adjustedContentInset.bottom - ConversationVC.bottomInset + let tableViewUnobscuredHeight = messagesTableView.bounds.height + bottomInset + return messagesTableView.contentSize.height - tableViewUnobscuredHeight + } + + lazy var viewModel = ConversationViewModel(thread: thread, focusMessageIdOnOpen: focusedMessageID, delegate: self) + + private lazy var mediaCache: NSCache = { + let result = NSCache() + result.countLimit = 24 + return result + }() + + // MARK: UI Components + lazy var messagesTableView: UITableView = { + let result = UITableView() + result.dataSource = self + result.delegate = self + result.register(VisibleMessageCell.self, forCellReuseIdentifier: VisibleMessageCell.identifier) + result.register(InfoMessageCell.self, forCellReuseIdentifier: InfoMessageCell.identifier) + result.register(TypingIndicatorCellV2.self, forCellReuseIdentifier: TypingIndicatorCellV2.identifier) + result.separatorStyle = .none + result.backgroundColor = .clear + result.contentInset = UIEdgeInsets(top: 0, leading: 0, bottom: ConversationVC.bottomInset, trailing: 0) + result.showsVerticalScrollIndicator = false + result.contentInsetAdjustmentBehavior = .never + result.keyboardDismissMode = .interactive + return result + }() + + lazy var snInputView = InputView(delegate: self) + + private lazy var scrollButton = ScrollToBottomButton(delegate: self) + + // MARK: Settings + private static let bottomInset = Values.mediumSpacing + private static let loadMoreThreshold: CGFloat = 120 + /// The button will be fully visible once the user has scrolled this amount from the bottom of the table view. + private static let scrollButtonFullVisibilityThreshold: CGFloat = 80 + /// The button will be invisible until the user has scrolled at least this amount from the bottom of the table view. + private static let scrollButtonNoVisibilityThreshold: CGFloat = 20 + + // MARK: Lifecycle + init(thread: TSThread, focusedMessageID: String? = nil) { + self.thread = thread + self.focusedMessageID = focusedMessageID + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(thread:) instead.") + } + + override func viewDidLoad() { + super.viewDidLoad() + // Gradient + setUpGradientBackground() + // Nav bar + setUpNavBarStyle() + setNavBarTitle(getTitle()) + updateNavBarButtons() + // Constraints + view.addSubview(messagesTableView) + messagesTableView.pin(to: view) + view.addSubview(scrollButton) + scrollButton.pin(.right, to: .right, of: view, withInset: -Values.mediumSpacing) + // Notifications + let notificationCenter = NotificationCenter.default + notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) + notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) + notificationCenter.addObserver(self, selector: #selector(handleAudioDidFinishPlayingNotification(_:)), name: .SNAudioDidFinishPlaying, object: nil) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + if !hasPerformedInitialScroll { + scrollToBottom(isAnimated: false) + hasPerformedInitialScroll = true + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + guard let lastSortID = viewItems.last?.interaction.sortId else { return } + OWSReadReceiptManager.shared().markAsReadLocally(beforeSortId: lastSortID, thread: thread) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + mediaCache.removeAllObjects() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: Table View Data Source + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewItems.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let viewItem = viewItems[indexPath.row] + let cell = tableView.dequeueReusableCell(withIdentifier: MessageCell.getCellType(for: viewItem).identifier) as! MessageCell + cell.delegate = self + cell.viewItem = viewItem + return cell + } + + // MARK: Updating + private func updateNavBarButtons() { + let rightBarButtonItem: UIBarButtonItem + if thread is TSContactThread { + let size = Values.verySmallProfilePictureSize + let profilePictureView = ProfilePictureView() + profilePictureView.accessibilityLabel = "Settings button" + profilePictureView.size = size + profilePictureView.update(for: thread) + profilePictureView.set(.width, to: size) + profilePictureView.set(.height, to: size) + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings)) + profilePictureView.addGestureRecognizer(tapGestureRecognizer) + rightBarButtonItem = UIBarButtonItem(customView: profilePictureView) + } else { + rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "Gear"), style: .plain, target: self, action: #selector(openSettings)) + } + rightBarButtonItem.accessibilityLabel = "Settings button" + rightBarButtonItem.isAccessibilityElement = true + navigationItem.rightBarButtonItem = rightBarButtonItem + } + + @objc private func handleKeyboardWillChangeFrameNotification(_ notification: Notification) { + guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return } + if !didConstrainScrollButton { + // Bit of a hack to do this here, but it works out. + scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -(newHeight + Values.mediumSpacing)) + didConstrainScrollButton = true + } + UIView.animate(withDuration: 0.25) { + self.messagesTableView.contentInset = UIEdgeInsets(top: 0, leading: 0, bottom: newHeight + ConversationVC.bottomInset, trailing: 0) + self.scrollButton.alpha = 0 + } + } + + @objc private func handleKeyboardWillHideNotification(_ notification: Notification) { + UIView.animate(withDuration: 0.25) { + self.messagesTableView.contentInset = UIEdgeInsets(top: 0, leading: 0, bottom: ConversationVC.bottomInset, trailing: 0) + self.scrollButton.alpha = self.getScrollButtonOpacity() + } + } + + func conversationViewModelWillUpdate() { + + } + + func conversationViewModelDidUpdate(_ conversationUpdate: ConversationUpdate) { + guard self.isViewLoaded else { return } + // TODO: Reload the thread if it's a group thread? + let updateType = conversationUpdate.conversationUpdateType + guard updateType != .minor else { return } // No view items were affected + if updateType == .reload { + return messagesTableView.reloadData() + } + var shouldScrollToBottom = false + let shouldAnimate = conversationUpdate.shouldAnimateUpdates + let batchUpdates: () -> Void = { + for update in conversationUpdate.updateItems! { + switch update.updateItemType { + case .delete: + self.messagesTableView.deleteRows(at: [ IndexPath(row: Int(update.oldIndex), section: 0) ], with: .fade) + case .insert: + // Perform inserts before updates + self.messagesTableView.insertRows(at: [ IndexPath(row: Int(update.newIndex), section: 0) ], with: .fade) + let viewItem = update.viewItem + if viewItem?.interaction is TSOutgoingMessage { + shouldScrollToBottom = true + } + case .update: + self.messagesTableView.reloadRows(at: [ IndexPath(row: Int(update.oldIndex), section: 0) ], with: .fade) + default: preconditionFailure() + } + } + } + let batchUpdatesCompletion: (Bool) -> Void = { isFinished in + // TODO: Update last visible sort ID? + if shouldScrollToBottom { + self.scrollToBottom(isAnimated: true) + } + // TODO: Update last known distance from bottom + } + if shouldAnimate { + messagesTableView.performBatchUpdates(batchUpdates, completion: batchUpdatesCompletion) + } else { + // HACK: We use `UIView.animateWithDuration:0` rather than `UIView.performWithAnimation` to work around a + // UIKit Crash like: + // + // *** Assertion failure in -[ConversationViewLayout prepareForCollectionViewUpdates:], + // /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.7.47/UICollectionViewLayout.m:760 + // *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'While + // preparing update a visible view at {length = 2, path = 0 - 142} + // wasn't found in the current data model and was not in an update animation. This is an internal + // error.' + // + // I'm unclear if this is a bug in UIKit, or if we're doing something crazy in + // ConversationViewLayout#prepareLayout. To reproduce, rapidily insert and delete items into the + // conversation. + UIView.animate(withDuration: 0) { + self.messagesTableView.performBatchUpdates(batchUpdates, completion: batchUpdatesCompletion) + if shouldScrollToBottom { + self.scrollToBottom(isAnimated: false) + } + } + } + // TODO: Set last reload date? + } + + func conversationViewModelWillLoadMoreItems() { + contentHeightBeforeUpdate = messagesTableView.contentSize.height + contentOffsetYBeforeUpdate = messagesTableView.contentOffset.y + } + + func conversationViewModelDidLoadMoreItems() { + guard let contentHeightBeforeUpdate = contentHeightBeforeUpdate, let contentOffsetYBeforeUpdate = contentOffsetYBeforeUpdate else { return } + view.layoutIfNeeded() + messagesTableView.contentOffset.y = (messagesTableView.contentSize.height - contentHeightBeforeUpdate + contentOffsetYBeforeUpdate) + isLoadingMore = false + } + + func conversationViewModelDidLoadPrevPage() { + + } + + func conversationViewModelRangeDidChange() { + + } + + func conversationViewModelDidReset() { + + } + + // MARK: General + func getMediaCache() -> NSCache { + return mediaCache + } + + @objc private func handleAudioDidFinishPlayingNotification(_ notification: Notification) { + guard let audioPlayer = audioPlayer, let viewItem = audioPlayer.owner as? ConversationViewItem, + let index = viewItems.firstIndex(where: { $0 === viewItem }), index < (viewItems.endIndex - 1) else { return } + let nextViewItem = viewItems[index + 1] + guard nextViewItem.messageCellType == .audio else { return } + playOrPauseAudio(for: nextViewItem) + } + + func scrollToBottom(isAnimated: Bool) { + guard !isUserScrolling else { return } + // Ensure the view is fully up to date before we try to scroll to the bottom, since + // we use the table view's bounds to determine where the bottom is. + view.layoutIfNeeded() + let firstContentPageTop = -messagesTableView.adjustedContentInset.top + let destinationY = max(firstContentPageTop, lastPageTop) + messagesTableView.setContentOffset(CGPoint(x: 0, y: destinationY), animated: isAnimated) + // TODO: Did scroll to bottom + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + isUserScrolling = true + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + isUserScrolling = false + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + scrollButton.alpha = getScrollButtonOpacity() + autoLoadMoreIfNeeded() + } + + private func autoLoadMoreIfNeeded() { + let isMainAppAndActive = CurrentAppContext().isMainAppAndActive + guard isMainAppAndActive && viewModel.canLoadMoreItems() && !isLoadingMore + && messagesTableView.contentOffset.y < ConversationVC.loadMoreThreshold else { return } + isLoadingMore = true + print("[Test] LOAD MORE") + viewModel.loadAnotherPageOfMessages() + } + + // MARK: Convenience + private func getTitle() -> String { + if let thread = thread as? TSGroupThread { + return thread.groupModel.groupName! + } else if thread.isNoteToSelf() { + return "Note to Self" + } else { + let sessionID = thread.contactIdentifier()! + var result = sessionID + Storage.read { transaction in + result = Storage.shared.getContact(with: sessionID)?.displayName ?? "Anonymous" + } + return result + } + } + + private func getScrollButtonOpacity() -> CGFloat { + let contentOffsetY = messagesTableView.contentOffset.y + let x = (lastPageTop - ConversationVC.bottomInset - contentOffsetY).clamp(0, .greatestFiniteMagnitude) + let a = 1 / (ConversationVC.scrollButtonFullVisibilityThreshold - ConversationVC.scrollButtonNoVisibilityThreshold) + return a * x + } +} diff --git a/Session/Conversations V2/Input View/InputTextView.swift b/Session/Conversations V2/Input View/InputTextView.swift new file mode 100644 index 000000000..70238375e --- /dev/null +++ b/Session/Conversations V2/Input View/InputTextView.swift @@ -0,0 +1,72 @@ + +public final class InputTextView : UITextView, UITextViewDelegate { + private let snDelegate: InputTextViewDelegate + private lazy var heightConstraint = self.set(.height, to: minHeight) + + // MARK: UI Components + private lazy var placeholderLabel: UILabel = { + let result = UILabel() + result.text = "Message" + result.font = .systemFont(ofSize: Values.mediumFontSize) + result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) + return result + }() + + // MARK: Settings + private let minHeight: CGFloat = 22 + private let maxHeight: CGFloat = 120 + + // MARK: Lifecycle + init(delegate: InputTextViewDelegate) { + snDelegate = delegate + super.init(frame: CGRect.zero, textContainer: nil) + setUpViewHierarchy() + self.delegate = self + } + + public override init(frame: CGRect, textContainer: NSTextContainer?) { + preconditionFailure("Use init(delegate:) instead.") + } + + public required init?(coder: NSCoder) { + preconditionFailure("Use init(delegate:) instead.") + } + + private func setUpViewHierarchy() { + showsHorizontalScrollIndicator = false + showsVerticalScrollIndicator = false + backgroundColor = .clear + textColor = Colors.text + font = .systemFont(ofSize: Values.mediumFontSize) + tintColor = Colors.accent + keyboardAppearance = isLightMode ? .light : .dark + heightConstraint.isActive = true + let horizontalInset: CGFloat = 2 + textContainerInset = UIEdgeInsets(top: 0, left: horizontalInset, bottom: 0, right: horizontalInset) + addSubview(placeholderLabel) + placeholderLabel.pin(.leading, to: .leading, of: self, withInset: horizontalInset + 3) // Slight visual adjustment + placeholderLabel.pin(.top, to: .top, of: self) + pin(.trailing, to: .trailing, of: placeholderLabel, withInset: horizontalInset) + pin(.bottom, to: .bottom, of: placeholderLabel) + } + + // MARK: Updating + public func textViewDidChange(_ textView: UITextView) { + placeholderLabel.isHidden = !text.isEmpty + let width = frame.width + let height = frame.height + let size = sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)) + // `textView.contentSize` isn't accurate when restoring a multiline draft, so we set it here manually + self.contentSize = size + let newHeight = size.height.clamp(minHeight, maxHeight) + guard newHeight != height else { return } + heightConstraint.constant = newHeight + snDelegate.inputTextViewDidChangeSize(self) + } +} + +// MARK: Delegate +protocol InputTextViewDelegate { + + func inputTextViewDidChangeSize(_ inputTextView: InputTextView) +} diff --git a/Session/Conversations V2/Input View/InputView.swift b/Session/Conversations V2/Input View/InputView.swift new file mode 100644 index 000000000..787fe9f55 --- /dev/null +++ b/Session/Conversations V2/Input View/InputView.swift @@ -0,0 +1,110 @@ + +final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate { + private let delegate: InputViewDelegate + + var text: String { + get { inputTextView.text } + set { inputTextView.text = newValue } + } + + override var intrinsicContentSize: CGSize { CGSize.zero } + + // MARK: UI Components + private lazy var cameraButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_black"), delegate: self) + private lazy var libraryButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_roll_black"), delegate: self) + private lazy var gifButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_gif_black"), delegate: self) + private lazy var documentButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_document_black"), delegate: self) + private lazy var sendButton = InputViewButton(icon: #imageLiteral(resourceName: "ArrowUp"), isSendButton: true, delegate: self) + + private lazy var inputTextView = InputTextView(delegate: self) + + // MARK: Lifecycle + init(delegate: InputViewDelegate) { + self.delegate = delegate + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(delegate:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(delegate:) instead.") + } + + private func setUpViewHierarchy() { + autoresizingMask = .flexibleHeight + // Background & blur + let backgroundView = UIView() + backgroundView.backgroundColor = isLightMode ? .white : .black + backgroundView.alpha = Values.lowOpacity + addSubview(backgroundView) + backgroundView.pin(to: self) + let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .regular)) + addSubview(blurView) + blurView.pin(to: self) + // Separator + let separator = UIView() + separator.backgroundColor = Colors.text.withAlphaComponent(0.2) + separator.set(.height, to: 1 / UIScreen.main.scale) + addSubview(separator) + separator.pin([ UIView.HorizontalEdge.leading, UIView.VerticalEdge.top, UIView.HorizontalEdge.trailing ], to: self) + // Buttons + func container(for button: InputViewButton) -> UIView { + let result = UIView() + result.addSubview(button) + result.set(.width, to: InputViewButton.expandedSize) + result.set(.height, to: InputViewButton.expandedSize) + button.center(in: result) + return result + } + let (cameraButtonContainer, libraryButtonContainer, gifButtonContainer, documentButtonContainer) = (container(for: cameraButton), container(for: libraryButton), container(for: gifButton), container(for: documentButton)) + let buttonStackView = UIStackView(arrangedSubviews: [ cameraButtonContainer, libraryButtonContainer, gifButtonContainer, documentButtonContainer, UIView.hStretchingSpacer() ]) + buttonStackView.axis = .horizontal + buttonStackView.spacing = Values.smallSpacing + // Bottom stack view + let bottomStackView = UIStackView(arrangedSubviews: [ inputTextView, container(for: sendButton) ]) + bottomStackView.axis = .horizontal + bottomStackView.spacing = Values.smallSpacing + // Main stack view + let mainStackView = UIStackView(arrangedSubviews: [ buttonStackView, bottomStackView ]) + mainStackView.axis = .vertical + mainStackView.spacing = 12 + mainStackView.isLayoutMarginsRelativeArrangement = true + let adjustment = (InputViewButton.expandedSize - InputViewButton.size) / 2 + mainStackView.layoutMargins = UIEdgeInsets(top: Values.smallSpacing, leading: Values.largeSpacing, bottom: Values.smallSpacing, trailing: Values.largeSpacing - adjustment) + addSubview(mainStackView) + mainStackView.pin(.top, to: .bottom, of: separator) + mainStackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: self) + mainStackView.pin(.bottom, to: .bottom, of: self, withInset: -12) + } + + // MARK: Updating + func inputTextViewDidChangeSize(_ inputTextView: InputTextView) { + invalidateIntrinsicContentSize() + } + + // MARK: Interaction + func handleInputViewButtonTapped(_ inputViewButton: InputViewButton) { + if inputViewButton == cameraButton { delegate.handleCameraButtonTapped() } + if inputViewButton == libraryButton { delegate.handleLibraryButtonTapped() } + if inputViewButton == gifButton { delegate.handleGIFButtonTapped() } + if inputViewButton == documentButton { delegate.handleDocumentButtonTapped() } + if inputViewButton == sendButton { delegate.handleSendButtonTapped() } + } + + override func resignFirstResponder() -> Bool { + inputTextView.resignFirstResponder() + } +} + +// MARK: Delegate +protocol InputViewDelegate { + + func handleCameraButtonTapped() + func handleLibraryButtonTapped() + func handleGIFButtonTapped() + func handleDocumentButtonTapped() + func handleSendButtonTapped() +} diff --git a/Session/Conversations V2/Input View/InputViewButton.swift b/Session/Conversations V2/Input View/InputViewButton.swift new file mode 100644 index 000000000..de99787ae --- /dev/null +++ b/Session/Conversations V2/Input View/InputViewButton.swift @@ -0,0 +1,90 @@ + +final class InputViewButton : UIView { + private let icon: UIImage + private let isSendButton: Bool + private let delegate: InputViewButtonDelegate + private lazy var widthConstraint = set(.width, to: InputViewButton.size) + private lazy var heightConstraint = set(.height, to: InputViewButton.size) + + // MARK: Settings + static let size = CGFloat(40) + static let expandedSize = CGFloat(48) + + // MARK: Lifecycle + init(icon: UIImage, isSendButton: Bool = false, delegate: InputViewButtonDelegate) { + self.icon = icon + self.isSendButton = isSendButton + self.delegate = delegate + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(icon:delegate:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(icon:delegate:) instead.") + } + + private func setUpViewHierarchy() { + backgroundColor = isSendButton ? Colors.accent : Colors.text.withAlphaComponent(0.05) + layer.cornerRadius = InputViewButton.size / 2 + layer.masksToBounds = true + isUserInteractionEnabled = true + widthConstraint.isActive = true + heightConstraint.isActive = true + let tint = isSendButton ? UIColor.black : Colors.text + let iconImageView = UIImageView(image: icon.withTint(tint)) + iconImageView.contentMode = .scaleAspectFit + iconImageView.set(.width, to: 20) + iconImageView.set(.height, to: 20) + addSubview(iconImageView) + iconImageView.center(in: self) + } + + // MARK: Animation + private func animate(to size: CGFloat, glowColor: UIColor, backgroundColor: UIColor) { + let frame = CGRect(center: center, size: CGSize(width: size, height: size)) + widthConstraint.constant = size + heightConstraint.constant = size + UIView.animate(withDuration: 0.25) { + self.layoutIfNeeded() + self.frame = frame + self.layer.cornerRadius = size / 2 + let glowConfiguration = UIView.CircularGlowConfiguration(size: size, color: glowColor, isAnimated: true, radius: isLightMode ? 4 : 6) + self.setCircularGlow(with: glowConfiguration) + self.backgroundColor = backgroundColor + } + } + + private func expand() { + animate(to: InputViewButton.expandedSize, glowColor: Colors.expandedButtonGlowColor, backgroundColor: Colors.accent) + } + + private func collapse() { + let backgroundColor = isSendButton ? Colors.accent : Colors.text.withAlphaComponent(0.05) + animate(to: InputViewButton.size, glowColor: .clear, backgroundColor: backgroundColor) + } + + // MARK: Interaction + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + UIImpactFeedbackGenerator(style: .heavy).impactOccurred() + expand() + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + collapse() + delegate.handleInputViewButtonTapped(self) + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + collapse() + } +} + +// MARK: Delegate +protocol InputViewButtonDelegate { + + func handleInputViewButtonTapped(_ inputViewButton: InputViewButton) +} diff --git a/Session/Conversations V2/Message Cells/Content Views/DocumentView.swift b/Session/Conversations V2/Message Cells/Content Views/DocumentView.swift new file mode 100644 index 000000000..ff2b2e73d --- /dev/null +++ b/Session/Conversations V2/Message Cells/Content Views/DocumentView.swift @@ -0,0 +1,51 @@ + +final class DocumentView : UIView { + private let viewItem: ConversationViewItem + private let textColor: UIColor + + // MARK: Settings + private static let iconSize: CGFloat = 24 + private static let iconImageViewSize: CGFloat = 40 + + // MARK: Lifecycle + init(viewItem: ConversationViewItem, textColor: UIColor) { + self.viewItem = viewItem + self.textColor = textColor + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(viewItem:textColor:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(viewItem:textColor:) instead.") + } + + private func setUpViewHierarchy() { + guard let attachment = viewItem.attachmentStream ?? viewItem.attachmentPointer else { return } + // Image view + let iconSize = DocumentView.iconSize + let icon = UIImage(named: "actionsheet_document_black")?.withTint(textColor)?.resizedImage(to: CGSize(width: iconSize, height: iconSize)) + let imageView = UIImageView(image: icon) + imageView.contentMode = .center + let iconImageViewSize = DocumentView.iconImageViewSize + imageView.set(.width, to: iconImageViewSize) + imageView.set(.height, to: iconImageViewSize) + // Body label + let titleLabel = UILabel() + titleLabel.lineBreakMode = .byTruncatingTail + titleLabel.text = attachment.sourceFilename ?? "File" + titleLabel.textColor = textColor + titleLabel.font = .systemFont(ofSize: Values.mediumFontSize) + // Stack view + let stackView = UIStackView(arrangedSubviews: [ imageView, titleLabel ]) + stackView.axis = .horizontal + stackView.alignment = .center + stackView.isLayoutMarginsRelativeArrangement = true + stackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 12) + addSubview(stackView) + stackView.pin(to: self, withInset: Values.smallSpacing) + } +} diff --git a/Session/Conversations/Views & Cells/MediaAlbumCellView.swift b/Session/Conversations V2/Message Cells/Content Views/MediaAlbumView.swift similarity index 82% rename from Session/Conversations/Views & Cells/MediaAlbumCellView.swift rename to Session/Conversations V2/Message Cells/Content Views/MediaAlbumView.swift index 969254144..8fdd9f266 100644 --- a/Session/Conversations/Views & Cells/MediaAlbumCellView.swift +++ b/Session/Conversations V2/Message Cells/Content Views/MediaAlbumView.swift @@ -4,15 +4,15 @@ import Foundation -@objc(OWSMediaAlbumCellView) -public class MediaAlbumCellView: UIStackView { +@objc(OWSMediaAlbumView) +public class MediaAlbumView: UIStackView { private let items: [ConversationMediaAlbumItem] @objc - public let itemViews: [ConversationMediaView] + public let itemViews: [MediaView] @objc - public var moreItemsView: ConversationMediaView? + public var moreItemsView: MediaView? private static let kSpacingPts: CGFloat = 2 private static let kMaxItems = 5 @@ -26,22 +26,20 @@ public class MediaAlbumCellView: UIStackView { public required init(mediaCache: NSCache, items: [ConversationMediaAlbumItem], isOutgoing: Bool, - maxMessageWidth: CGFloat, - isOnionRouted: Bool) { + maxMessageWidth: CGFloat) { self.items = items - self.itemViews = MediaAlbumCellView.itemsToDisplay(forItems: items).map { - let result = ConversationMediaView(mediaCache: mediaCache, + self.itemViews = MediaAlbumView.itemsToDisplay(forItems: items).map { + let result = MediaView(mediaCache: mediaCache, attachment: $0.attachment, isOutgoing: isOutgoing, - maxMessageWidth: maxMessageWidth, - isOnionRouted: isOnionRouted) + maxMessageWidth: maxMessageWidth) return result } super.init(frame: .zero) // UIStackView's backgroundColor property has no effect. - addBackgroundView(withBackgroundColor: Theme.backgroundColor) + addBackgroundView(withBackgroundColor: Colors.navigationBarBackground) createContents(maxMessageWidth: maxMessageWidth) } @@ -62,19 +60,19 @@ public class MediaAlbumCellView: UIStackView { case 2: // X X // side-by-side. - let imageSize = (maxMessageWidth - MediaAlbumCellView.kSpacingPts) / 2 + let imageSize = (maxMessageWidth - MediaAlbumView.kSpacingPts) / 2 autoSet(viewSize: imageSize, ofViews: itemViews) for itemView in itemViews { addArrangedSubview(itemView) } self.axis = .horizontal - self.spacing = MediaAlbumCellView.kSpacingPts + self.spacing = MediaAlbumView.kSpacingPts case 3: // x // X x // Big on left, 2 small on right. - let smallImageSize = (maxMessageWidth - MediaAlbumCellView.kSpacingPts * 2) / 3 - let bigImageSize = smallImageSize * 2 + MediaAlbumCellView.kSpacingPts + let smallImageSize = (maxMessageWidth - MediaAlbumView.kSpacingPts * 2) / 3 + let bigImageSize = smallImageSize * 2 + MediaAlbumView.kSpacingPts guard let leftItemView = itemViews.first else { owsFailDebug("Missing view") @@ -88,12 +86,12 @@ public class MediaAlbumCellView: UIStackView { axis: .vertical, viewSize: smallImageSize)) self.axis = .horizontal - self.spacing = MediaAlbumCellView.kSpacingPts + self.spacing = MediaAlbumView.kSpacingPts case 4: // X X // X X // Square - let imageSize = (maxMessageWidth - MediaAlbumCellView.kSpacingPts) / 2 + let imageSize = (maxMessageWidth - MediaAlbumView.kSpacingPts) / 2 let topViews = Array(itemViews[0..<2]) addArrangedSubview(newRow(rowViews: topViews, @@ -106,13 +104,13 @@ public class MediaAlbumCellView: UIStackView { viewSize: imageSize)) self.axis = .vertical - self.spacing = MediaAlbumCellView.kSpacingPts + self.spacing = MediaAlbumView.kSpacingPts default: // X X // xxx // 2 big on top, 3 small on bottom. - let bigImageSize = (maxMessageWidth - MediaAlbumCellView.kSpacingPts) / 2 - let smallImageSize = (maxMessageWidth - MediaAlbumCellView.kSpacingPts * 2) / 3 + let bigImageSize = (maxMessageWidth - MediaAlbumView.kSpacingPts) / 2 + let smallImageSize = (maxMessageWidth - MediaAlbumView.kSpacingPts * 2) / 3 let topViews = Array(itemViews[0..<2]) addArrangedSubview(newRow(rowViews: topViews, @@ -125,9 +123,9 @@ public class MediaAlbumCellView: UIStackView { viewSize: smallImageSize)) self.axis = .vertical - self.spacing = MediaAlbumCellView.kSpacingPts + self.spacing = MediaAlbumView.kSpacingPts - if items.count > MediaAlbumCellView.kMaxItems { + if items.count > MediaAlbumView.kMaxItems { guard let lastView = bottomViews.last else { owsFailDebug("Missing lastView") return @@ -140,7 +138,7 @@ public class MediaAlbumCellView: UIStackView { lastView.addSubview(tintView) tintView.autoPinEdgesToSuperviewEdges() - let moreCount = max(1, items.count - MediaAlbumCellView.kMaxItems) + let moreCount = max(1, items.count - MediaAlbumView.kMaxItems) let moreCountText = OWSFormat.formatInt(Int32(moreCount)) let moreText = String(format: NSLocalizedString("MEDIA_GALLERY_MORE_ITEMS_FORMAT", comment: "Format for the 'more items' indicator for media galleries. Embeds {{the number of additional items}}."), moreCountText) @@ -184,24 +182,24 @@ public class MediaAlbumCellView: UIStackView { } private func autoSet(viewSize: CGFloat, - ofViews views: [ConversationMediaView]) { + ofViews views: [MediaView]) { for itemView in views { itemView.autoSetDimensions(to: CGSize(width: viewSize, height: viewSize)) } } - private func newRow(rowViews: [ConversationMediaView], + private func newRow(rowViews: [MediaView], axis: NSLayoutConstraint.Axis, viewSize: CGFloat) -> UIStackView { autoSet(viewSize: viewSize, ofViews: rowViews) return newRow(rowViews: rowViews, axis: axis) } - private func newRow(rowViews: [ConversationMediaView], + private func newRow(rowViews: [MediaView], axis: NSLayoutConstraint.Axis) -> UIStackView { let stackView = UIStackView(arrangedSubviews: rowViews) stackView.axis = axis - stackView.spacing = MediaAlbumCellView.kSpacingPts + stackView.spacing = MediaAlbumView.kSpacingPts return stackView } @@ -267,8 +265,8 @@ public class MediaAlbumCellView: UIStackView { } @objc - public func mediaView(forLocation location: CGPoint) -> ConversationMediaView? { - var bestMediaView: ConversationMediaView? + public func mediaView(forLocation location: CGPoint) -> MediaView? { + var bestMediaView: MediaView? var bestDistance: CGFloat = 0 for itemView in itemViews { let itemCenter = convert(itemView.center, from: itemView.superview) @@ -283,7 +281,7 @@ public class MediaAlbumCellView: UIStackView { } @objc - public func isMoreItemsView(mediaView: ConversationMediaView) -> Bool { + public func isMoreItemsView(mediaView: MediaView) -> Bool { return moreItemsView == mediaView } } diff --git a/Session/Conversations V2/Message Cells/Content Views/MediaLoaderView.swift b/Session/Conversations V2/Message Cells/Content Views/MediaLoaderView.swift new file mode 100644 index 000000000..ee4897e34 --- /dev/null +++ b/Session/Conversations V2/Message Cells/Content Views/MediaLoaderView.swift @@ -0,0 +1,78 @@ + +final class MediaLoaderView : UIView { + private let bar = UIView() + + private lazy var barLeftConstraint = bar.pin(.left, to: .left, of: self) + private lazy var barRightConstraint = bar.pin(.right, to: .right, of: self) + + // MARK: Lifecycle + override init(frame: CGRect) { + super.init(frame: frame) + setUpViewHierarchy() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUpViewHierarchy() + } + + private func setUpViewHierarchy() { + bar.backgroundColor = Colors.accent + bar.set(.height, to: 8) + addSubview(bar) + barLeftConstraint.isActive = true + bar.pin(.top, to: .top, of: self) + barRightConstraint.isActive = true + bar.pin(.bottom, to: .bottom, of: self) + step1() + } + + // MARK: Animation + func step1() { + barRightConstraint.constant = -bounds.width + UIView.animate(withDuration: 0.5, animations: { [weak self] in + guard let self = self else { return } + self.barRightConstraint.constant = 0 + self.layoutIfNeeded() + }, completion: { [weak self] _ in + self?.step2() + }) + } + + func step2() { + barLeftConstraint.constant = 0 + UIView.animate(withDuration: 0.5, animations: { [weak self] in + guard let self = self else { return } + self.barLeftConstraint.constant = self.bounds.width + self.layoutIfNeeded() + }, completion: { [weak self] _ in + Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in + self?.step3() + } + }) + } + + func step3() { + barLeftConstraint.constant = bounds.width + UIView.animate(withDuration: 0.5, animations: { [weak self] in + guard let self = self else { return } + self.barLeftConstraint.constant = 0 + self.layoutIfNeeded() + }, completion: { [weak self] _ in + self?.step4() + }) + } + + func step4() { + barRightConstraint.constant = 0 + UIView.animate(withDuration: 0.5, animations: { [weak self] in + guard let self = self else { return } + self.barRightConstraint.constant = -self.bounds.width + self.layoutIfNeeded() + }, completion: { [weak self] _ in + Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { _ in + self?.step1() + } + }) + } +} diff --git a/Session/Conversations V2/Message Cells/Content Views/MediaTextOverlayView.swift b/Session/Conversations V2/Message Cells/Content Views/MediaTextOverlayView.swift new file mode 100644 index 000000000..b67f089cb --- /dev/null +++ b/Session/Conversations V2/Message Cells/Content Views/MediaTextOverlayView.swift @@ -0,0 +1,78 @@ + +final class MediaTextOverlayView : UIView { + private let viewItem: ConversationViewItem + private let albumViewWidth: CGFloat + private let delegate: MessageCellDelegate + var readMoreButton: UIButton? + + // MARK: Settings + private static let maxHeight: CGFloat = 88; + + // MARK: Lifecycle + init(viewItem: ConversationViewItem, albumViewWidth: CGFloat, delegate: MessageCellDelegate) { + self.viewItem = viewItem + self.albumViewWidth = albumViewWidth + self.delegate = delegate + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(text:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(text:) instead.") + } + + private func setUpViewHierarchy() { + guard let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 else { return } + // Shadow + let shadowView = GradientView(from: .clear, to: UIColor.black.withAlphaComponent(0.7)) + addSubview(shadowView) + shadowView.pin(to: self) + // Line + let lineView = UIView() + lineView.backgroundColor = Colors.accent + lineView.set(.width, to: Values.accentLineThickness) + // Body label + let bodyLabel = UILabel() + bodyLabel.numberOfLines = 0 + bodyLabel.lineBreakMode = .byTruncatingTail + bodyLabel.text = given(body) { MentionUtilities.highlightMentions(in: $0, threadID: viewItem.interaction.uniqueThreadId) } + bodyLabel.textColor = .white + bodyLabel.font = .systemFont(ofSize: Values.smallFontSize) + // Content stack view + let contentStackView = UIStackView(arrangedSubviews: [ lineView, bodyLabel ]) + contentStackView.axis = .horizontal + contentStackView.spacing = Values.smallSpacing + addSubview(contentStackView) + let inset = Values.mediumSpacing + contentStackView.pin(.left, to: .left, of: self, withInset: inset) + contentStackView.pin(.top, to: .top, of: self, withInset: 3 * inset) + contentStackView.pin(.right, to: .right, of: self, withInset: -inset) + // Max height + bodyLabel.heightAnchor.constraint(lessThanOrEqualToConstant: MediaTextOverlayView.maxHeight).isActive = true + // Overflow button + let bodyLabelTargetSize = bodyLabel.sizeThatFits(CGSize(width: albumViewWidth - 2 * inset, height: .greatestFiniteMagnitude)) + if bodyLabelTargetSize.height > MediaTextOverlayView.maxHeight { + let readMoreButton = UIButton() + self.readMoreButton = readMoreButton + readMoreButton.setTitle("Read More", for: UIControl.State.normal) + readMoreButton.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize) + readMoreButton.setTitleColor(.white, for: UIControl.State.normal) + readMoreButton.addTarget(self, action: #selector(readMore), for: UIControl.Event.touchUpInside) + addSubview(readMoreButton) + readMoreButton.pin(.left, to: .left, of: self, withInset: inset) + readMoreButton.pin(.top, to: .bottom, of: contentStackView, withInset: Values.smallSpacing) + readMoreButton.pin(.bottom, to: .bottom, of: self, withInset: -Values.smallSpacing) + } else { + contentStackView.pin(.bottom, to: .bottom, of: self, withInset: -inset) + } + } + + // MARK: Interaction + @objc private func readMore() { + delegate.showFullText(viewItem) + } +} diff --git a/Session/Conversations/Views & Cells/ConversationMediaView.swift b/Session/Conversations V2/Message Cells/Content Views/MediaView.swift similarity index 87% rename from Session/Conversations/Views & Cells/ConversationMediaView.swift rename to Session/Conversations V2/Message Cells/Content Views/MediaView.swift index fc57923a8..9c9e7b77b 100644 --- a/Session/Conversations/Views & Cells/ConversationMediaView.swift +++ b/Session/Conversations V2/Message Cells/Content Views/MediaView.swift @@ -4,8 +4,8 @@ import Foundation -@objc(OWSConversationMediaView) -public class ConversationMediaView: UIView { +@objc(OWSMediaView) +public class MediaView: UIView { private enum MediaError { case missing @@ -22,7 +22,6 @@ public class ConversationMediaView: UIView { private let maxMessageWidth: CGFloat private var loadBlock: (() -> Void)? private var unloadBlock: (() -> Void)? - private let isOnionRouted: Bool // MARK: - LoadState @@ -85,17 +84,15 @@ public class ConversationMediaView: UIView { public required init(mediaCache: NSCache, attachment: TSAttachment, isOutgoing: Bool, - maxMessageWidth: CGFloat, - isOnionRouted: Bool) { + maxMessageWidth: CGFloat) { self.mediaCache = mediaCache self.attachment = attachment self.isOutgoing = isOutgoing self.maxMessageWidth = maxMessageWidth - self.isOnionRouted = isOnionRouted super.init(frame: .zero) - backgroundColor = Theme.offBackgroundColor + backgroundColor = Colors.unimportant clipsToBounds = true createContents() @@ -152,53 +149,19 @@ public class ConversationMediaView: UIView { configure(forError: .missing) return } - guard let attachmentId = attachmentPointer.uniqueId else { - owsFailDebug("Attachment missing unique ID.") - configure(forError: .invalid) - return - } - /* - guard nil != attachmentDownloads.downloadProgress(forAttachmentId: attachmentId) else { - // Not being downloaded. - configure(forError: .missing) - return - } - */ - backgroundColor = (Theme.isDarkThemeEnabled ? .ows_gray90 : .ows_gray05) - let view: UIView - if isOnionRouted { // Loki: Due to the way onion routing works we can't get upload progress for those attachments - let activityIndicatorView = UIActivityIndicatorView(style: .white) - activityIndicatorView.isHidden = false - activityIndicatorView.startAnimating() - view = activityIndicatorView - } else { - view = MediaDownloadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) - } - addSubview(view) - view.autoPinEdgesToSuperviewEdges() + let loader = MediaLoaderView() + addSubview(loader) + loader.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.bottom, UIView.HorizontalEdge.right ], to: self) } private func addUploadProgressIfNecessary(_ subview: UIView) -> Bool { guard isOutgoing else { return false } guard let attachmentStream = attachment as? TSAttachmentStream else { return false } - guard let attachmentId = attachmentStream.uniqueId else { - owsFailDebug("Attachment missing unique ID.") - configure(forError: .invalid) - return false - } guard !attachmentStream.isUploaded else { return false } - let view: UIView - if isOnionRouted { // Loki: Due to the way onion routing works we can't get upload progress for those attachments - let activityIndicatorView = UIActivityIndicatorView(style: .white) - activityIndicatorView.isHidden = false - activityIndicatorView.startAnimating() - view = activityIndicatorView - } else { - view = MediaUploadView(attachmentId: attachmentId, radius: maxMessageWidth * 0.1) - } - addSubview(view) - view.autoPinEdgesToSuperviewEdges() + let loader = MediaLoaderView() + addSubview(loader) + loader.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.bottom, UIView.HorizontalEdge.right ], to: self) return true } @@ -215,7 +178,7 @@ public class ConversationMediaView: UIView { // some performance cost. animatedImageView.layer.minificationFilter = .trilinear animatedImageView.layer.magnificationFilter = .trilinear - animatedImageView.backgroundColor = Theme.offBackgroundColor + animatedImageView.backgroundColor = Colors.unimportant addSubview(animatedImageView) animatedImageView.autoPinEdgesToSuperviewEdges() _ = addUploadProgressIfNecessary(animatedImageView) @@ -274,7 +237,7 @@ public class ConversationMediaView: UIView { // some performance cost. stillImageView.layer.minificationFilter = .trilinear stillImageView.layer.magnificationFilter = .trilinear - stillImageView.backgroundColor = Theme.offBackgroundColor + stillImageView.backgroundColor = Colors.unimportant addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() _ = addUploadProgressIfNecessary(stillImageView) @@ -329,7 +292,7 @@ public class ConversationMediaView: UIView { // some performance cost. stillImageView.layer.minificationFilter = .trilinear stillImageView.layer.magnificationFilter = .trilinear - stillImageView.backgroundColor = Theme.offBackgroundColor + stillImageView.backgroundColor = Colors.unimportant addSubview(stillImageView) stillImageView.autoPinEdgesToSuperviewEdges() @@ -408,7 +371,7 @@ public class ConversationMediaView: UIView { return } let iconView = UIImageView(image: icon.withRenderingMode(.alwaysTemplate)) - iconView.tintColor = Theme.primaryColor.withAlphaComponent(0.6) + iconView.tintColor = Colors.text.withAlphaComponent(Values.mediumOpacity) addSubview(iconView) iconView.autoCenterInSuperview() } @@ -457,7 +420,7 @@ public class ConversationMediaView: UIView { Logger.verbose("media cache miss") let threadSafeLoadState = self.threadSafeLoadState - ConversationMediaView.loadQueue.async { + MediaView.loadQueue.async { guard threadSafeLoadState.get() == .loading else { Logger.verbose("Skipping obsolete load.") return diff --git a/Session/Conversations V2/Message Cells/Content Views/QuoteView.swift b/Session/Conversations V2/Message Cells/Content Views/QuoteView.swift new file mode 100644 index 000000000..5eeacd7a7 --- /dev/null +++ b/Session/Conversations V2/Message Cells/Content Views/QuoteView.swift @@ -0,0 +1,150 @@ + +final class QuoteView : UIView { + private let viewItem: ConversationViewItem + private let maxMessageWidth: CGFloat + + private var direction: Direction { + guard let message = viewItem.interaction as? TSMessage else { preconditionFailure() } + switch message { + case is TSIncomingMessage: return .incoming + case is TSOutgoingMessage: return .outgoing + default: preconditionFailure() + } + } + + private var lineColor: UIColor { + return .black + } + + private var textColor: UIColor { + switch (direction, AppModeManager.shared.currentAppMode) { + case (.outgoing, .dark), (.incoming, .light): return .white + default: return .black + } + } + + private var snBackgroundColor: UIColor { + switch direction { + case .outgoing: return Colors.receivedMessageBackground + case .incoming: return Colors.sentMessageBackground + } + } + + // MARK: Direction + enum Direction { case incoming, outgoing } + + // MARK: Settings + static let inset = Values.smallSpacing + static let thumbnailSize: CGFloat = 48 + static let iconSize: CGFloat = 24 + static let labelStackViewSpacing: CGFloat = 2 + + // MARK: Lifecycle + init(for viewItem: ConversationViewItem, maxMessageWidth: CGFloat) { + self.viewItem = viewItem + self.maxMessageWidth = maxMessageWidth + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(for:maxMessageWidth:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(for:maxMessageWidth:) instead.") + } + + private func setUpViewHierarchy() { + guard let quote = (viewItem.interaction as? TSMessage)?.quotedMessage else { return } + let hasAttachments = !quote.quotedAttachments.isEmpty + let thumbnailSize = QuoteView.thumbnailSize + let iconSize = QuoteView.iconSize + let labelStackViewSpacing = QuoteView.labelStackViewSpacing + let smallSpacing = Values.smallSpacing + let inset = QuoteView.inset + let availableWidth: CGFloat + if !hasAttachments { + availableWidth = maxMessageWidth - 2 * inset - Values.accentLineThickness - 2 * smallSpacing + } else { + availableWidth = maxMessageWidth - 2 * inset - thumbnailSize - 2 * smallSpacing + } + let availableSpace = CGSize(width: availableWidth, height: .greatestFiniteMagnitude) + var body = quote.body + // Main stack view + let mainStackView = UIStackView(arrangedSubviews: []) + mainStackView.axis = .horizontal + mainStackView.spacing = smallSpacing + mainStackView.isLayoutMarginsRelativeArrangement = true + mainStackView.layoutMargins = UIEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: smallSpacing) + mainStackView.alignment = .center + // Content view + let contentView = UIView() + contentView.backgroundColor = snBackgroundColor + contentView.layer.cornerRadius = VisibleMessageCell.smallCornerRadius + contentView.layer.masksToBounds = true + addSubview(contentView) + contentView.pin(to: self, withInset: inset) + // Line view + let lineView = UIView() + lineView.backgroundColor = lineColor + lineView.set(.width, to: Values.accentLineThickness) + if !hasAttachments { + mainStackView.addArrangedSubview(lineView) + } else { + let image = viewItem.quotedReply?.thumbnailImage + let fallbackImage = UIImage(named: "actionsheet_document_black")?.withTint(.white)?.resizedImage(to: CGSize(width: iconSize, height: iconSize)) + let imageView = UIImageView(image: image ?? fallbackImage) + imageView.contentMode = (image != nil) ? .scaleAspectFill : .center + imageView.backgroundColor = lineColor + imageView.set(.width, to: thumbnailSize) + imageView.set(.height, to: thumbnailSize) + mainStackView.addArrangedSubview(imageView) + body = (image != nil) ? "Image" : "Document" + } + // Body label + let bodyLabel = UILabel() + bodyLabel.numberOfLines = 0 + bodyLabel.lineBreakMode = .byTruncatingTail + bodyLabel.text = given(body) { MentionUtilities.highlightMentions(in: $0, threadID: viewItem.interaction.uniqueThreadId) } ?? "Document" + bodyLabel.textColor = textColor + bodyLabel.font = .systemFont(ofSize: Values.smallFontSize) + if hasAttachments { + bodyLabel.numberOfLines = 1 + } + let bodyLabelSize = bodyLabel.systemLayoutSizeFitting(availableSpace) + // Label stack view + if viewItem.isGroupThread { + let authorLabel = UILabel() + authorLabel.lineBreakMode = .byTruncatingTail + authorLabel.text = SSKEnvironment.shared.profileManager.profileNameForRecipient(withID: quote.authorId, avoidingWriteTransaction: true) + authorLabel.textColor = textColor + authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize) + let authorLabelSize = authorLabel.systemLayoutSizeFitting(availableSpace) + let labelStackView = UIStackView(arrangedSubviews: [ authorLabel, bodyLabel ]) + labelStackView.axis = .vertical + labelStackView.spacing = labelStackViewSpacing + labelStackView.set(.width, to: max(bodyLabelSize.width, authorLabelSize.width)) + mainStackView.addArrangedSubview(labelStackView) + } else { + mainStackView.addArrangedSubview(bodyLabel) + } + // Constraints + contentView.addSubview(mainStackView) + mainStackView.pin(to: contentView) + if !viewItem.isGroupThread { + bodyLabel.set(.width, to: bodyLabelSize.width) + } + let bodyLabelHeight = bodyLabelSize.height + let authorLabelHeight: CGFloat = 14.33 + let isAuthorShown = viewItem.isGroupThread + let contentViewHeight: CGFloat + if hasAttachments { + contentViewHeight = thumbnailSize + } else { + contentViewHeight = bodyLabelHeight + 2 * smallSpacing + (isAuthorShown ? (authorLabelHeight + labelStackViewSpacing) : 0) + } + contentView.set(.height, to: contentViewHeight) + lineView.set(.height, to: contentViewHeight) + } +} diff --git a/Session/Conversations/Views & Cells/TypingIndicatorView.swift b/Session/Conversations V2/Message Cells/Content Views/TypingIndicatorView.swift similarity index 100% rename from Session/Conversations/Views & Cells/TypingIndicatorView.swift rename to Session/Conversations V2/Message Cells/Content Views/TypingIndicatorView.swift diff --git a/Session/Conversations V2/Message Cells/Content Views/VoiceMessageViewV2.swift b/Session/Conversations V2/Message Cells/Content Views/VoiceMessageViewV2.swift new file mode 100644 index 000000000..d567fef11 --- /dev/null +++ b/Session/Conversations V2/Message Cells/Content Views/VoiceMessageViewV2.swift @@ -0,0 +1,170 @@ +import NVActivityIndicatorView + +@objc(SNVoiceMessageView) +public final class VoiceMessageViewV2 : UIView { + private let viewItem: ConversationViewItem + private var isShowingSpeedUpLabel = false + @objc var progress: Int = 0 { didSet { handleProgressChanged() } } + @objc var isPlaying = false { didSet { handleIsPlayingChanged() } } + + private lazy var progressViewRightConstraint = progressView.pin(.right, to: .right, of: self, withInset: -VoiceMessageViewV2.width) + + private var attachment: TSAttachment? { viewItem.attachmentStream ?? viewItem.attachmentPointer } + private var duration: Int { Int(viewItem.audioDurationSeconds) } + + // MARK: UI Components + private lazy var progressView: UIView = { + let result = UIView() + result.backgroundColor = UIColor.black.withAlphaComponent(0.2) + return result + }() + + private lazy var toggleImageView: UIImageView = { + let result = UIImageView(image: UIImage(named: "Play")) + result.set(.width, to: 8) + result.set(.height, to: 8) + result.contentMode = .scaleAspectFit + return result + }() + + private lazy var loader: NVActivityIndicatorView = { + let result = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: Colors.text, padding: nil) + result.set(.width, to: VoiceMessageViewV2.toggleContainerSize + 2) + result.set(.height, to: VoiceMessageViewV2.toggleContainerSize + 2) + return result + }() + + private lazy var countdownLabelContainer: UIView = { + let result = UIView() + result.backgroundColor = .white + result.layer.masksToBounds = true + result.set(.height, to: VoiceMessageViewV2.toggleContainerSize) + result.set(.width, to: 44) + return result + }() + + private lazy var countdownLabel: UILabel = { + let result = UILabel() + result.textColor = .black + result.font = .systemFont(ofSize: Values.smallFontSize) + result.text = "00:00" + return result + }() + + private lazy var speedUpLabel: UILabel = { + let result = UILabel() + result.textColor = .black + result.font = .systemFont(ofSize: Values.smallFontSize) + result.alpha = 0 + result.text = "1.5x" + result.textAlignment = .center + return result + }() + + // MARK: Settings + private static let width: CGFloat = 160 + private static let toggleContainerSize: CGFloat = 20 + private static let inset = Values.smallSpacing + + // MARK: Lifecycle + init(viewItem: ConversationViewItem) { + self.viewItem = viewItem + self.progress = Int(viewItem.audioProgressSeconds) + super.init(frame: CGRect.zero) + setUpViewHierarchy() + handleProgressChanged() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(viewItem:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(viewItem:) instead.") + } + + private func setUpViewHierarchy() { + let toggleContainerSize = VoiceMessageViewV2.toggleContainerSize + let inset = VoiceMessageViewV2.inset + // Width & height + set(.width, to: VoiceMessageViewV2.width) + // Toggle + let toggleContainer = UIView() + toggleContainer.backgroundColor = .white + toggleContainer.set(.width, to: toggleContainerSize) + toggleContainer.set(.height, to: toggleContainerSize) + toggleContainer.addSubview(toggleImageView) + toggleImageView.center(in: toggleContainer) + toggleContainer.layer.cornerRadius = toggleContainerSize / 2 + toggleContainer.layer.masksToBounds = true + // Line + let lineView = UIView() + lineView.backgroundColor = .white + lineView.set(.height, to: 1) + // Countdown label + countdownLabelContainer.addSubview(countdownLabel) + countdownLabel.center(in: countdownLabelContainer) + // Speed up label + countdownLabelContainer.addSubview(speedUpLabel) + speedUpLabel.center(in: countdownLabelContainer) + // Constraints + addSubview(progressView) + progressView.pin(.left, to: .left, of: self) + progressView.pin(.top, to: .top, of: self) + progressViewRightConstraint.isActive = true + progressView.pin(.bottom, to: .bottom, of: self) + addSubview(toggleContainer) + toggleContainer.pin(.left, to: .left, of: self, withInset: inset) + toggleContainer.pin(.top, to: .top, of: self, withInset: inset) + toggleContainer.pin(.bottom, to: .bottom, of: self, withInset: -inset) + addSubview(lineView) + lineView.pin(.left, to: .right, of: toggleContainer) + lineView.center(.vertical, in: self) + addSubview(countdownLabelContainer) + countdownLabelContainer.pin(.left, to: .right, of: lineView) + countdownLabelContainer.pin(.right, to: .right, of: self, withInset: -inset) + countdownLabelContainer.center(.vertical, in: self) + addSubview(loader) + loader.center(in: toggleContainer) + } + + // MARK: Updating + public override func layoutSubviews() { + super.layoutSubviews() + countdownLabelContainer.layer.cornerRadius = countdownLabelContainer.bounds.height / 2 + } + + private func handleIsPlayingChanged() { + toggleImageView.image = isPlaying ? UIImage(named: "Pause") : UIImage(named: "Play") + } + + private func handleProgressChanged() { + let isDownloaded = (attachment?.isDownloaded == true) + loader.isHidden = isDownloaded + if isDownloaded { loader.stopAnimating() } else if !loader.isAnimating { loader.startAnimating() } + guard isDownloaded else { return } + countdownLabel.text = OWSFormat.formatDurationSeconds(duration - progress) + guard viewItem.audioProgressSeconds > 0 && viewItem.audioDurationSeconds > 0 else { return } + let fraction = viewItem.audioProgressSeconds / viewItem.audioDurationSeconds + progressViewRightConstraint.constant = -(VoiceMessageViewV2.width * (1 - fraction)) + } + + func showSpeedUpLabel() { + guard !isShowingSpeedUpLabel else { return } + isShowingSpeedUpLabel = true + UIView.animate(withDuration: 0.25) { [weak self] in + guard let self = self else { return } + self.countdownLabel.alpha = 0 + self.speedUpLabel.alpha = 1 + } + Timer.scheduledTimer(withTimeInterval: 1.25, repeats: false) { [weak self] _ in + UIView.animate(withDuration: 0.25, animations: { + guard let self = self else { return } + self.countdownLabel.alpha = 1 + self.speedUpLabel.alpha = 0 + }, completion: { _ in + self?.isShowingSpeedUpLabel = false + }) + } + } +} diff --git a/Session/Conversations V2/Message Cells/InfoMessageCell.swift b/Session/Conversations V2/Message Cells/InfoMessageCell.swift new file mode 100644 index 000000000..c7873cbc1 --- /dev/null +++ b/Session/Conversations V2/Message Cells/InfoMessageCell.swift @@ -0,0 +1,71 @@ + +final class InfoMessageCell : MessageCell { + private lazy var iconImageViewWidthConstraint = iconImageView.set(.width, to: InfoMessageCell.iconSize) + private lazy var iconImageViewHeightConstraint = iconImageView.set(.height, to: InfoMessageCell.iconSize) + + // MARK: UI Components + private lazy var iconImageView = UIImageView() + + private lazy var label: UILabel = { + let result = UILabel() + result.numberOfLines = 0 + result.lineBreakMode = .byWordWrapping + result.font = .boldSystemFont(ofSize: Values.verySmallFontSize) + result.textColor = Colors.text + result.textAlignment = .center + return result + }() + + private lazy var stackView: UIStackView = { + let result = UIStackView(arrangedSubviews: [ iconImageView, label ]) + result.axis = .vertical + result.alignment = .center + result.spacing = Values.smallSpacing + return result + }() + + // MARK: Settings + private static let iconSize: CGFloat = 16 + private static let inset = Values.mediumSpacing + + override class var identifier: String { "InfoMessageCell" } + + // MARK: Lifecycle + override func setUpViewHierarchy() { + super.setUpViewHierarchy() + iconImageViewWidthConstraint.isActive = true + iconImageViewHeightConstraint.isActive = true + addSubview(stackView) + stackView.pin(.left, to: .left, of: self, withInset: InfoMessageCell.inset) + stackView.pin(.top, to: .top, of: self, withInset: InfoMessageCell.inset) + stackView.pin(.right, to: .right, of: self, withInset: -InfoMessageCell.inset) + stackView.pin(.bottom, to: .bottom, of: self, withInset: -InfoMessageCell.inset) + } + + // MARK: Updating + override func update() { + guard let message = viewItem?.interaction as? TSInfoMessage else { return } + let icon: UIImage? + switch message.messageType { + case .typeDisappearingMessagesUpdate: + var configuration: OWSDisappearingMessagesConfiguration? + Storage.read { transaction in + configuration = message.thread(with: transaction).disappearingMessagesConfiguration(with: transaction) + } + if let configuration = configuration { + icon = configuration.isEnabled ? UIImage(named: "ic_timer") : UIImage(named: "ic_timer_disabled") + } else { + icon = nil + } + default: icon = nil + } + if let icon = icon { + iconImageView.image = icon.withTint(Colors.text) + } + iconImageViewWidthConstraint.constant = (icon != nil) ? InfoMessageCell.iconSize : 0 + iconImageViewHeightConstraint.constant = (icon != nil) ? InfoMessageCell.iconSize : 0 + Storage.read { transaction in + self.label.text = message.previewText(with: transaction) + } + } +} diff --git a/Session/Conversations V2/Message Cells/MessageCell.swift b/Session/Conversations V2/Message Cells/MessageCell.swift new file mode 100644 index 000000000..219165e20 --- /dev/null +++ b/Session/Conversations V2/Message Cells/MessageCell.swift @@ -0,0 +1,58 @@ +import UIKit + +class MessageCell : UITableViewCell { + var delegate: MessageCellDelegate? + var viewItem: ConversationViewItem? { didSet { update() } } + + // MARK: Settings + class var identifier: String { preconditionFailure("Must be overridden by subclasses.") } + + // MARK: Lifecycle + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setUpViewHierarchy() + setUpGestureRecognizers() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUpViewHierarchy() + setUpGestureRecognizers() + } + + func setUpViewHierarchy() { + backgroundColor = .clear + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = .clear + self.selectedBackgroundView = selectedBackgroundView + } + + func setUpGestureRecognizers() { + // To be overridden by subclasses + } + + // MARK: Updating + func update() { + preconditionFailure("Must be overridden by subclasses.") + } + + // MARK: Convenience + static func getCellType(for viewItem: ConversationViewItem) -> MessageCell.Type { + switch viewItem.interaction { + case is TSIncomingMessage: fallthrough + case is TSOutgoingMessage: return VisibleMessageCell.self + case is TSInfoMessage: return InfoMessageCell.self + case is TypingIndicatorInteraction: return TypingIndicatorCellV2.self + default: preconditionFailure() + } + } +} + +protocol MessageCellDelegate { + + func getMediaCache() -> NSCache + func handleViewItemLongPressed(_ viewItem: ConversationViewItem) + func handleViewItemTapped(_ viewItem: ConversationViewItem, gestureRecognizer: UITapGestureRecognizer) + func handleViewItemDoubleTapped(_ viewItem: ConversationViewItem) + func showFullText(_ viewItem: ConversationViewItem) +} diff --git a/Session/Conversations V2/Message Cells/TypingIndicatorCellV2.swift b/Session/Conversations V2/Message Cells/TypingIndicatorCellV2.swift new file mode 100644 index 000000000..0dda6b790 --- /dev/null +++ b/Session/Conversations V2/Message Cells/TypingIndicatorCellV2.swift @@ -0,0 +1,85 @@ + +// Assumptions +// • We'll never encounter an outgoing typing indicator. +// • Typing indicators are only sent in contact threads. + +final class TypingIndicatorCellV2 : MessageCell { + + private var positionInCluster: Position? { + guard let viewItem = viewItem else { return nil } + if viewItem.isFirstInCluster { return .top } + if viewItem.isLastInCluster { return .bottom } + return .middle + } + + private var isOnlyMessageInCluster: Bool { viewItem?.isFirstInCluster == true && viewItem?.isLastInCluster == true } + + // MARK: UI Components + private lazy var bubbleView: UIView = { + let result = UIView() + result.layer.cornerRadius = VisibleMessageCell.smallCornerRadius + result.backgroundColor = Colors.receivedMessageBackground + return result + }() + + private let bubbleViewMaskLayer = CAShapeLayer() + + private lazy var typingIndicatorView = TypingIndicatorView() + + // MARK: Settings + override class var identifier: String { "TypingIndicatorCell" } + + // MARK: Direction & Position + enum Position { case top, middle, bottom } + + // MARK: Lifecycle + override func setUpViewHierarchy() { + super.setUpViewHierarchy() + // Bubble view + addSubview(bubbleView) + bubbleView.pin(.left, to: .left, of: self, withInset: VisibleMessageCell.contactThreadHSpacing) + bubbleView.pin(.top, to: .top, of: self, withInset: 1) + // Typing indicator view + bubbleView.addSubview(typingIndicatorView) + typingIndicatorView.pin(to: bubbleView, withInset: 12) + } + + // MARK: Updating + override func update() { + guard let viewItem = viewItem, viewItem.interaction is TypingIndicatorInteraction else { return } + // Bubble view + updateBubbleViewCorners() + // Typing indicator view + typingIndicatorView.startAnimation() + } + + override func layoutSubviews() { + super.layoutSubviews() + updateBubbleViewCorners() + } + + private func updateBubbleViewCorners() { + let maskPath = UIBezierPath(roundedRect: bubbleView.bounds, byRoundingCorners: getCornersToRound(), + cornerRadii: CGSize(width: VisibleMessageCell.largeCornerRadius, height: VisibleMessageCell.largeCornerRadius)) + bubbleViewMaskLayer.path = maskPath.cgPath + bubbleView.layer.mask = bubbleViewMaskLayer + } + + override func prepareForReuse() { + super.prepareForReuse() + typingIndicatorView.stopAnimation() + } + + // MARK: Convenience + private func getCornersToRound() -> UIRectCorner { + guard !isOnlyMessageInCluster else { return .allCorners } + let result: UIRectCorner + switch positionInCluster { + case .top: result = [ .topLeft, .topRight, .bottomRight ] + case .middle: result = [ .topRight, .bottomRight ] + case .bottom: result = [ .topRight, .bottomRight, .bottomLeft ] + case nil: result = .allCorners + } + return result + } +} diff --git a/Session/Conversations V2/Message Cells/VisibleMessageCell.swift b/Session/Conversations V2/Message Cells/VisibleMessageCell.swift new file mode 100644 index 000000000..ec145ac4b --- /dev/null +++ b/Session/Conversations V2/Message Cells/VisibleMessageCell.swift @@ -0,0 +1,395 @@ + +final class VisibleMessageCell : MessageCell { + private var unloadContent: (() -> Void)? + var albumView: MediaAlbumView? + var mediaTextOverlayView: MediaTextOverlayView? + // Constraints + private lazy var headerViewTopConstraint = headerView.pin(.top, to: .top, of: self, withInset: 1) + private lazy var profilePictureViewLeftConstraint = profilePictureView.pin(.left, to: .left, of: self, withInset: VisibleMessageCell.groupThreadHSpacing) + private lazy var profilePictureViewWidthConstraint = profilePictureView.set(.width, to: Values.verySmallProfilePictureSize) + private lazy var bubbleViewLeftConstraint1 = bubbleView.pin(.left, to: .right, of: profilePictureView, withInset: VisibleMessageCell.groupThreadHSpacing) + private lazy var bubbleViewLeftConstraint2 = bubbleView.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor, constant: VisibleMessageCell.gutterSize) + private lazy var bubbleViewTopConstraint = bubbleView.pin(.top, to: .bottom, of: authorLabel, withInset: VisibleMessageCell.authorLabelBottomSpacing) + private lazy var bubbleViewRightConstraint1 = bubbleView.pin(.right, to: .right, of: self, withInset: -VisibleMessageCell.contactThreadHSpacing) + private lazy var bubbleViewRightConstraint2 = bubbleView.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor, constant: -VisibleMessageCell.gutterSize) + private lazy var messageStatusImageViewTopConstraint = messageStatusImageView.pin(.top, to: .bottom, of: bubbleView, withInset: 0) + private lazy var messageStatusImageViewWidthConstraint = messageStatusImageView.set(.width, to: VisibleMessageCell.messageStatusImageViewSize) + private lazy var messageStatusImageViewHeightConstraint = messageStatusImageView.set(.height, to: VisibleMessageCell.messageStatusImageViewSize) + + private var positionInCluster: Position? { + guard let viewItem = viewItem else { return nil } + if viewItem.isFirstInCluster { return .top } + if viewItem.isLastInCluster { return .bottom } + return .middle + } + + private var isOnlyMessageInCluster: Bool { viewItem?.isFirstInCluster == true && viewItem?.isLastInCluster == true } + + private var direction: Direction { + guard let message = viewItem?.interaction as? TSMessage else { preconditionFailure() } + switch message { + case is TSIncomingMessage: return .incoming + case is TSOutgoingMessage: return .outgoing + default: preconditionFailure() + } + } + + private var shouldInsetHeader: Bool { + guard let viewItem = viewItem else { preconditionFailure() } + return (positionInCluster == .top || isOnlyMessageInCluster) && !viewItem.wasPreviousItemInfoMessage + } + + // MARK: UI Components + private lazy var profilePictureView: ProfilePictureView = { + let result = ProfilePictureView() + let size = Values.verySmallProfilePictureSize + result.set(.height, to: size) + result.size = size + return result + }() + + lazy var bubbleView: UIView = { + let result = UIView() + result.layer.cornerRadius = VisibleMessageCell.smallCornerRadius + return result + }() + + private let bubbleViewMaskLayer = CAShapeLayer() + + private lazy var headerView = UIView() + + private lazy var authorLabel: UILabel = { + let result = UILabel() + result.font = .boldSystemFont(ofSize: Values.smallFontSize) + return result + }() + + private lazy var snContentView = UIView() + + private lazy var messageStatusImageView: UIImageView = { + let result = UIImageView() + result.contentMode = .scaleAspectFit + result.layer.cornerRadius = VisibleMessageCell.messageStatusImageViewSize / 2 + result.layer.masksToBounds = true + return result + }() + + // MARK: Settings + private static let messageStatusImageViewSize: CGFloat = 16 + private static let authorLabelBottomSpacing: CGFloat = 4 + private static let groupThreadHSpacing: CGFloat = 12 + private static let profilePictureSize = Values.verySmallProfilePictureSize + static let smallCornerRadius: CGFloat = 4 + static let largeCornerRadius: CGFloat = 18 + static let contactThreadHSpacing = Values.mediumSpacing + + static var gutterSize: CGFloat { groupThreadHSpacing + profilePictureSize + groupThreadHSpacing } + + private var bodyLabelTextColor: UIColor { + switch (direction, AppModeManager.shared.currentAppMode) { + case (.outgoing, .dark), (.incoming, .light): return .black + default: return .white + } + } + + override class var identifier: String { "VisibleMessageCell" } + + // MARK: Direction & Position + enum Direction { case incoming, outgoing } + enum Position { case top, middle, bottom } + + // MARK: Lifecycle + override func setUpViewHierarchy() { + super.setUpViewHierarchy() + isUserInteractionEnabled = true + // Header view + addSubview(headerView) + headerViewTopConstraint.isActive = true + headerView.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right ], to: self) + // Author label + addSubview(authorLabel) + authorLabel.pin(.top, to: .bottom, of: headerView) + // Profile picture view + addSubview(profilePictureView) + profilePictureViewLeftConstraint.isActive = true + profilePictureViewWidthConstraint.isActive = true + profilePictureView.pin(.bottom, to: .bottom, of: self, withInset: -1) + // Bubble view + addSubview(bubbleView) + bubbleViewLeftConstraint1.isActive = true + bubbleViewTopConstraint.isActive = true + bubbleViewRightConstraint1.isActive = true + // Content view + bubbleView.addSubview(snContentView) + snContentView.pin(to: bubbleView) + // Message status image view + addSubview(messageStatusImageView) + messageStatusImageViewTopConstraint.isActive = true + messageStatusImageView.pin(.right, to: .right, of: bubbleView, withInset: -1) + messageStatusImageView.pin(.bottom, to: .bottom, of: self, withInset: -1) + messageStatusImageViewWidthConstraint.isActive = true + messageStatusImageViewHeightConstraint.isActive = true + // Remaining constraints + authorLabel.pin(.left, to: .left, of: bubbleView, withInset: 12) + } + + override func setUpGestureRecognizers() { + let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) + addGestureRecognizer(longPressRecognizer) + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + tapGestureRecognizer.numberOfTapsRequired = 1 + addGestureRecognizer(tapGestureRecognizer) + let doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap)) + doubleTapGestureRecognizer.numberOfTapsRequired = 2 + addGestureRecognizer(doubleTapGestureRecognizer) + tapGestureRecognizer.require(toFail: doubleTapGestureRecognizer) + } + + // MARK: Updating + override func update() { + guard let viewItem = viewItem, let message = viewItem.interaction as? TSMessage else { return } + let thread = message.thread + let isGroupThread = thread.isGroupThread() + // Profile picture view + profilePictureViewLeftConstraint.constant = isGroupThread ? VisibleMessageCell.groupThreadHSpacing : 0 + profilePictureViewWidthConstraint.constant = isGroupThread ? VisibleMessageCell.profilePictureSize : 0 + let senderSessionID = (message as? TSIncomingMessage)?.authorId + profilePictureView.isHidden = !VisibleMessageCell.shouldShowProfilePicture(for: viewItem) + if let senderSessionID = senderSessionID { + profilePictureView.update(for: senderSessionID) + } + // Bubble view + bubbleViewLeftConstraint1.isActive = (direction == .incoming) + bubbleViewLeftConstraint1.constant = isGroupThread ? VisibleMessageCell.groupThreadHSpacing : VisibleMessageCell.contactThreadHSpacing + bubbleViewLeftConstraint2.isActive = (direction == .outgoing) + bubbleViewTopConstraint.constant = (viewItem.senderName == nil) ? 0 : VisibleMessageCell.authorLabelBottomSpacing + bubbleViewRightConstraint1.isActive = (direction == .outgoing) + bubbleViewRightConstraint2.isActive = (direction == .incoming) + bubbleView.backgroundColor = (direction == .incoming) ? Colors.receivedMessageBackground : Colors.sentMessageBackground + updateBubbleViewCorners() + // Content view + populateContentView(for: viewItem) + // Date break + headerViewTopConstraint.constant = shouldInsetHeader ? Values.mediumSpacing : 1 + headerView.subviews.forEach { $0.removeFromSuperview() } + if viewItem.shouldShowDate { + populateHeader(for: viewItem) + } + // Author label + authorLabel.textColor = Colors.text + authorLabel.isHidden = (viewItem.senderName == nil) + authorLabel.text = viewItem.senderName?.string // Will only be set if it should be shown + // Message status image view + let (image, backgroundColor) = getMessageStatusImage(for: message) + messageStatusImageView.image = image + messageStatusImageView.backgroundColor = backgroundColor + if let message = message as? TSOutgoingMessage { + messageStatusImageView.isHidden = (message.messageState == .sent && message.thread.lastInteraction != message) + } else { + messageStatusImageView.isHidden = true + } + messageStatusImageViewTopConstraint.constant = (messageStatusImageView.isHidden) ? 0 : 5 + [ messageStatusImageViewWidthConstraint, messageStatusImageViewHeightConstraint ].forEach { + $0.constant = (messageStatusImageView.isHidden) ? 0 : VisibleMessageCell.messageStatusImageViewSize + } + } + + private func populateHeader(for viewItem: ConversationViewItem) { + guard viewItem.shouldShowDate else { return } + let dateBreakLabel = UILabel() + dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize) + dateBreakLabel.textColor = Colors.text + dateBreakLabel.textAlignment = .center + let date = viewItem.interaction.receivedAtDate() + let description = DateUtil.formatDate(forConversationDateBreaks: date) + dateBreakLabel.text = description + headerView.addSubview(dateBreakLabel) + dateBreakLabel.pin(.top, to: .top, of: headerView, withInset: Values.smallSpacing) + let additionalBottomInset = shouldInsetHeader ? Values.mediumSpacing : 1 + headerView.pin(.bottom, to: .bottom, of: dateBreakLabel, withInset: Values.smallSpacing + additionalBottomInset) + dateBreakLabel.center(.horizontal, in: headerView) + } + + private func populateContentView(for viewItem: ConversationViewItem) { + snContentView.subviews.forEach { $0.removeFromSuperview() } + albumView = nil + mediaTextOverlayView = nil + let isOutgoing = (viewItem.interaction.interactionType() == .outgoingMessage) + switch viewItem.messageCellType { + case .textOnlyMessage: + guard let message = viewItem.interaction as? TSMessage else { return } + // Stack view + let stackView = UIStackView(arrangedSubviews: []) + stackView.axis = .vertical + // Quote label + if viewItem.quotedReply != nil { + let maxMessageWidth = VisibleMessageCell.getMaxWidth(for: viewItem) + let quoteView = QuoteView(for: viewItem, maxMessageWidth: maxMessageWidth) + stackView.addArrangedSubview(quoteView) + } + // Body label + let bodyLabel = UILabel() + bodyLabel.numberOfLines = 0 + bodyLabel.lineBreakMode = .byWordWrapping + bodyLabel.textColor = bodyLabelTextColor + bodyLabel.font = .systemFont(ofSize: getFontSize(for: viewItem)) + bodyLabel.attributedText = given(message.body) { MentionUtilities.highlightMentions(in: $0, isOutgoingMessage: isOutgoing, threadID: viewItem.interaction.uniqueThreadId, attributes: [:]) } + stackView.addArrangedSubview(bodyLabel) + // Constraints + snContentView.addSubview(stackView) + stackView.pin(to: snContentView, withInset: 12) + case .mediaMessage: + guard let cache = delegate?.getMediaCache() else { preconditionFailure() } + let maxMessageWidth = VisibleMessageCell.getMaxWidth(for: viewItem) + let albumView = MediaAlbumView(mediaCache: cache, items: viewItem.mediaAlbumItems!, isOutgoing: isOutgoing, maxMessageWidth: maxMessageWidth) + self.albumView = albumView + snContentView.addSubview(albumView) + let size = getSize(for: viewItem) + albumView.set(.width, to: size.width) + albumView.set(.height, to: size.height) + albumView.pin(to: snContentView) + albumView.loadMedia() + albumView.layer.mask = bubbleViewMaskLayer + if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0, + let delegate = delegate { // delegate should always be set at this point + let overlayView = MediaTextOverlayView(viewItem: viewItem, albumViewWidth: size.width, delegate: delegate) + self.mediaTextOverlayView = overlayView + snContentView.addSubview(overlayView) + overlayView.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.bottom, UIView.HorizontalEdge.right ], to: snContentView) + } + unloadContent = { albumView.unloadMedia() } + case .audio: + let voiceMessageView = VoiceMessageViewV2(viewItem: viewItem) + snContentView.addSubview(voiceMessageView) + voiceMessageView.pin(to: snContentView) + viewItem.lastAudioMessageView = voiceMessageView + case .genericAttachment: + let documentView = DocumentView(viewItem: viewItem, textColor: bodyLabelTextColor) + snContentView.addSubview(documentView) + documentView.pin(to: snContentView) + default: return + } + } + + override func layoutSubviews() { + super.layoutSubviews() + updateBubbleViewCorners() + } + + private func updateBubbleViewCorners() { + let maskPath = UIBezierPath(roundedRect: bubbleView.bounds, byRoundingCorners: getCornersToRound(), + cornerRadii: CGSize(width: VisibleMessageCell.largeCornerRadius, height: VisibleMessageCell.largeCornerRadius)) + bubbleViewMaskLayer.path = maskPath.cgPath + bubbleView.layer.mask = bubbleViewMaskLayer + } + + override func prepareForReuse() { + super.prepareForReuse() + unloadContent?() + } + + // MARK: Interaction + @objc private func handleLongPress() { + guard let viewItem = viewItem else { return } + delegate?.handleViewItemLongPressed(viewItem) + } + + @objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) { + guard let viewItem = viewItem else { return } + delegate?.handleViewItemTapped(viewItem, gestureRecognizer: gestureRecognizer) + } + + @objc private func handleDoubleTap() { + guard let viewItem = viewItem else { return } + delegate?.handleViewItemDoubleTapped(viewItem) + } + + // MARK: Convenience + private func getCornersToRound() -> UIRectCorner { + guard !isOnlyMessageInCluster else { return .allCorners } + let result: UIRectCorner + switch (positionInCluster, direction) { + case (.top, .outgoing): result = [ .bottomLeft, .topLeft, .topRight ] + case (.middle, .outgoing): result = [ .bottomLeft, .topLeft ] + case (.bottom, .outgoing): result = [ .bottomRight, .bottomLeft, .topLeft ] + case (.top, .incoming): result = [ .topLeft, .topRight, .bottomRight ] + case (.middle, .incoming): result = [ .topRight, .bottomRight ] + case (.bottom, .incoming): result = [ .topRight, .bottomRight, .bottomLeft ] + case (nil, _): result = .allCorners + } + return result + } + + private func getFontSize(for viewItem: ConversationViewItem) -> CGFloat { + let baselineFontSize = Values.mediumFontSize + switch viewItem.displayableBodyText?.jumbomojiCount { + case 1: return baselineFontSize + 30 + case 2: return baselineFontSize + 24 + case 3, 4, 5: return baselineFontSize + 18 + default: return baselineFontSize + } + } + + private func getMessageStatusImage(for message: TSMessage) -> (image: UIImage?, backgroundColor: UIColor?) { + guard let message = message as? TSOutgoingMessage else { return (nil, nil) } + let image: UIImage + var backgroundColor: UIColor? = nil + let status = MessageRecipientStatusUtils.recipientStatus(outgoingMessage: message) + switch status { + case .uploading, .sending: image = #imageLiteral(resourceName: "CircleDotDotDot").asTintedImage(color: Colors.text)! + case .sent, .skipped, .delivered: image = #imageLiteral(resourceName: "CircleCheck").asTintedImage(color: Colors.text)! + case .read: + backgroundColor = isLightMode ? .black : .white + image = isLightMode ? #imageLiteral(resourceName: "FilledCircleCheckLightMode") : #imageLiteral(resourceName: "FilledCircleCheckDarkMode") + case .failed: image = #imageLiteral(resourceName: "message_status_failed").asTintedImage(color: Colors.text)! + } + return (image, backgroundColor) + } + + + private func getSize(for viewItem: ConversationViewItem) -> CGSize { + guard let albumItems = viewItem.mediaAlbumItems else { preconditionFailure() } + let maxMessageWidth = VisibleMessageCell.getMaxWidth(for: viewItem) + let defaultSize = MediaAlbumView.layoutSize(forMaxMessageWidth: maxMessageWidth, items: albumItems) + guard albumItems.count == 1 else { return defaultSize } + // Honor the content aspect ratio for single media + let albumItem = albumItems.first! + let size = albumItem.mediaSize + guard size.width > 0 && size.height > 0 else { return defaultSize } + var aspectRatio = (size.width / size.height) + // Clamp the aspect ratio so that very thin/wide content still looks alright + let minAspectRatio: CGFloat = 0.35 + let maxAspectRatio = 1 / minAspectRatio + aspectRatio = aspectRatio.clamp(minAspectRatio, maxAspectRatio) + let maxSize = CGSize(width: maxMessageWidth, height: maxMessageWidth) + var width = with(maxSize.height * aspectRatio) { $0 > maxSize.width ? maxSize.width : $0 } + var height = (width > maxSize.width) ? (maxSize.width / aspectRatio) : maxSize.height + // Don't blow up small images unnecessarily + let minSize: CGFloat = 150 + let shortSourceDimension = min(size.width, size.height) + let shortDestinationDimension = min(width, height) + if shortDestinationDimension > minSize && shortDestinationDimension > shortSourceDimension { + let factor = minSize / shortDestinationDimension + width *= factor; height *= factor + } + return CGSize(width: width, height: height) + } + + static func getMaxWidth(for viewItem: ConversationViewItem) -> CGFloat { + let screen = UIScreen.main.bounds + switch viewItem.interaction.interactionType() { + case .outgoingMessage: return screen.width - contactThreadHSpacing - gutterSize + case .incomingMessage: + let leftGutterSize = shouldShowProfilePicture(for: viewItem) ? gutterSize : contactThreadHSpacing + return screen.width - leftGutterSize - gutterSize + default: preconditionFailure() + } + } + + private static func shouldShowProfilePicture(for viewItem: ConversationViewItem) -> Bool { + guard let message = viewItem.interaction as? TSMessage else { preconditionFailure() } + let isGroupThread = message.thread.isGroupThread() + let senderSessionID = (message as? TSIncomingMessage)?.authorId + return isGroupThread && viewItem.shouldShowSenderProfilePicture && senderSessionID != nil + } +} diff --git a/Session/Conversations/ConversationInputTextView.m b/Session/Conversations/ConversationInputTextView.m index 1a7e86721..b90aac58b 100644 --- a/Session/Conversations/ConversationInputTextView.m +++ b/Session/Conversations/ConversationInputTextView.m @@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN self.placeholderView = [UILabel new]; self.placeholderView.text = NSLocalizedString(@"Message", @""); - self.placeholderView.textColor = [LKColors.text colorWithAlphaComponent:LKValues.composeViewTextFieldPlaceholderOpacity]; + self.placeholderView.textColor = [LKColors.text colorWithAlphaComponent:LKValues.lowOpacity]; self.placeholderView.userInteractionEnabled = NO; [self addSubview:self.placeholderView]; diff --git a/Session/Conversations/ConversationInputToolbar.m b/Session/Conversations/ConversationInputToolbar.m index a397466ec..075cc2372 100644 --- a/Session/Conversations/ConversationInputToolbar.m +++ b/Session/Conversations/ConversationInputToolbar.m @@ -256,7 +256,7 @@ const CGFloat kMaxTextViewHeight = 120; self.borderView.backgroundColor = UIColor.clearColor; self.borderView.opaque = NO; self.borderView.layer.borderColor = LKColors.text.CGColor; - self.borderView.layer.opacity = LKValues.composeViewTextFieldBorderOpacity; + self.borderView.layer.opacity = LKValues.lowOpacity; self.borderView.layer.borderWidth = LKValues.composeViewTextFieldBorderThickness; self.borderView.layer.cornerRadius = vStackRounding; [self addSubview:self.borderView]; diff --git a/Session/Conversations/ConversationScrollButton.m b/Session/Conversations/ConversationScrollButton.m index 819b02220..6fe8a095c 100644 --- a/Session/Conversations/ConversationScrollButton.m +++ b/Session/Conversations/ConversationScrollButton.m @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN self.circleView = circleView; circleView.userInteractionEnabled = NO; circleView.layer.cornerRadius = circleSize * 0.5f; - circleView.layer.borderColor = [LKColors.text colorWithAlphaComponent:LKValues.composeViewTextFieldBorderOpacity].CGColor; + circleView.layer.borderColor = [LKColors.text colorWithAlphaComponent:LKValues.lowOpacity].CGColor; circleView.layer.borderWidth = LKValues.composeViewTextFieldBorderThickness; [circleView autoSetDimension:ALDimensionWidth toSize:circleSize]; [circleView autoSetDimension:ALDimensionHeight toSize:circleSize]; diff --git a/Session/Conversations/ConversationViewItem.h b/Session/Conversations/ConversationViewItem.h index 45f91a697..d2d316d20 100644 --- a/Session/Conversations/ConversationViewItem.h +++ b/Session/Conversations/ConversationViewItem.h @@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString *const SNAudioDidFinishPlayingNotification; + typedef NS_ENUM(NSInteger, OWSMessageCellType) { OWSMessageCellType_Unknown, OWSMessageCellType_TextOnlyMessage, @@ -23,7 +25,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @class ContactShareViewModel; @class ConversationViewCell; @class DisplayableText; -@class LKVoiceMessageView; +@class SNVoiceMessageView; @class OWSLinkPreview; @class OWSQuotedReplyModel; @class OWSUnreadIndicator; @@ -79,11 +81,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly) BOOL isExpiringMessage; @property (nonatomic) BOOL shouldShowDate; -@property (nonatomic) BOOL shouldShowSenderAvatar; +@property (nonatomic) BOOL shouldShowSenderProfilePicture; @property (nonatomic, nullable) NSAttributedString *senderName; @property (nonatomic) BOOL shouldHideFooter; @property (nonatomic) BOOL isFirstInCluster; +@property (nonatomic) BOOL isOnlyMessageInCluster; @property (nonatomic) BOOL isLastInCluster; +@property (nonatomic) BOOL wasPreviousItemInfoMessage; @property (nonatomic, nullable) OWSUnreadIndicator *unreadIndicator; @@ -98,7 +102,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); #pragma mark - Audio Playback -@property (nonatomic, weak) LKVoiceMessageView *lastAudioMessageView; +@property (nonatomic, weak) SNVoiceMessageView *lastAudioMessageView; @property (nonatomic, readonly) CGFloat audioDurationSeconds; @property (nonatomic, readonly) CGFloat audioProgressSeconds; diff --git a/Session/Conversations/ConversationViewItem.m b/Session/Conversations/ConversationViewItem.m index 75e2b9ceb..a6df0ce38 100644 --- a/Session/Conversations/ConversationViewItem.m +++ b/Session/Conversations/ConversationViewItem.m @@ -20,6 +20,8 @@ NS_ASSUME_NONNULL_BEGIN +NSString *const SNAudioDidFinishPlayingNotification = @"SNAudioDidFinishPlayingNotification"; + NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) { switch (cellType) { @@ -111,13 +113,15 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @implementation ConversationInteractionViewItem @synthesize shouldShowDate = _shouldShowDate; -@synthesize shouldShowSenderAvatar = _shouldShowSenderAvatar; +@synthesize shouldShowSenderProfilePicture = _shouldShowSenderProfilePicture; @synthesize unreadIndicator = _unreadIndicator; @synthesize didCellMediaFailToLoad = _didCellMediaFailToLoad; @synthesize interaction = _interaction; @synthesize isFirstInCluster = _isFirstInCluster; @synthesize isGroupThread = _isGroupThread; +@synthesize isOnlyMessageInCluster = _isOnlyMessageInCluster; @synthesize isLastInCluster = _isLastInCluster; +@synthesize wasPreviousItemInfoMessage = _wasPreviousItemInfoMessage; @synthesize lastAudioMessageView = _lastAudioMessageView; @synthesize senderName = _senderName; @synthesize shouldHideFooter = _shouldHideFooter; @@ -228,13 +232,13 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) [self clearCachedLayoutState]; } -- (void)setShouldShowSenderAvatar:(BOOL)shouldShowSenderAvatar +- (void)setShouldShowSenderAvatar:(BOOL)shouldShowSenderProfilePicture { - if (_shouldShowSenderAvatar == shouldShowSenderAvatar) { + if (_shouldShowSenderProfilePicture == shouldShowSenderProfilePicture) { return; } - _shouldShowSenderAvatar = shouldShowSenderAvatar; + _shouldShowSenderProfilePicture = shouldShowSenderProfilePicture; [self clearCachedLayoutState]; } @@ -446,7 +450,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.audioProgressSeconds = progress; - [self.lastAudioMessageView setProgress:progress / duration]; + [self.lastAudioMessageView setProgress:(int)(progress)]; } - (void)showInvalidAudioFileAlert @@ -458,6 +462,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @"Message for the alert indicating that an audio file is invalid.")]; } +- (void)audioPlayerDidFinishPlaying:(OWSAudioPlayer *)player successfully:(BOOL)flag +{ + if (!flag) { return; } + [NSNotificationCenter.defaultCenter postNotificationName:SNAudioDidFinishPlayingNotification object:nil]; +} + #pragma mark - Displayable Text // TODO: Now that we're caching the displayable text on the view items, diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index 3b9557422..729a98b86 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -1083,13 +1083,11 @@ static const int kYapDatabaseRangeMaxLength = 25000; [TSInteraction fetchObjectWithUniqueID:uniqueId transaction:transaction]; if (!interaction) { OWSFailDebug(@"missing interaction in message mapping: %@.", uniqueId); - // TODO: Add analytics. hasError = YES; continue; } if (!interaction.uniqueId) { OWSFailDebug(@"invalid interaction in message mapping: %@.", interaction); - // TODO: Add analytics. hasError = YES; continue; } @@ -1226,7 +1224,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; id viewItem = viewItems[i]; id _Nullable previousViewItem = (i > 0 ? viewItems[i - 1] : nil); id _Nullable nextViewItem = (i + 1 < viewItems.count ? viewItems[i + 1] : nil); - BOOL shouldShowSenderAvatar = NO; + BOOL shouldShowSenderProfilePicture = NO; BOOL shouldHideFooter = NO; BOOL isFirstInCluster = YES; BOOL isLastInCluster = YES; @@ -1322,9 +1320,8 @@ static const int kYapDatabaseRangeMaxLength = 25000; } if (viewItem.isGroupThread) { - // Show the sender name for incoming group messages unless - // the previous message has the same sender name and - // no "date break" separates us. + // Show the sender name for incoming group messages unless the + // previous message has the same sender and no "date break" separates us. BOOL shouldShowSenderName = YES; NSString *_Nullable previousIncomingSenderId = nil; if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) { @@ -1333,37 +1330,18 @@ static const int kYapDatabaseRangeMaxLength = 25000; previousIncomingSenderId = previousIncomingMessage.authorId; OWSAssertDebug(previousIncomingSenderId.length > 0); - shouldShowSenderName - = (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId] - || viewItem.hasCellHeader); + shouldShowSenderName = (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId] || viewItem.hasCellHeader); } if (shouldShowSenderName) { senderName = [[NSAttributedString alloc] initWithString:[SSKEnvironment.shared.profileManager profileNameForRecipientWithID:incomingSenderId avoidingWriteTransaction:YES]]; - - if ([self.thread isKindOfClass:[TSGroupThread class]]) { - TSGroupThread *groupThread = (TSGroupThread *)self.thread; - NSData *groupId = groupThread.groupModel.groupId; - NSString *stringGroupId = [[NSString alloc] initWithData:groupId encoding:NSUTF8StringEncoding]; - - if (stringGroupId != nil) { - NSString __block *displayName; - [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - displayName = [transaction objectForKey:incomingSenderId inCollection:stringGroupId]; - }]; - if (displayName != nil) { - senderName = [[NSAttributedString alloc] initWithString:displayName attributes:[OWSMessageBubbleView senderNamePrimaryAttributes]]; - } - } - } } - // Show the sender avatar for incoming group messages unless - // the next message has the same sender avatar and - // no "date break" separates us. - shouldShowSenderAvatar = YES; - if (previousViewItem && previousViewItem.interaction.interactionType == interactionType) { - shouldShowSenderAvatar = (![NSObject isNullableObject:previousIncomingSenderId equalTo:incomingSenderId]); + // Show the sender profile picture for incoming group messages unless the + // next message has the same sender and no "date break" separates us. + shouldShowSenderProfilePicture = YES; + if (nextViewItem && nextViewItem.interaction.interactionType == interactionType) { + shouldShowSenderProfilePicture = (![NSObject isNullableObject:nextIncomingSenderId equalTo:incomingSenderId]); } } } @@ -1374,9 +1352,10 @@ static const int kYapDatabaseRangeMaxLength = 25000; viewItem.isFirstInCluster = isFirstInCluster; viewItem.isLastInCluster = isLastInCluster; - viewItem.shouldShowSenderAvatar = shouldShowSenderAvatar; + viewItem.shouldShowSenderProfilePicture = shouldShowSenderProfilePicture; viewItem.shouldHideFooter = shouldHideFooter; viewItem.senderName = senderName; + viewItem.wasPreviousItemInfoMessage = (previousViewItem.interaction.interactionType == OWSInteractionType_Info); } self.viewState = [[ConversationViewState alloc] initWithViewItems:viewItems]; diff --git a/Session/Conversations/MenuActionsViewController.swift b/Session/Conversations/MenuActionsViewController.swift index 15ac746d3..165175f08 100644 --- a/Session/Conversations/MenuActionsViewController.swift +++ b/Session/Conversations/MenuActionsViewController.swift @@ -404,7 +404,7 @@ class MenuActionView: UIButton { var image = action.image image = image.withRenderingMode(.alwaysTemplate) let imageView = UIImageView(image: image) - imageView.tintColor = textColor.withAlphaComponent(Values.unimportantElementOpacity) + imageView.tintColor = textColor.withAlphaComponent(Values.mediumOpacity) let imageWidth: CGFloat = 24 imageView.autoSetDimensions(to: CGSize(width: imageWidth, height: imageWidth)) imageView.isUserInteractionEnabled = false @@ -417,7 +417,7 @@ class MenuActionView: UIButton { let subtitleLabel = UILabel() subtitleLabel.font = .systemFont(ofSize: Values.smallFontSize) - subtitleLabel.textColor = textColor.withAlphaComponent(Values.unimportantElementOpacity) + subtitleLabel.textColor = textColor.withAlphaComponent(Values.mediumOpacity) subtitleLabel.text = action.subtitle subtitleLabel.isUserInteractionEnabled = false diff --git a/Session/Conversations/Views & Cells/LinkPreviewView.swift b/Session/Conversations/Views & Cells/LinkPreviewView.swift index 1b765fb0f..a2e7d12ca 100644 --- a/Session/Conversations/Views & Cells/LinkPreviewView.swift +++ b/Session/Conversations/Views & Cells/LinkPreviewView.swift @@ -628,7 +628,7 @@ public class LinkPreviewView: UIStackView { displayDomain.count > 0 { let label = UILabel() label.text = displayDomain - label.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + label.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) label.font = .systemFont(ofSize: Values.verySmallFontSize) textStack.addArrangedSubview(label) } diff --git a/Session/Conversations/Views & Cells/OWSMessageBubbleView.m b/Session/Conversations/Views & Cells/OWSMessageBubbleView.m index 73384fe01..4e2533476 100644 --- a/Session/Conversations/Views & Cells/OWSMessageBubbleView.m +++ b/Session/Conversations/Views & Cells/OWSMessageBubbleView.m @@ -608,7 +608,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(match.range.length >= ConversationSearchController.kMinimumSearchTextLength); UIColor *highlightColor; if (LKAppModeUtilities.isLightMode) { - highlightColor = isOutgoingMessage ? UIColor.whiteColor : [LKColors.accent colorWithAlphaComponent:LKValues.unimportantElementOpacity]; + highlightColor = isOutgoingMessage ? UIColor.whiteColor : [LKColors.accent colorWithAlphaComponent:LKValues.mediumOpacity]; } else { highlightColor = UIColor.whiteColor; } @@ -637,7 +637,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(self.senderNameLabel); OWSAssertDebug(self.shouldShowSenderName); - self.senderNameLabel.textColor = [LKColors.text colorWithAlphaComponent:LKValues.unimportantElementOpacity]; + self.senderNameLabel.textColor = [LKColors.text colorWithAlphaComponent:LKValues.mediumOpacity]; self.senderNameLabel.font = OWSMessageBubbleView.senderNameFont; self.senderNameLabel.text = self.viewItem.senderName.string; self.senderNameLabel.lineBreakMode = NSLineBreakByTruncatingTail; @@ -695,12 +695,11 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertDebug(self.viewItem.mediaAlbumItems); - OWSMediaAlbumCellView *albumView = - [[OWSMediaAlbumCellView alloc] initWithMediaCache:self.cellMediaCache + OWSMediaAlbumView *albumView = + [[OWSMediaAlbumView alloc] initWithMediaCache:self.cellMediaCache items:self.viewItem.mediaAlbumItems isOutgoing:self.isOutgoing - maxMessageWidth:self.conversationStyle.maxMessageWidth - isOnionRouted:YES]; + maxMessageWidth:self.conversationStyle.maxMessageWidth]; self.loadCellContentBlock = ^{ [albumView loadMedia]; }; @@ -729,12 +728,14 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(attachment); OWSAssertDebug([attachment isAudio]); - LKVoiceMessageView *voiceMessageView = [[LKVoiceMessageView alloc] initWithVoiceMessage:attachment isOutgoing:self.isOutgoing]; - [voiceMessageView setDuration:(int)self.viewItem.audioDurationSeconds]; - [voiceMessageView setProgress:self.viewItem.audioProgressSeconds / self.viewItem.audioDurationSeconds]; - [voiceMessageView initialize]; + UIView *voiceMessageView = [UIView new]; - self.viewItem.lastAudioMessageView = voiceMessageView; +// LKVoiceMessageView *voiceMessageView = [[LKVoiceMessageView alloc] initWithVoiceMessage:attachment isOutgoing:self.isOutgoing]; +// [voiceMessageView setDuration:(int)self.viewItem.audioDurationSeconds]; +// [voiceMessageView setProgress:self.viewItem.audioProgressSeconds / self.viewItem.audioDurationSeconds]; +// [voiceMessageView initialize]; + +// self.viewItem.lastAudioMessageView = voiceMessageView; self.loadCellContentBlock = ^{ // Do nothing. @@ -946,7 +947,7 @@ NS_ASSUME_NONNULL_BEGIN break; } case OWSMessageCellType_MediaMessage: - result = [OWSMediaAlbumCellView layoutSizeForMaxMessageWidth:maxMessageWidth + result = [OWSMediaAlbumView layoutSizeForMaxMessageWidth:maxMessageWidth items:self.viewItem.mediaAlbumItems]; if (self.viewItem.mediaAlbumItems.count == 1) { @@ -1314,19 +1315,19 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(self.bodyMediaView); OWSAssertDebug(self.viewItem.mediaAlbumItems.count > 0); - if (![self.bodyMediaView isKindOfClass:[OWSMediaAlbumCellView class]]) { + if (![self.bodyMediaView isKindOfClass:[OWSMediaAlbumView class]]) { OWSFailDebug(@"Unexpected body media view: %@", self.bodyMediaView.class); return; } - OWSMediaAlbumCellView *_Nullable mediaAlbumCellView = (OWSMediaAlbumCellView *)self.bodyMediaView; + OWSMediaAlbumView *_Nullable MediaAlbumView = (OWSMediaAlbumView *)self.bodyMediaView; CGPoint location = [self convertPoint:locationInMessageBubble toView:self.bodyMediaView]; - OWSConversationMediaView *_Nullable mediaView = [mediaAlbumCellView mediaViewForLocation:location]; + OWSMediaView *_Nullable mediaView = [MediaAlbumView mediaViewForLocation:location]; if (!mediaView) { OWSFailDebug(@"Missing media view."); return; } - if ([mediaAlbumCellView isMoreItemsViewWithMediaView:mediaView] + if ([MediaAlbumView isMoreItemsViewWithMediaView:mediaView] && self.viewItem.mediaAlbumHasFailedAttachment) { [self.delegate didTapFailedIncomingAttachment:self.viewItem]; return; @@ -1355,12 +1356,12 @@ NS_ASSUME_NONNULL_BEGIN { switch (self.cellType) { case OWSMessageCellType_Audio: { - LKVoiceMessageView *voiceMessageView = self.viewItem.lastAudioMessageView; - NSTimeInterval currentTime = [voiceMessageView getCurrentTime:sender]; - [self.viewItem setAudioProgress:((CGFloat)currentTime) duration:self.viewItem.audioDurationSeconds]; - CGFloat progress = self.viewItem.audioProgressSeconds / self.viewItem.audioDurationSeconds; - [voiceMessageView setProgress:progress]; - [self.delegate didPanAudioViewItemToCurrentTime:currentTime]; +// SNVoiceMessageView *voiceMessageView = self.viewItem.lastAudioMessageView; +// NSTimeInterval currentTime = [voiceMessageView getCurrentTime:sender]; +// [self.viewItem setAudioProgress:((CGFloat)currentTime) duration:self.viewItem.audioDurationSeconds]; +// CGFloat progress = self.viewItem.audioProgressSeconds / self.viewItem.audioDurationSeconds; +// [voiceMessageView setProgress:progress]; +// [self.delegate didPanAudioViewItemToCurrentTime:currentTime]; return; } default: return; diff --git a/Session/Conversations/Views & Cells/OWSMessageCell.m b/Session/Conversations/Views & Cells/OWSMessageCell.m index 7cbafcef3..79b8d81a3 100644 --- a/Session/Conversations/Views & Cells/OWSMessageCell.m +++ b/Session/Conversations/Views & Cells/OWSMessageCell.m @@ -265,7 +265,7 @@ NS_ASSUME_NONNULL_BEGIN // Returns YES IFF the avatar view is appropriate and configured. - (BOOL)updateAvatarView { - if (!self.viewItem.shouldShowSenderAvatar) { + if (!self.viewItem.shouldShowSenderProfilePicture) { return NO; } if (!self.viewItem.isGroupThread) { @@ -314,7 +314,7 @@ NS_ASSUME_NONNULL_BEGIN { OWSAssertIsOnMainThread(); - if (!self.viewItem.shouldShowSenderAvatar) { + if (!self.viewItem.shouldShowSenderProfilePicture) { return; } if (!self.viewItem.isGroupThread) { @@ -496,7 +496,7 @@ NS_ASSUME_NONNULL_BEGIN -(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { - LKVoiceMessageView *voiceMessageView = self.viewItem.lastAudioMessageView; + SNVoiceMessageView *voiceMessageView = self.viewItem.lastAudioMessageView; if (![gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class] || voiceMessageView == nil) { return NO; } UIPanGestureRecognizer *panGestureRecognizer = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint location = [panGestureRecognizer locationInView:voiceMessageView]; diff --git a/Session/Conversations/Views & Cells/OWSQuotedMessageView.m b/Session/Conversations/Views & Cells/OWSQuotedMessageView.m index 604363c7f..7a6d11444 100644 --- a/Session/Conversations/Views & Cells/OWSQuotedMessageView.m +++ b/Session/Conversations/Views & Cells/OWSQuotedMessageView.m @@ -383,7 +383,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4; kRemotelySourcedContentRowMargin, kRemotelySourcedContentRowMargin); - UIColor *backgroundColor = LKAppModeUtilities.isLightMode ? [UIColor.whiteColor colorWithAlphaComponent:LKValues.unimportantElementOpacity] : [LKColors.text colorWithAlphaComponent:LKValues.unimportantElementOpacity]; + UIColor *backgroundColor = LKAppModeUtilities.isLightMode ? [UIColor.whiteColor colorWithAlphaComponent:LKValues.mediumOpacity] : [LKColors.text colorWithAlphaComponent:LKValues.mediumOpacity]; [sourceRow addBackgroundViewWithBackgroundColor:backgroundColor]; return sourceRow; diff --git a/Session/Conversations/Views & Cells/VoiceMessageView.swift b/Session/Conversations/Views & Cells/VoiceMessageView.swift index 85f69a7af..35fc3ba73 100644 --- a/Session/Conversations/Views & Cells/VoiceMessageView.swift +++ b/Session/Conversations/Views & Cells/VoiceMessageView.swift @@ -13,7 +13,7 @@ final class VoiceMessageView : UIView { @objc var isPlaying = false { didSet { updateToggleImageView() } } // MARK: Components - private lazy var toggleImageView = UIImageView(image: #imageLiteral(resourceName: "Play")) + private lazy var toggleImageView = UIImageView(image: UIImage(named: "Play")) private lazy var spinner = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: .black, padding: nil) diff --git a/Session/Conversations/Views & Cells/VoiceNoteLock.swift b/Session/Conversations/Views & Cells/VoiceNoteLock.swift index a94030c04..495dc9c4d 100644 --- a/Session/Conversations/Views & Cells/VoiceNoteLock.swift +++ b/Session/Conversations/Views & Cells/VoiceNoteLock.swift @@ -71,7 +71,7 @@ public class VoiceMemoLockView: UIView { view.autoSetDimension(.width, toSize: width) view.backgroundColor = Colors.composeViewBackground view.layer.cornerRadius = width / 2 - view.layer.borderColor = Colors.text.withAlphaComponent(Values.composeViewTextFieldBorderOpacity).cgColor + view.layer.borderColor = Colors.text.withAlphaComponent(Values.lowOpacity).cgColor view.layer.borderWidth = Values.composeViewTextFieldBorderThickness return view diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index 04994c47e..5daa7138a 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -36,7 +36,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIViewC result.backgroundColor = .clear result.separatorStyle = .none result.register(ConversationCell.self, forCellReuseIdentifier: ConversationCell.reuseIdentifier) - let bottomInset = Values.newConversationButtonBottomOffset + Values.newConversationButtonExpandedSize + Values.largeSpacing + Values.newConversationButtonCollapsedSize + let bottomInset = Values.newConversationButtonBottomOffset + NewConversationButtonSet.expandedButtonSize + Values.largeSpacing + NewConversationButtonSet.collapsedButtonSize result.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: bottomInset, right: 0) result.showsVerticalScrollIndicator = false return result @@ -342,8 +342,8 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIViewC if let presentedVC = self.presentedViewController { presentedVC.dismiss(animated: false, completion: nil) } - let conversationVC = ConversationViewController() - conversationVC.configure(for: thread, action: action, focusMessageId: highlightedMessageID) + let conversationVC = ConversationVC(thread: thread) +// conversationVC.configure(for: thread, action: action, focusMessageId: highlightedMessageID) self.navigationController?.setViewControllers([ self, conversationVC ], animated: true) } } @@ -424,7 +424,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIViewC } @objc func joinOpenGroup() { - let joinOpenGroupVC = JoinPublicChatVC() + let joinOpenGroupVC = JoinOpenGroupVC() let navigationController = OWSNavigationController(rootViewController: joinOpenGroupVC) present(navigationController, animated: true, completion: nil) } diff --git a/Session/Home/NewConversationButtonSet.swift b/Session/Home/NewConversationButtonSet.swift index 4f3b31234..b6f03a221 100644 --- a/Session/Home/NewConversationButtonSet.swift +++ b/Session/Home/NewConversationButtonSet.swift @@ -12,6 +12,8 @@ final class NewConversationButtonSet : UIView { private let iconSize = CGFloat(24) private let maxDragDistance = CGFloat(56) private let dragMargin = CGFloat(16) + static let collapsedButtonSize = CGFloat(60) + static let expandedButtonSize = CGFloat(72) // MARK: Components private lazy var mainButton = NewConversationButton(isMainButton: true, icon: #imageLiteral(resourceName: "Plus").scaled(to: CGSize(width: iconSize, height: iconSize))) @@ -39,7 +41,7 @@ final class NewConversationButtonSet : UIView { createNewClosedGroupButton.isAccessibilityElement = true joinOpenGroupButton.accessibilityLabel = "Join open group button" joinOpenGroupButton.isAccessibilityElement = true - let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 + let inset = (NewConversationButtonSet.expandedButtonSize - NewConversationButtonSet.collapsedButtonSize) / 2 addSubview(joinOpenGroupButton) horizontalButtonConstraints[joinOpenGroupButton] = joinOpenGroupButton.pin(.left, to: .left, of: self, withInset: inset) verticalButtonConstraints[joinOpenGroupButton] = joinOpenGroupButton.pin(.bottom, to: .bottom, of: self, withInset: -inset) @@ -52,9 +54,9 @@ final class NewConversationButtonSet : UIView { addSubview(mainButton) mainButton.center(.horizontal, in: self) mainButton.pin(.bottom, to: .bottom, of: self, withInset: -inset) - let width = 2 * Values.newConversationButtonExpandedSize + 2 * spacing + Values.newConversationButtonCollapsedSize + let width = 2 * NewConversationButtonSet.expandedButtonSize + 2 * spacing + NewConversationButtonSet.collapsedButtonSize set(.width, to: width) - let height = Values.newConversationButtonExpandedSize + spacing + Values.newConversationButtonCollapsedSize + let height = NewConversationButtonSet.expandedButtonSize + spacing + NewConversationButtonSet.collapsedButtonSize set(.height, to: height) collapse(withAnimation: false) isUserInteractionEnabled = true @@ -75,8 +77,8 @@ final class NewConversationButtonSet : UIView { let buttons = [ joinOpenGroupButton, createNewPrivateChatButton, createNewClosedGroupButton ] UIView.animate(withDuration: 0.25, animations: { buttons.forEach { $0.alpha = 1 } - let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 - let size = Values.newConversationButtonCollapsedSize + let inset = (NewConversationButtonSet.expandedButtonSize - NewConversationButtonSet.collapsedButtonSize) / 2 + let size = NewConversationButtonSet.collapsedButtonSize self.joinOpenGroupButton.frame = CGRect(origin: CGPoint(x: inset, y: self.height() - size - inset), size: CGSize(width: size, height: size)) self.createNewPrivateChatButton.frame = CGRect(center: CGPoint(x: self.bounds.center.x, y: inset + size / 2), size: CGSize(width: size, height: size)) self.createNewClosedGroupButton.frame = CGRect(origin: CGPoint(x: self.width() - size - inset, y: self.height() - size - inset), size: CGSize(width: size, height: size)) @@ -91,14 +93,14 @@ final class NewConversationButtonSet : UIView { UIView.animate(withDuration: isAnimated ? 0.25 : 0) { buttons.forEach { button in button.alpha = 0 - let size = Values.newConversationButtonCollapsedSize + let size = NewConversationButtonSet.collapsedButtonSize button.frame = CGRect(center: self.mainButton.center, size: CGSize(width: size, height: size)) } } } private func reset() { - let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - Values.newConversationButtonExpandedSize / 2) + let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - NewConversationButtonSet.expandedButtonSize / 2) let mainButtonSize = mainButton.frame.size UIView.animate(withDuration: 0.25) { self.mainButton.frame = CGRect(center: mainButtonLocationInSelfCoordinates, size: mainButtonSize) @@ -120,7 +122,7 @@ final class NewConversationButtonSet : UIView { override func touchesMoved(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first, isUserDragging else { return } let mainButtonSize = mainButton.frame.size - let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - Values.newConversationButtonExpandedSize / 2) + let mainButtonLocationInSelfCoordinates = CGPoint(x: width() / 2, y: height() - NewConversationButtonSet.expandedButtonSize / 2) let touchLocationInSelfCoordinates = touch.location(in: self) mainButton.frame = CGRect(center: touchLocationInSelfCoordinates, size: mainButtonSize) mainButton.alpha = 1 - (touchLocationInSelfCoordinates.distance(to: mainButtonLocationInSelfCoordinates) / maxDragDistance) @@ -159,7 +161,7 @@ final class NewConversationButtonSet : UIView { private func expand(_ button: NewConversationButton) { if let horizontalConstraint = horizontalButtonConstraints[button] { horizontalConstraint.constant = 0 } if let verticalConstraint = verticalButtonConstraints[button] { verticalConstraint.constant = 0 } - let size = Values.newConversationButtonExpandedSize + let size = NewConversationButtonSet.expandedButtonSize let frame = CGRect(center: button.center, size: CGSize(width: size, height: size)) button.widthConstraint.constant = size button.heightConstraint.constant = size @@ -167,7 +169,7 @@ final class NewConversationButtonSet : UIView { self.layoutIfNeeded() button.frame = frame button.layer.cornerRadius = size / 2 - let glowColor = Colors.newConversationButtonShadow + let glowColor = Colors.expandedButtonGlowColor let glowConfiguration = UIView.CircularGlowConfiguration(size: size, color: glowColor, isAnimated: true, radius: isLightMode ? 4 : 6) button.setCircularGlow(with: glowConfiguration) button.backgroundColor = Colors.accent @@ -175,7 +177,7 @@ final class NewConversationButtonSet : UIView { } private func collapse(_ button: NewConversationButton) { - let inset = (Values.newConversationButtonExpandedSize - Values.newConversationButtonCollapsedSize) / 2 + let inset = (NewConversationButtonSet.expandedButtonSize - NewConversationButtonSet.collapsedButtonSize) / 2 if joinOpenGroupButton == expandedButton { horizontalButtonConstraints[joinOpenGroupButton]!.constant = inset verticalButtonConstraints[joinOpenGroupButton]!.constant = -inset @@ -185,7 +187,7 @@ final class NewConversationButtonSet : UIView { horizontalButtonConstraints[createNewClosedGroupButton]!.constant = -inset verticalButtonConstraints[createNewClosedGroupButton]!.constant = -inset } - let size = Values.newConversationButtonCollapsedSize + let size = NewConversationButtonSet.collapsedButtonSize let frame = CGRect(center: button.center, size: CGSize(width: size, height: size)) button.widthConstraint.constant = size button.heightConstraint.constant = size @@ -249,9 +251,9 @@ private final class NewConversationButton : UIImageView { private func setUpViewHierarchy(isUpdate: Bool = false) { let newConversationButtonCollapsedBackground = isLightMode ? UIColor(hex: 0xF5F5F5) : UIColor(hex: 0x1F1F1F) backgroundColor = isMainButton ? Colors.accent : newConversationButtonCollapsedBackground - let size = Values.newConversationButtonCollapsedSize + let size = NewConversationButtonSet.collapsedButtonSize layer.cornerRadius = size / 2 - let glowColor = isMainButton ? Colors.newConversationButtonShadow : (isLightMode ? UIColor.black.withAlphaComponent(0.4) : UIColor.black) + let glowColor = isMainButton ? Colors.expandedButtonGlowColor : (isLightMode ? UIColor.black.withAlphaComponent(0.4) : UIColor.black) let glowConfiguration = UIView.CircularGlowConfiguration(size: size, color: glowColor, isAnimated: false, radius: isLightMode ? 4 : 6) setCircularGlow(with: glowConfiguration) layer.masksToBounds = false diff --git a/Session/Meta/Images.xcassets/Contents.json b/Session/Meta/Images.xcassets/Contents.json index da4a164c9..73c00596a 100644 --- a/Session/Meta/Images.xcassets/Contents.json +++ b/Session/Meta/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Contents.json b/Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Contents.json deleted file mode 100644 index 94f23044f..000000000 --- a/Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Gear.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Gear.pdf b/Session/Meta/Images.xcassets/Loki V2/Gear.imageset/Gear.pdf deleted file mode 100644 index 751543bcd5f9739217b8fab822d519df39bd455e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7822 zcmbuEbyQT{_y0*r2|*eek?x_9?(Q1e85l}nq;n_<=@Kac>5@~SP=j^@LnfG~JdJP#lb|422Hhufr;o5e=(W{~MPHbKP7XSva z#TF3(aH>EZtX!-CdU;AFeyN<_% z$1ewkUohr9%%6wADg3m24Kb5XynW=#Pm`LG6lczkHG9AA=9AQ^@J$$6Z01;zYpH{0 z--u?7*)~8TgnG2SN2neY>oQeV<0z-+rRI=1Rr$i7kM)g`^;-^QXc}5ZAN24YJMjd6 zfBxF0%WBY2d)7k|*3q@A*R>msiP0*AHa()c1oP09%!G z!UQ$vMAzH$Z86$kHPrecf8MLu0y`p&hwN4$2?rc00$rDj?h0gk+@{qtd?!g7aOz=w zCzO2%i%K2qouGXWX3%Spw<&-u;4GYPrGMiQ=KK!RqKX@2bR47)Sun%7&(UXVEmShD zn@Q%37ZT{rG9+bf&zFK;EnJBowjxs#^~jf2-s%`1EtAm5`Upiyo{Jsl$`q|;7jWif zi22F7`H+5=N>U|!Rl6?7g}jx{E-bfMgac zjJj4fGE8H}CzfOes!oeKBfFz=jJL~8dF#4wIZNHvSbJrSuI%XZQDlVYi7mPx5;;;5 z{8Z@HM8?)vDEn0Lz))U6-*;3ESjtNC$O<44srR+)%eR4jTQVca&k`pzlx{_#6O>v~ z1|1;LsZ;GZT#-ls%q&|kPjNJS1z!gnU!vq*kv_$ERmE;L#V;}AhV(GQP8nIDEh-*; zaoPkFcUuV4e#VtR>qKCf`gJ?m;X$ktb$v&y-N;9n z3eJ6%vP6um2QL6)gfa<|q{{CDw+U5;joFx~q|~WkA}JtK&_%cDK`n^IJeQ=}|49Fi z4y3}gERn-l&@oFV3E$xIWK#dwl$?&J%y4)6`<|B?6>65kSGoQK1=^rK71CSEu(!`Y zg1v8-y;^M@m8tB>wVq01Pw>OSr_ABTv(GAic88!?(l!jsJaH8ehwNGsa>m~y$6GUM zr3^z}osOLByby2ZTwJU2T*RpSi?o@>5kG?~<$pi;GB zapz!#pq?>GA$}(DrF*;zBbaLWFSGG*o{LC^IgM81eoSzUrMmc9Eoqh598IZJ)IML_ z#ZhF568vORaVtkR&p#bxYz>vv^{N+RH+3yuaOm)o_n(;@)%ZG@_?`$#-Bu()X9F*y zcq9l3V9vr#S&Kh*r0<}EtOC$kf@67|Gn`feKD2fgAD8)usX4N+e(;=F@(EEeCJdPu zta7K5k5aOU7ZNXiYY{8=b}@0VdMPc^3`dt^1!m+$*K$V(@X}FB0uFK;@v(mbf-oF* z5g9z!oYeT99mivU;~J={MudEB`B7&y@6F`f>4EIa`;yGHw3ts_@gS1^Er~2MO!OQN z1V|J}7AcLCGU!$B*adbg$|?{SsntZK2eUZ}geAXx7$Npr6a2_%>`kRJ$+Bai@QbMQ zwf1pa=Pr+4BFdFTAZpi|e}M_>ST2yp_))gz_Ty6$%>ryel5`@C@YhFE$()RV&Th5Z zb>l6BP`r!_R}NiOM$0&>L`=rS$S2*XOANcBi-fj^QmnOxiv}ICg{4Z$=DT?Lv;J zDrB(eND{`wPwH$}q81T`5Q||CqTuV#wh5XC+5{9_q?U{{zil36=A7-?UtJtlU4yLG zMxX_Bp()_yyJL{Da zvi8sgiO0tnxB9t7INAJVuY*f-PNEQ`1ux^RcpKru?R(R*{HdG!Z2KT?mX>F_^c7FL z;)U-PMwbUL>eGpp^lBo{N%Y^X8B?(ZwdUZU z_uQ98u+5XFpX#VqRth=q-qBx~M9(WkZj_(i<1dTgjP#_G6I*7boUGCJaWz_(f*!6e zm(1RK-*)k-NcCIv05q(ggfN_;vFc-G{y{h=iN%XdQ(Qq(Gk^NqEL_Sy9jvyn?5*Wq z(iPnrfr0ukPJWCDxBd>&G$K|tx<)dpDRa<=t&55oJ7dR&Sq%fSGXCWDB#f@{lTRH< z-_^)zOnlZHugNNX52s1Y%ER_%Y{T}pt;5P=NOT6@B9WR+N9w((M?@R-YEN2M+nxq| ziUs8x!*`amU3ZT$O4Bfs9St{6!B6$BzPPcaO^}Lm;x*XKe$H5q^P~lsio|nikorED zsy+q7wOEeU*P~Njy0_XkExutm%FdqoZZhH0gjG3rJT=fZm^mR>-{Cr&nZHM@LzY?N z9vm=Rqo?q_Y!-)fT@k{U^ zKHcb~8*y~=&dn_#cysl~=RY)*j^`67fKvl(_2cXWb#MXj{M1ugP-mE{lR4BG!2i=A z4RdfoTz3ZCXfUlCZS^bO&Gp}!Ov4Fgt_^ho7$Jhn$N@|MoFH|GE!5lvaHH7d?12D* zUw8iz;KuYjz%NlJ_`~ZzMIGnQG5?OssqG4J`7sMQn3Fx?HxTC=4{RWSQ%VZv0Wf0c z10W6_F2sQt&;RBn^z%35onWp{ehg0=z^UT|c5r?IMkK=A6Tm604FGc8cwuuY0$iM2 z5lK=8Sc2`GZ=~hl)FrD9rVf)17n0NiZ2}f0T}=IMZLT36?QWeRcMg6n1&&+FluX2o zte;Z@V}WGIy7`iPs9)EdB#$@zae*4$$lJ(q5)RQ)AyTy@-8z^wOhMxm(&z+iYU{1$ zx5rXsjM{=XDU|jpX zPr&BT9~qE#<^}+PKl1W(g1<@(l&m#J2-Nc>_3Qpl&wiP%U|> z|67x%zi`*lo+b$$Y!GM|xB+%$=(Ug&0n~QvmwO7h_G@p9aWw0>4Q8PJhwFjT9L(=#bh}P-? zLiXp)0ug`NZZW?ul2WM9S$Xn>!{4HQhOJ41 z!Fl4^FOF6~n(h`7w(K~+h%W=_8p?C+$_{ewYqXEYiOk#~KyH#OtnAmcEA8cWCk5-Q z^*zZ+469ib1_R_DcrzmFHRe<4mvSP}vW%1I7p-!9FrW=Oim6Czr%a=xXD?NdxQJ8C z`ZgG{?83NZiOIi&9ikv%K5rNL;Di@(E@Yj7R%3fA&X3JV4q?gw_8fJyK6A9ZhhV~y>QQ0`%1Li>r(fC zVP`NyGBU$p!EWa@X9Ee+37ugYb!B6sE>e~KEs3?I*Xnx!q(gM1s`-~k_d@JUHTu4C!9|z9`Hc%E)P_8Ex#aNS*EWK7cXhh z;_8&(dZk&&roX3+?PX>&=|S*kUCqzWv4X;^>w`rCK(QPa*g<~dOMYbhj2Ps;>5qx6 zUCof-!9p6fiWuE6Q6%XHsB{kEUMTQwBLX0HcjE`^(ztTx7;dlc8y7S<#^DZ2Dw4F? z>nFG|7EbipY~(o$L3S-DcVaPIUSabFaB!ipJ-SWh&+`Oz9)J|&uRwtM0+mAn#YO_{ zt)$o#61F683fb+c?GvQ20Iwl5wLtzSw~W#Bn{PkC!S$Dyzz%6q=0ctiu)z%6C%Bs} zSsY7}i=QGjK|pDU=_xI#AxMncAsrS=5HE2)#861a^0YWhT0`{sfeS?)rpeQbR~SP8 z-dk75@F2oulu_{QDWq!u=?Y;gO!?N{MH!E~FEL1)XO@&|NyPAKT1l5E_mS9w%~}&M zCAu+Dhvh(+lm{|Wu^>8R!uyZoXme4DWK(1Daw*?ODa1LhqTUM40w(d z@Vc7Gm#Lok){+@>JMfjE)!4a}<`!d70*fI@W_+7JTIc4G+ho9HRY4~kR_Y6--O zA#|i#bj&GL0XmAdv?dIq3=Q`adLCNHJCPYkn5vi2Wdh9T@#Doco-;8Jt1D5-N=>T? zDZQ8Ql<<_!)4E;gFa|14u1hymZ;*j2!8zp7hnzOlwqY6l;|q zwQjX?Jv!sjBF~FVh^&u{IpSS^Ke#eD!(VP=di{Yp@tnnh#V3(A(J~P^F{W5W7dB2j zo?dKUtUktN%U}z(owmIk=O~F9EEsgp7)&?gyZpZ=2d%P`?YOLbSr@(m7AJWA;70c z5t3;wg<0WQP2dc0&XW0q;<%DwmK?(z;Z8yGYRB*cu@xhRim-IWc*Uq5Rqd&9S zfP7IB8p^xgbZ$;Xrz3O9p({Jf*ehQ+c#L0E?tF53w^r0USQcy6V&KSfkCKLZf#5of zsI_QKrR>=3z55#4WI}!DW&B1b>qgXp$rXPpk zUEi7=2CNo6kA0rQ%u_U6)Nd11byhVhMGp?IU^U?pWo+PV+{zaE4TVgm) zg~{2#vOYQbyUJIENV+uorv^Bcwr}UH;p!4b{%$i?RMuZD@$!>l4wnbze0MK!GV+9qDkpm-(mJ)O}+k3qaN7E@e#gFj73Zpcj4OxrE**O z@q(?~tpJLc)b)lf@0#88PhB&UO$+vZqsURH9T;Nwh^apKb)RZHE|s+J0M9PHZX4Xr z-l}V|@-y?3*;P1*80&+BT9aUB;Pn-nM%c+TG%72o-0#jy)^wfI%qo2X<{gD2Vb;X#L|An$zm2zw_rj+CXgc)(HM@A) zwa;`>iP7zm{FKiULm^iWDu=u<>>Ta`_Ul2ZD(kOlsaFumw ze%uwexubrjo|qXW?(T#2?a+h4-g156H0bE^-;(!-480M!-26c9-=g=2hP^Q&_92SW z(o$e&s0HAMh}8m^{N?l?Fd6uZCjWnk+F)IyXOy~R-xGv-udZO|D19yC-Sj(=OjKpKrx%3&o*zM`V-czzUDbpX$VKnMhjnOX>$plo| z&Q*oo3TaWNso6$}pbAl?!~B-ryv83A6+$|5#wVXJ*zrmThpi-0iqHey!{<#X=joyw zo<_5JJmu_|j_fh*;|ZnI0cvUQ_o46QJq2oW1vDy;W&&KhiwCGXU2)$vX|c7VXP}vC z;)Dq;%KMk|y$e>sSSXtrrrI}``JNuN<%8xS@TAz{-b;)SnkECA+`6-L?Zj`Di!}k) zouEFd%^ef%QZg?^;g*H6{i@HfX9`-p!s!U#O@D)4EK#ReCE0uGNHM5Qrlr&*Ld^xo zDdR7RK6G2{>d-SB5`SAu!VZ1goYUOoB+7jJjQ+}~rZSxh)IG#Q%K>>KSo6|rDKvOT zNeum-V~RuC`G9G)@d#eN@P#^yrP4lR?_J%OJ|-uevwk_Xv1m|0Phg4yN}Cm+H_CHZ zJs_=Wk(Ho;f*qpU^Ga2VNnSC0*^rUw#T}d6r%M%1TvYBR{Xvz3*(@GWsnPwdsjkm= z(@jdHpcS`F+^bkRq-ZozGgyu>roy+iv&g+AW(M=8 zahZ3RPk4o!mmgV8iA}nbc-P$x^Kk$u%?$QF>!zMVv2A)rVbAjD3O$^{pe(epj4g)0 zQ>2&t2wk<|uyaAQVWgsRi-qChq1URZDE>E++Iygmx_eVk;7kFTD3oVL@~{tvwiykh zP)98awW^7N87~qOU9`llfdP6KN$LI%1?yp^G)ZcH=dVcZ6VtuFZw=**(wlpZnah%% zm82MvKjiuHW{IZFFGRm>FIXJ+nxlao&H|@TPZCSwNT4-|hF2qa~ILmNkCy`;CmmCFAO;hVLP)-mOcF ziI`$=V@1lX)mp6~*AAdTcn`Y+zo%n-ySw(zgSr&w#?VH4eUSm*5bJ9*k5l-bV;6-C zE6H&8$~}P)_cheLJL%q1_R0O#;)myr58f(yp9-EfW--m1iPsnk9NeSyh_F8D*W1oK zH7f1LU-WzWIq*Bpt4tr=|5(?4qZ=TXz+apBA2i}$>)KBs^Vhm|voHU>GEfA`}T;z4x9 z-)(#d<3DZOLVSq4{nJn2Ut{JIK;X20`0*o3^iP`r7axKq{XL!l@a9kUpEmwK#_Zw* zwy}dc{dmZ$ZR3Tgn-GHA!C;6k{ly{`9V}spmi_VIS5XGhv?3Ok76O7mZZH>^pNAg| v1PfVkgSjCQ+7Q_C(U4AybvkRgne>8#sw;&%jJ-w{D9QOYM{41G3 diff --git a/Session/Meta/Images.xcassets/Loki V2/AddPerson.imageset/AddPerson.pdf b/Session/Meta/Images.xcassets/Session/AddPerson.imageset/AddPerson.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/AddPerson.imageset/AddPerson.pdf rename to Session/Meta/Images.xcassets/Session/AddPerson.imageset/AddPerson.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/AddPerson.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/AddPerson.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/AddPerson.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/AddPerson.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/ArrowUp.pdf b/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/ArrowUp.pdf new file mode 100644 index 000000000..fe36e100e --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/ArrowUp.pdf @@ -0,0 +1,125 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 16.000000 2.500000 cm +0.000000 0.000000 0.000000 scn +-1.500000 3.000000 m +-1.500000 2.171572 -0.828427 1.500000 0.000000 1.500000 c +0.828427 1.500000 1.500000 2.171572 1.500000 3.000000 c +-1.500000 3.000000 l +h +1.500000 24.000000 m +1.500000 24.828426 0.828427 25.500000 0.000000 25.500000 c +-0.828427 25.500000 -1.500000 24.828426 -1.500000 24.000000 c +1.500000 24.000000 l +h +1.500000 3.000000 m +1.500000 24.000000 l +-1.500000 24.000000 l +-1.500000 3.000000 l +1.500000 3.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 7.000000 14.207108 cm +0.000000 0.000000 0.000000 scn +-1.060660 4.353553 m +-1.646447 3.767766 -1.646447 2.818019 -1.060660 2.232232 c +-0.474874 1.646446 0.474874 1.646446 1.060660 2.232232 c +-1.060660 4.353553 l +h +10.060660 11.232232 m +10.646446 11.818019 10.646446 12.767766 10.060660 13.353553 c +9.474874 13.939339 8.525126 13.939339 7.939340 13.353553 c +10.060660 11.232232 l +h +1.060660 2.232232 m +10.060660 11.232232 l +7.939340 13.353553 l +-1.060660 4.353553 l +1.060660 2.232232 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 16.000000 14.207108 cm +0.000000 0.000000 0.000000 scn +1.060660 13.353553 m +0.474874 13.939339 -0.474874 13.939339 -1.060660 13.353553 c +-1.646447 12.767766 -1.646447 11.818019 -1.060660 11.232232 c +1.060660 13.353553 l +h +7.939340 2.232232 m +8.525126 1.646445 9.474874 1.646445 10.060660 2.232232 c +10.646447 2.818019 10.646447 3.767766 10.060660 4.353552 c +7.939340 2.232232 l +h +-1.060660 11.232232 m +7.939340 2.232232 l +10.060660 4.353552 l +1.060660 13.353553 l +-1.060660 11.232232 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 1618 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 32.000000 32.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001708 00000 n +0000001731 00000 n +0000001904 00000 n +0000001978 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2037 +%%EOF \ No newline at end of file diff --git a/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/Contents.json new file mode 100644 index 000000000..3fc8d40ed --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/ArrowUp.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ArrowUp.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Session/Meta/Images.xcassets/Loki V2/ArrowUpDarkMode.imageset/ArrowUp.pdf b/Session/Meta/Images.xcassets/Session/ArrowUpDarkMode.imageset/ArrowUp.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ArrowUpDarkMode.imageset/ArrowUp.pdf rename to Session/Meta/Images.xcassets/Session/ArrowUpDarkMode.imageset/ArrowUp.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/ArrowUpDarkMode.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/ArrowUpDarkMode.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ArrowUpDarkMode.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/ArrowUpDarkMode.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/ArrowUpLightMode.imageset/ArrowUpLightMode.pdf b/Session/Meta/Images.xcassets/Session/ArrowUpLightMode.imageset/ArrowUpLightMode.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ArrowUpLightMode.imageset/ArrowUpLightMode.pdf rename to Session/Meta/Images.xcassets/Session/ArrowUpLightMode.imageset/ArrowUpLightMode.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/ArrowUpLightMode.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/ArrowUpLightMode.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ArrowUpLightMode.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/ArrowUpLightMode.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/ChatBubbles.imageset/ChatBubbles.pdf b/Session/Meta/Images.xcassets/Session/ChatBubbles.imageset/ChatBubbles.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ChatBubbles.imageset/ChatBubbles.pdf rename to Session/Meta/Images.xcassets/Session/ChatBubbles.imageset/ChatBubbles.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/ChatBubbles.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/ChatBubbles.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/ChatBubbles.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/ChatBubbles.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Check.imageset/Check.pdf b/Session/Meta/Images.xcassets/Session/Check.imageset/Check.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Check.imageset/Check.pdf rename to Session/Meta/Images.xcassets/Session/Check.imageset/Check.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Check.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Check.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Circle.imageset/Circle.pdf b/Session/Meta/Images.xcassets/Session/Circle.imageset/Circle.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Circle.imageset/Circle.pdf rename to Session/Meta/Images.xcassets/Session/Circle.imageset/Circle.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Circle.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Circle.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Circle.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Circle.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/CircleCheck.imageset/CircleCheck.pdf b/Session/Meta/Images.xcassets/Session/CircleCheck.imageset/CircleCheck.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CircleCheck.imageset/CircleCheck.pdf rename to Session/Meta/Images.xcassets/Session/CircleCheck.imageset/CircleCheck.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/CircleCheck.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/CircleCheck.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CircleCheck.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/CircleCheck.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/CircleDotDotDot.imageset/CircleDotDotDot.pdf b/Session/Meta/Images.xcassets/Session/CircleDotDotDot.imageset/CircleDotDotDot.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CircleDotDotDot.imageset/CircleDotDotDot.pdf rename to Session/Meta/Images.xcassets/Session/CircleDotDotDot.imageset/CircleDotDotDot.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/CircleDotDotDot.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/CircleDotDotDot.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CircleDotDotDot.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/CircleDotDotDot.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePause.imageset/CirclePause.pdf b/Session/Meta/Images.xcassets/Session/CirclePause.imageset/CirclePause.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePause.imageset/CirclePause.pdf rename to Session/Meta/Images.xcassets/Session/CirclePause.imageset/CirclePause.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePause.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/CirclePause.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePause.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/CirclePause.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePlay.imageset/CirclePlay.pdf b/Session/Meta/Images.xcassets/Session/CirclePlay.imageset/CirclePlay.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePlay.imageset/CirclePlay.pdf rename to Session/Meta/Images.xcassets/Session/CirclePlay.imageset/CirclePlay.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePlay.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/CirclePlay.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePlay.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/CirclePlay.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePlus.imageset/CirclePlus.pdf b/Session/Meta/Images.xcassets/Session/CirclePlus.imageset/CirclePlus.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePlus.imageset/CirclePlus.pdf rename to Session/Meta/Images.xcassets/Session/CirclePlus.imageset/CirclePlus.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/CirclePlus.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/CirclePlus.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/CirclePlus.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/CirclePlus.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Contents.json b/Session/Meta/Images.xcassets/Session/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Contents.json rename to Session/Meta/Images.xcassets/Session/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckDarkMode.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/FilledCircleCheckDarkMode.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckDarkMode.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/FilledCircleCheckDarkMode.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckDarkMode.imageset/FilledCircleCheckDarkMode.pdf b/Session/Meta/Images.xcassets/Session/FilledCircleCheckDarkMode.imageset/FilledCircleCheckDarkMode.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckDarkMode.imageset/FilledCircleCheckDarkMode.pdf rename to Session/Meta/Images.xcassets/Session/FilledCircleCheckDarkMode.imageset/FilledCircleCheckDarkMode.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckLightMode.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/FilledCircleCheckLightMode.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckLightMode.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/FilledCircleCheckLightMode.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckLightMode.imageset/FilledCircleCheckLightMode.pdf b/Session/Meta/Images.xcassets/Session/FilledCircleCheckLightMode.imageset/FilledCircleCheckLightMode.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/FilledCircleCheckLightMode.imageset/FilledCircleCheckLightMode.pdf rename to Session/Meta/Images.xcassets/Session/FilledCircleCheckLightMode.imageset/FilledCircleCheckLightMode.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Flag.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Flag.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Flag.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Flag.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Flag.imageset/Flag.pdf b/Session/Meta/Images.xcassets/Session/Flag.imageset/Flag.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Flag.imageset/Flag.pdf rename to Session/Meta/Images.xcassets/Session/Flag.imageset/Flag.pdf diff --git a/Session/Meta/Images.xcassets/Session/Gear.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Gear.imageset/Contents.json new file mode 100644 index 000000000..a25957746 --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/Gear.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Gear.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Session/Meta/Images.xcassets/Session/Gear.imageset/Gear.pdf b/Session/Meta/Images.xcassets/Session/Gear.imageset/Gear.pdf new file mode 100644 index 000000000..311252946 --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/Gear.imageset/Gear.pdf @@ -0,0 +1,203 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +12.753094 0.000000 m +11.246860 0.000000 l +10.028625 0.000000 9.037453 0.991125 9.037453 2.209358 c +9.037453 2.718937 l +8.519531 2.884407 8.016328 3.093281 7.532906 3.343452 c +7.171782 2.982327 l +6.297141 2.106609 4.896094 2.132298 4.046859 2.982656 c +2.982281 4.047188 l +2.131547 4.896984 2.107078 6.297564 2.982563 7.172110 c +3.343406 7.532953 l +3.093234 8.016376 2.884406 8.519484 2.718891 9.037499 c +2.209359 9.037499 l +0.991172 9.037499 0.000000 10.028625 0.000000 11.246861 c +0.000000 12.753140 l +0.000000 13.971375 0.991172 14.962500 2.209406 14.962500 c +2.718938 14.962500 l +2.884453 15.480469 3.093282 15.983624 3.343453 16.467047 c +2.982328 16.828125 l +2.107359 17.702156 2.131500 19.102875 2.982610 19.953047 c +4.047281 21.017672 l +4.898485 21.870047 6.299203 21.891329 7.172156 21.017391 c +7.532953 20.656593 l +8.016376 20.906719 8.519578 21.115593 9.037500 21.281109 c +9.037500 21.790640 l +9.037500 23.008875 10.028625 24.000000 11.246906 24.000000 c +12.753139 24.000000 l +13.971375 24.000000 14.962501 23.008875 14.962501 21.790640 c +14.962501 21.281063 l +15.480422 21.115593 15.983624 20.906719 16.467047 20.656548 c +16.828173 21.017672 l +17.702812 21.893391 19.103859 21.867702 19.953093 21.017344 c +21.017673 19.952812 l +21.868406 19.103016 21.892876 17.702438 21.017391 16.827890 c +20.656548 16.467047 l +20.906719 15.983624 21.115547 15.480515 21.281063 14.962500 c +21.790594 14.962500 l +23.008827 14.962500 24.000000 13.971375 24.000000 12.753140 c +24.000000 11.246861 l +24.000000 10.028625 23.008827 9.037499 21.790594 9.037499 c +21.281063 9.037499 l +21.115547 8.519530 20.906719 8.016376 20.656548 7.532953 c +21.017673 7.171827 l +21.892641 6.297796 21.868500 4.897079 21.017391 4.046907 c +19.952719 2.982281 l +19.101515 2.129908 17.700796 2.108625 16.827843 2.982563 c +16.467047 3.343361 l +15.983624 3.093235 15.480422 2.884359 14.962501 2.718845 c +14.962501 2.209267 l +14.962501 0.991125 13.971375 0.000000 12.753094 0.000000 c +12.753094 0.000000 l +h +7.767984 4.820156 m +8.439562 4.422983 9.162375 4.122936 9.916313 3.928360 c +10.226812 3.848249 10.443750 3.568218 10.443750 3.247547 c +10.443750 2.209358 l +10.443750 1.766531 10.804078 1.406250 11.246906 1.406250 c +12.753139 1.406250 l +13.195968 1.406250 13.556296 1.766531 13.556296 2.209358 c +13.556296 3.247547 l +13.556296 3.568218 13.773234 3.848249 14.083735 3.928360 c +14.837672 4.122936 15.560484 4.422983 16.232063 4.820156 c +16.508390 4.983562 16.860188 4.939125 17.087204 4.712109 c +17.822578 3.976688 l +18.139641 3.659250 18.648796 3.666611 18.958078 3.976360 c +20.023405 5.041641 l +20.331936 5.349796 20.342249 5.859047 20.023687 6.177187 c +19.287983 6.912891 l +19.061016 7.139860 19.016579 7.491703 19.179937 7.767984 c +19.577108 8.439516 19.877110 9.162329 20.071688 9.916313 c +20.151844 10.226812 20.431875 10.443704 20.752501 10.443704 c +21.790642 10.443704 l +22.233469 10.443704 22.593798 10.803985 22.593798 11.246813 c +22.593798 12.753094 l +22.593798 13.195922 22.233469 13.556204 21.790642 13.556204 c +20.752501 13.556204 l +20.431828 13.556204 20.151844 13.773141 20.071688 14.083593 c +19.877110 14.837578 19.577063 15.560390 19.179937 16.231922 c +19.016579 16.508204 19.061016 16.860046 19.287983 17.087015 c +20.023405 17.822437 l +20.341312 18.139969 20.333015 18.649031 20.023687 18.957985 c +18.958452 20.023218 l +18.649687 20.332407 18.140436 20.341454 17.822906 20.023500 c +17.087250 19.287796 l +16.860283 19.060781 16.508345 19.016344 16.232109 19.179750 c +15.560532 19.576921 14.837719 19.876968 14.083782 20.071547 c +13.773282 20.151657 13.556343 20.431688 13.556343 20.752359 c +13.556343 21.790640 l +13.556343 22.233469 13.196015 22.593750 12.753187 22.593750 c +11.246953 22.593750 l +10.804125 22.593750 10.443796 22.233469 10.443796 21.790640 c +10.443796 20.752453 l +10.443796 20.431782 10.226859 20.151751 9.916359 20.071640 c +9.162422 19.877062 8.439610 19.577015 7.768031 19.179844 c +7.491656 19.016483 7.139860 19.060921 6.912891 19.287891 c +6.177516 20.023312 l +5.860453 20.340750 5.351250 20.333391 5.042016 20.023640 c +3.976687 18.958359 l +3.668156 18.650204 3.657844 18.141001 3.976406 17.822813 c +4.712110 17.087109 l +4.939078 16.860140 4.983516 16.508297 4.820156 16.232016 c +4.422985 15.560485 4.122984 14.837671 3.928406 14.083687 c +3.848250 13.773188 3.568219 13.556297 3.247594 13.556297 c +2.209406 13.556297 l +1.766578 13.556251 1.406250 13.195969 1.406250 12.753140 c +1.406250 11.246861 l +1.406250 10.804032 1.766578 10.443749 2.209406 10.443749 c +3.247547 10.443749 l +3.568219 10.443749 3.848203 10.226813 3.928360 9.916360 c +4.122938 9.162375 4.422985 8.439562 4.820109 7.768030 c +4.983469 7.491749 4.939031 7.139906 4.712063 6.912937 c +3.976641 6.177515 l +3.658734 5.859983 3.667031 5.350922 3.976359 5.041969 c +5.041594 3.976734 l +5.350359 3.667547 5.859610 3.658499 6.177141 3.976452 c +6.912797 4.712156 l +7.080047 4.879360 7.428000 5.021250 7.767984 4.820156 c +7.767984 4.820156 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 6.778137 6.778126 cm +0.000000 0.000000 0.000000 scn +5.221874 0.000000 m +2.342484 0.000000 0.000000 2.342530 0.000000 5.221874 c +0.000000 8.101217 2.342484 10.443748 5.221874 10.443748 c +8.101265 10.443748 10.443748 8.101217 10.443748 5.221874 c +10.443748 2.342530 8.101265 0.000000 5.221874 0.000000 c +5.221874 0.000000 l +h +5.221874 9.037498 m +3.117890 9.037498 1.406250 7.325811 1.406250 5.221874 c +1.406250 3.117937 3.117937 1.406250 5.221874 1.406250 c +7.325811 1.406250 9.037498 3.117937 9.037498 5.221874 c +9.037498 7.325811 7.325859 9.037498 5.221874 9.037498 c +5.221874 9.037498 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 5666 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000005756 00000 n +0000005779 00000 n +0000005952 00000 n +0000006026 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +6085 +%%EOF \ No newline at end of file diff --git a/Session/Meta/Images.xcassets/Loki V2/Globe.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Globe.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Globe.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Globe.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Globe.imageset/Globe.pdf b/Session/Meta/Images.xcassets/Session/Globe.imageset/Globe.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Globe.imageset/Globe.pdf rename to Session/Meta/Images.xcassets/Session/Globe.imageset/Globe.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Group.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Group.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Group.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Group.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Group.imageset/Group.pdf b/Session/Meta/Images.xcassets/Session/Group.imageset/Group.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Group.imageset/Group.pdf rename to Session/Meta/Images.xcassets/Session/Group.imageset/Group.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Key.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Key.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Key.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Key.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Key.imageset/Key.pdf b/Session/Meta/Images.xcassets/Session/Key.imageset/Key.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Key.imageset/Key.pdf rename to Session/Meta/Images.xcassets/Session/Key.imageset/Key.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/MagnifyingGlass.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/MagnifyingGlass.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/MagnifyingGlass.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/MagnifyingGlass.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/MagnifyingGlass.imageset/MagnifyingGlass.pdf b/Session/Meta/Images.xcassets/Session/MagnifyingGlass.imageset/MagnifyingGlass.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/MagnifyingGlass.imageset/MagnifyingGlass.pdf rename to Session/Meta/Images.xcassets/Session/MagnifyingGlass.imageset/MagnifyingGlass.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Message.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Message.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Message.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Message.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Message.imageset/Message.pdf b/Session/Meta/Images.xcassets/Session/Message.imageset/Message.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Message.imageset/Message.pdf rename to Session/Meta/Images.xcassets/Session/Message.imageset/Message.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Microphone.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Microphone.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Microphone.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Microphone.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Microphone.imageset/Microphone.pdf b/Session/Meta/Images.xcassets/Session/Microphone.imageset/Microphone.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Microphone.imageset/Microphone.pdf rename to Session/Meta/Images.xcassets/Session/Microphone.imageset/Microphone.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Mute.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Mute.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Mute.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Mute.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Mute.imageset/Mute.pdf b/Session/Meta/Images.xcassets/Session/Mute.imageset/Mute.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Mute.imageset/Mute.pdf rename to Session/Meta/Images.xcassets/Session/Mute.imageset/Mute.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Pause.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Pause.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Pause.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Pause.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Pause.imageset/Pause.pdf b/Session/Meta/Images.xcassets/Session/Pause.imageset/Pause.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Pause.imageset/Pause.pdf rename to Session/Meta/Images.xcassets/Session/Pause.imageset/Pause.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/People.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/People.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/People.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/People.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/People.imageset/People.pdf b/Session/Meta/Images.xcassets/Session/People.imageset/People.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/People.imageset/People.pdf rename to Session/Meta/Images.xcassets/Session/People.imageset/People.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Play.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Play.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Play.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Play.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Play.imageset/Play.pdf b/Session/Meta/Images.xcassets/Session/Play.imageset/Play.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Play.imageset/Play.pdf rename to Session/Meta/Images.xcassets/Session/Play.imageset/Play.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Plus.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Plus.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Plus.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Plus.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Plus.imageset/Plus.pdf b/Session/Meta/Images.xcassets/Session/Plus.imageset/Plus.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Plus.imageset/Plus.pdf rename to Session/Meta/Images.xcassets/Session/Plus.imageset/Plus.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/QRCode.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/QRCode.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/QRCode.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/QRCode.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/QRCode.imageset/QRCodeFilled.pdf b/Session/Meta/Images.xcassets/Session/QRCode.imageset/QRCodeFilled.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/QRCode.imageset/QRCodeFilled.pdf rename to Session/Meta/Images.xcassets/Session/QRCode.imageset/QRCodeFilled.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/QuestionMark.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/QuestionMark.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/QuestionMark.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/QuestionMark.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/QuestionMark.imageset/QuestionMark.pdf b/Session/Meta/Images.xcassets/Session/QuestionMark.imageset/QuestionMark.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/QuestionMark.imageset/QuestionMark.pdf rename to Session/Meta/Images.xcassets/Session/QuestionMark.imageset/QuestionMark.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32.png b/Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32.png rename to Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32@2x.png b/Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32@2x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32@2x.png rename to Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32@2x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32@3x.png b/Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32@3x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen32.imageset/SessionGreen32@3x.png rename to Session/Meta/Images.xcassets/Session/SessionGreen32.imageset/SessionGreen32@3x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64.png b/Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64.png rename to Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64@2x.png b/Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64@2x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64@2x.png rename to Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64@2x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64@3x.png b/Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64@3x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionGreen64.imageset/SessionGreen64@3x.png rename to Session/Meta/Images.xcassets/Session/SessionGreen64.imageset/SessionGreen64@3x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16.png b/Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16.png rename to Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16@2x.png b/Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16@2x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16@2x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16@2x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16@3x.png b/Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16@3x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite16.imageset/SessionWhite16@3x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite16.imageset/SessionWhite16@3x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24.png b/Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24.png rename to Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24@2x.png b/Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24@2x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24@2x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24@2x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24@3x.png b/Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24@3x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite24.imageset/SessionWhite24@3x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite24.imageset/SessionWhite24@3x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40.png b/Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40.png rename to Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40@2x.png b/Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40@2x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40@2x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40@2x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40@3x.png b/Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40@3x.png similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/SessionWhite40.imageset/SessionWhite40@3x.png rename to Session/Meta/Images.xcassets/Session/SessionWhite40.imageset/SessionWhite40@3x.png diff --git a/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Shield.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Shield.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Shield.pdf b/Session/Meta/Images.xcassets/Session/Shield.imageset/Shield.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Shield.imageset/Shield.pdf rename to Session/Meta/Images.xcassets/Session/Shield.imageset/Shield.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Star.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Star.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Star.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Star.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Star.imageset/StarOutline.pdf b/Session/Meta/Images.xcassets/Session/Star.imageset/StarOutline.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Star.imageset/StarOutline.pdf rename to Session/Meta/Images.xcassets/Session/Star.imageset/StarOutline.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/Sun.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Sun.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Sun.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/Sun.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/Sun.imageset/Sun.pdf b/Session/Meta/Images.xcassets/Session/Sun.imageset/Sun.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/Sun.imageset/Sun.pdf rename to Session/Meta/Images.xcassets/Session/Sun.imageset/Sun.pdf diff --git a/Session/Meta/Images.xcassets/Loki V2/X.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/X.imageset/Contents.json similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/X.imageset/Contents.json rename to Session/Meta/Images.xcassets/Session/X.imageset/Contents.json diff --git a/Session/Meta/Images.xcassets/Loki V2/X.imageset/X.pdf b/Session/Meta/Images.xcassets/Session/X.imageset/X.pdf similarity index 100% rename from Session/Meta/Images.xcassets/Loki V2/X.imageset/X.pdf rename to Session/Meta/Images.xcassets/Session/X.imageset/X.pdf diff --git a/Session/Meta/Signal-Bridging-Header.h b/Session/Meta/Signal-Bridging-Header.h index 900f868e9..3f81f443b 100644 --- a/Session/Meta/Signal-Bridging-Header.h +++ b/Session/Meta/Signal-Bridging-Header.h @@ -15,6 +15,7 @@ #import "ContactTableViewCell.h" #import "ConversationViewCell.h" #import "ConversationViewItem.h" +#import "ConversationViewModel.h" #import "DateUtil.h" #import "MediaDetailViewController.h" #import "NotificationSettingsViewController.h" @@ -25,6 +26,7 @@ #import "OWSBezierPathView.h" #import "OWSBubbleShapeView.h" #import "OWSBubbleView.h" +#import "OWSConversationSettingsViewController.h" #import "OWSDatabaseMigration.h" #import "OWSMessageBubbleView.h" #import "OWSMessageCell.h" diff --git a/Session/Onboarding/PNOptionView.swift b/Session/Onboarding/PNOptionView.swift index e412c4acd..f9623f62c 100644 --- a/Session/Onboarding/PNOptionView.swift +++ b/Session/Onboarding/PNOptionView.swift @@ -29,7 +29,7 @@ final class OptionView : UIView { // Round corners layer.cornerRadius = Values.pnOptionCornerRadius // Set up border - layer.borderWidth = Values.borderThickness + layer.borderWidth = 1 layer.borderColor = Colors.pnOptionBorder.cgColor // Set up shadow layer.shadowColor = UIColor.black.cgColor @@ -88,7 +88,7 @@ final class OptionView : UIView { layer.add(borderAnimation, forKey: borderAnimation.keyPath) layer.borderColor = newBorderColor // Animate shadow color - let newShadowColor = isSelected ? Colors.newConversationButtonShadow.cgColor : UIColor.black.cgColor + let newShadowColor = isSelected ? Colors.expandedButtonGlowColor.cgColor : UIColor.black.cgColor let shadowAnimation = CABasicAnimation(keyPath: "shadowColor") shadowAnimation.fromValue = layer.shadowColor shadowAnimation.toValue = newShadowColor diff --git a/Session/Onboarding/RegisterVC.swift b/Session/Onboarding/RegisterVC.swift index b6f2f651c..f0803ecab 100644 --- a/Session/Onboarding/RegisterVC.swift +++ b/Session/Onboarding/RegisterVC.swift @@ -64,8 +64,8 @@ final class RegisterVC : BaseVC { let publicKeyLabelContainer = UIView() publicKeyLabelContainer.addSubview(publicKeyLabel) publicKeyLabel.pin(to: publicKeyLabelContainer, withInset: Values.mediumSpacing) - publicKeyLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius - publicKeyLabelContainer.layer.borderWidth = Values.borderThickness + publicKeyLabelContainer.layer.cornerRadius = TextField.cornerRadius + publicKeyLabelContainer.layer.borderWidth = 1 publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor // Set up spacers let topSpacer = UIView.vStretchingSpacer() diff --git a/Session/Onboarding/SeedReminderView.swift b/Session/Onboarding/SeedReminderView.swift index 0ff748461..aaa06700e 100644 --- a/Session/Onboarding/SeedReminderView.swift +++ b/Session/Onboarding/SeedReminderView.swift @@ -25,7 +25,7 @@ final class SeedReminderView : UIView { lazy var subtitleLabel: UILabel = { let result = UILabel() - result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) result.font = .systemFont(ofSize: Values.verySmallFontSize) result.lineBreakMode = .byWordWrapping result.numberOfLines = 0 diff --git a/Session/Onboarding/SeedVC.swift b/Session/Onboarding/SeedVC.swift index 4a29d288c..a5e856db4 100644 --- a/Session/Onboarding/SeedVC.swift +++ b/Session/Onboarding/SeedVC.swift @@ -82,12 +82,12 @@ final class SeedVC : BaseVC { let mnemonicLabelContainer = UIView() mnemonicLabelContainer.addSubview(mnemonicLabel) mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? Values.smallSpacing : Values.mediumSpacing) - mnemonicLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius - mnemonicLabelContainer.layer.borderWidth = Values.borderThickness + mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius + mnemonicLabelContainer.layer.borderWidth = 1 mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor // Set up call to action label let callToActionLabel = UILabel() - callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + callToActionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) callToActionLabel.font = .systemFont(ofSize: isIPhone5OrSmaller ? Values.smallFontSize : Values.mediumFontSize) callToActionLabel.text = NSLocalizedString("vc_seed_reveal_button_title", comment: "") callToActionLabel.textAlignment = .center diff --git a/Session/Open Groups/JoinPublicChatVC.swift b/Session/Open Groups/JoinOpenGroupVC.swift similarity index 98% rename from Session/Open Groups/JoinPublicChatVC.swift rename to Session/Open Groups/JoinOpenGroupVC.swift index 267a6618e..d88b8df80 100644 --- a/Session/Open Groups/JoinPublicChatVC.swift +++ b/Session/Open Groups/JoinOpenGroupVC.swift @@ -1,5 +1,5 @@ -final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate { +final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControllerDelegate, OWSQRScannerDelegate { private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) private var pages: [UIViewController] = [] private var isJoining = false @@ -164,7 +164,7 @@ final class JoinPublicChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie } private final class EnterChatURLVC : UIViewController { - weak var joinPublicChatVC: JoinPublicChatVC! + weak var joinPublicChatVC: JoinOpenGroupVC! private var bottomConstraint: NSLayoutConstraint! // MARK: Components @@ -251,7 +251,7 @@ private final class EnterChatURLVC : UIViewController { } private final class ScanQRCodePlaceholderVC : UIViewController { - weak var joinPublicChatVC: JoinPublicChatVC! + weak var joinPublicChatVC: JoinOpenGroupVC! override func viewDidLoad() { // Remove background color diff --git a/Session/Path/PathVC.swift b/Session/Path/PathVC.swift index 57e3d7bb5..925a71353 100644 --- a/Session/Path/PathVC.swift +++ b/Session/Path/PathVC.swift @@ -44,7 +44,7 @@ final class PathVC : BaseVC { private func setUpViewHierarchy() { // Set up explanation label let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.text = NSLocalizedString("vc_path_explanation", comment: "") explanationLabel.numberOfLines = 0 diff --git a/Session/Settings/AboutTableViewController.h b/Session/Settings/AboutTableViewController.h deleted file mode 100644 index ee8bbf74c..000000000 --- a/Session/Settings/AboutTableViewController.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. -// - -#import "OWSTableViewController.h" - -@interface AboutTableViewController : OWSTableViewController - -@end diff --git a/Session/Settings/AboutTableViewController.m b/Session/Settings/AboutTableViewController.m deleted file mode 100644 index 451eb379c..000000000 --- a/Session/Settings/AboutTableViewController.m +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "AboutTableViewController.h" -#import "Session-Swift.h" -#import "UIView+OWS.h" -#import -#import -#import -#import -#import - -@implementation AboutTableViewController - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - self.title = NSLocalizedString(@"SETTINGS_ABOUT", @"Navbar title"); - - [self updateTableContents]; - - // Crash app if user performs obscure gesture in order to test - // crash reporting. - UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(crashApp)]; - gesture.numberOfTouchesRequired = 2; - gesture.numberOfTapsRequired = 5; - [self.tableView addGestureRecognizer:gesture]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(pushTokensDidChange:) - name:[OWSSyncPushTokensJob PushTokensDidChange] - object:nil]; -} - -- (void)pushTokensDidChange:(NSNotification *)notification -{ - [self updateTableContents]; -} - -#pragma mark - Table Contents - -- (void)updateTableContents -{ - OWSTableContents *contents = [OWSTableContents new]; - - OWSTableSection *informationSection = [OWSTableSection new]; - informationSection.headerTitle = NSLocalizedString(@"SETTINGS_INFORMATION_HEADER", @""); - [informationSection addItem:[OWSTableItem labelItemWithText:NSLocalizedString(@"SETTINGS_VERSION", @"") - accessoryText:[[[NSBundle mainBundle] infoDictionary] - objectForKey:@"CFBundleVersion"]]]; - - [informationSection addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"SETTINGS_LEGAL_TERMS_CELL", - @"table cell label") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"terms") - actionBlock:^{ - [[UIApplication sharedApplication] - openURL:[NSURL URLWithString:kLegalTermsUrlString]]; - }]]; - - [contents addSection:informationSection]; - - OWSTableSection *helpSection = [OWSTableSection new]; - helpSection.headerTitle = NSLocalizedString(@"SETTINGS_HELP_HEADER", @""); - [helpSection addItem:[OWSTableItem disclosureItemWithText:NSLocalizedString(@"SETTINGS_SUPPORT", @"") - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"support") - actionBlock:^{ - [[UIApplication sharedApplication] - openURL:[NSURL URLWithString:@"https://support.signal.org"]]; - }]]; - [contents addSection:helpSection]; - - UILabel *copyrightLabel = [UILabel new]; - copyrightLabel.text = NSLocalizedString(@"SETTINGS_COPYRIGHT", @""); - copyrightLabel.textColor = [Theme secondaryColor]; - copyrightLabel.font = [UIFont ows_regularFontWithSize:15.0f]; - copyrightLabel.numberOfLines = 2; - copyrightLabel.lineBreakMode = NSLineBreakByWordWrapping; - copyrightLabel.textAlignment = NSTextAlignmentCenter; - helpSection.customFooterView = copyrightLabel; - helpSection.customFooterHeight = @(60.f); - -#ifdef DEBUG - __block NSUInteger threadCount; - __block NSUInteger messageCount; - __block NSUInteger attachmentCount; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - threadCount = [transaction numberOfKeysInCollection:[TSThread collection]]; - messageCount = [transaction numberOfKeysInCollection:[TSInteraction collection]]; - attachmentCount = [transaction numberOfKeysInCollection:[TSAttachment collection]]; - }]; - - NSByteCountFormatter *byteCountFormatter = [NSByteCountFormatter new]; - - // format counts with thousands separator - NSNumberFormatter *numberFormatter = [NSNumberFormatter new]; - numberFormatter.formatterBehavior = NSNumberFormatterBehavior10_4; - numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; - - OWSTableSection *debugSection = [OWSTableSection new]; - - debugSection.headerTitle = @"Debug"; - - NSString *formattedThreadCount = [numberFormatter stringFromNumber:@(threadCount)]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Threads: %@", formattedThreadCount]]]; - - NSString *formattedMessageCount = [numberFormatter stringFromNumber:@(messageCount)]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Messages: %@", formattedMessageCount]]]; - - NSString *formattedAttachmentCount = [numberFormatter stringFromNumber:@(attachmentCount)]; - [debugSection addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Attachments: %@", - formattedAttachmentCount]]]; - - NSString *dbSize = - [byteCountFormatter stringFromByteCount:(long long)[OWSPrimaryStorage.sharedManager databaseFileSize]]; - [debugSection addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Database size: %@", dbSize]]]; - - NSString *dbWALSize = - [byteCountFormatter stringFromByteCount:(long long)[OWSPrimaryStorage.sharedManager databaseWALFileSize]]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Database WAL size: %@", dbWALSize]]]; - - NSString *dbSHMSize = - [byteCountFormatter stringFromByteCount:(long long)[OWSPrimaryStorage.sharedManager databaseSHMFileSize]]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Database SHM size: %@", dbSHMSize]]]; - - [contents addSection:debugSection]; - - OWSPreferences *preferences = Environment.shared.preferences; - NSString *_Nullable pushToken = [preferences getPushToken]; - NSString *_Nullable voipToken = [preferences getVoipToken]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Push Token: %@", pushToken ?: @"None"]]]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"VOIP Token: %@", voipToken ?: @"None"]]]; - - // Strip prefix from category, otherwise it's too long to fit into cell on a small device. - NSString *audioCategory = - [AVAudioSession.sharedInstance.category stringByReplacingOccurrencesOfString:@"AVAudioSessionCategory" - withString:@""]; - [debugSection - addItem:[OWSTableItem labelItemWithText:[NSString stringWithFormat:@"Audio Category: %@", audioCategory]]]; -#endif - - self.contents = contents; -} - -- (void)crashApp -{ - OWSFail(@"crashApp"); -} - -@end diff --git a/Session/Settings/MultiDeviceVC.swift b/Session/Settings/MultiDeviceVC.swift index 60fc933d5..3a86e5b15 100644 --- a/Session/Settings/MultiDeviceVC.swift +++ b/Session/Settings/MultiDeviceVC.swift @@ -104,8 +104,8 @@ final class MultiDeviceVC : BaseVC { let mnemonicLabelContainer = UIView() mnemonicLabelContainer.addSubview(mnemonicLabel) mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? 4 : Values.smallSpacing) - mnemonicLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius - mnemonicLabelContainer.layer.borderWidth = Values.borderThickness + mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius + mnemonicLabelContainer.layer.borderWidth = 1 mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor let stepsLabel1Container = UIView() stepsLabel1Container.addSubview(stepsLabel1) diff --git a/Session/Settings/NukeDataModal.swift b/Session/Settings/NukeDataModal.swift index 15ca1da09..413cd0745 100644 --- a/Session/Settings/NukeDataModal.swift +++ b/Session/Settings/NukeDataModal.swift @@ -14,7 +14,7 @@ final class NukeDataModal : Modal { titleLabel.textAlignment = .center // Set up explanation label let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.text = NSLocalizedString("modal_clear_all_data_explanation", comment: "") explanationLabel.numberOfLines = 0 diff --git a/Session/Settings/PrivacySettingsTableViewController.m b/Session/Settings/PrivacySettingsTableViewController.m index 6fb711b9b..d90389621 100644 --- a/Session/Settings/PrivacySettingsTableViewController.m +++ b/Session/Settings/PrivacySettingsTableViewController.m @@ -253,48 +253,6 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s [self.typingIndicators setTypingIndicatorsEnabledWithValue:enabled]; } -- (void)didToggleCallsHideIPAddressSwitch:(UISwitch *)sender -{ - BOOL enabled = sender.isOn; - OWSLogInfo(@"toggled callsHideIPAddress: %@", enabled ? @"ON" : @"OFF"); - [self.preferences setDoCallsHideIPAddress:enabled]; -} - -- (void)didToggleEnableSystemCallLogSwitch:(UISwitch *)sender -{ - OWSLogInfo(@"user toggled call kit preference: %@", (sender.isOn ? @"ON" : @"OFF")); - [self.preferences setIsSystemCallLogEnabled:sender.isOn]; -} - -- (void)didToggleEnableCallKitSwitch:(UISwitch *)sender -{ - OWSLogInfo(@"user toggled call kit preference: %@", (sender.isOn ? @"ON" : @"OFF")); - [self.preferences setIsCallKitEnabled:sender.isOn]; - - // Show/Hide dependent switch: CallKit privacy - [self updateTableContents]; -} - -- (void)didToggleEnableCallKitPrivacySwitch:(UISwitch *)sender -{ - OWSLogInfo(@"user toggled call kit privacy preference: %@", (sender.isOn ? @"ON" : @"OFF")); - [self.preferences setIsCallKitPrivacyEnabled:!sender.isOn]; - - // rebuild callUIAdapter since CallKit configuration changed. -// [AppEnvironment.shared.callService createCallUIAdapter]; -} - -- (void)didToggleUDUnrestrictedAccessSwitch:(UISwitch *)sender -{ - -} - -- (void)didToggleUDShowIndicatorsSwitch:(UISwitch *)sender -{ - OWSLogInfo(@"toggled to: %@", (sender.isOn ? @"ON" : @"OFF")); - [self.preferences setShouldShowUnidentifiedDeliveryIndicators:sender.isOn]; -} - - (void)didToggleLinkPreviewsEnabled:(UISwitch *)sender { BOOL isOn = sender.isOn; @@ -313,11 +271,6 @@ static NSString *const kSealedSenderInfoURL = @"https://signal.org/blog/sealed-s SSKPreferences.areLinkPreviewsEnabled = sender.isOn; } -- (void)show2FASettings -{ - -} - - (void)isScreenLockEnabledDidChange:(UISwitch *)sender { BOOL shouldBeEnabled = sender.isOn; diff --git a/Session/Settings/SeedModal.swift b/Session/Settings/SeedModal.swift index d5dd02e53..1aae18602 100644 --- a/Session/Settings/SeedModal.swift +++ b/Session/Settings/SeedModal.swift @@ -34,12 +34,12 @@ final class SeedModal : Modal { let mnemonicLabelContainer = UIView() mnemonicLabelContainer.addSubview(mnemonicLabel) mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? 4 : Values.smallSpacing) - mnemonicLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius - mnemonicLabelContainer.layer.borderWidth = Values.borderThickness + mnemonicLabelContainer.layer.cornerRadius = TextField.cornerRadius + mnemonicLabelContainer.layer.borderWidth = 1 mnemonicLabelContainer.layer.borderColor = Colors.text.cgColor // Set up explanation label let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + explanationLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.text = NSLocalizedString("modal_seed_explanation", comment: "") explanationLabel.numberOfLines = 0 diff --git a/Session/Settings/SettingsVC.swift b/Session/Settings/SettingsVC.swift index 3c93fe9fa..53fbc0c72 100644 --- a/Session/Settings/SettingsVC.swift +++ b/Session/Settings/SettingsVC.swift @@ -119,7 +119,7 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { } // Set up version label let versionLabel = UILabel() - versionLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + versionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) versionLabel.font = .systemFont(ofSize: Values.verySmallFontSize) versionLabel.numberOfLines = 0 versionLabel.textAlignment = .center diff --git a/Session/Shared/BaseVC.swift b/Session/Shared/BaseVC.swift index ed234fead..f722f68fc 100644 --- a/Session/Shared/BaseVC.swift +++ b/Session/Shared/BaseVC.swift @@ -30,7 +30,7 @@ class BaseVC : UIViewController { internal func setUpGradientBackground() { hasGradient = true view.backgroundColor = .clear - let gradient = Gradients.defaultLokiBackground + let gradient = Gradients.defaultBackground view.setGradient(gradient) } diff --git a/Session/Shared/ConversationCell.swift b/Session/Shared/ConversationCell.swift index 2fcc524bd..0faf2dea6 100644 --- a/Session/Shared/ConversationCell.swift +++ b/Session/Shared/ConversationCell.swift @@ -23,7 +23,7 @@ final class ConversationCell : UITableViewCell { result.font = .systemFont(ofSize: Values.smallFontSize) result.textColor = Colors.text result.lineBreakMode = .byTruncatingTail - result.alpha = Values.conversationCellTimestampOpacity + result.alpha = Values.lowOpacity return result }() @@ -40,11 +40,14 @@ final class ConversationCell : UITableViewCell { private lazy var statusIndicatorView: UIImageView = { let result = UIImageView() result.contentMode = .scaleAspectFit - result.layer.cornerRadius = Values.conversationCellStatusIndicatorSize / 2 + result.layer.cornerRadius = ConversationCell.conversationCellStatusIndicatorSize / 2 result.layer.masksToBounds = true return result }() + // MARK: Settings + private static let conversationCellStatusIndicatorSize: CGFloat = 14 + // MARK: Initialization override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -107,8 +110,8 @@ final class ConversationCell : UITableViewCell { bottomLabelStackView.set(.width, to: UIScreen.main.bounds.width - Values.accentLineThickness - Values.mediumSpacing - profilePictureViewSize - Values.mediumSpacing - Values.mediumSpacing) bottomLabelStackView.set(.height, to: 18) bottomLabelSpacer.set(.height, to: 18) - statusIndicatorView.set(.width, to: Values.conversationCellStatusIndicatorSize) - statusIndicatorView.set(.height, to: Values.conversationCellStatusIndicatorSize) + statusIndicatorView.set(.width, to: ConversationCell.conversationCellStatusIndicatorSize) + statusIndicatorView.set(.height, to: ConversationCell.conversationCellStatusIndicatorSize) snippetLabel.pin(to: snippetLabelContainer) typingIndicatorView.pin(.leading, to: .leading, of: snippetLabelContainer) typingIndicatorView.centerYAnchor.constraint(equalTo: snippetLabel.centerYAnchor).isActive = true diff --git a/Session/Shared/LoadingViewController.swift b/Session/Shared/LoadingViewController.swift index 926840d22..c961caad4 100644 --- a/Session/Shared/LoadingViewController.swift +++ b/Session/Shared/LoadingViewController.swift @@ -19,7 +19,7 @@ public class LoadingViewController: UIViewController { // Loki: Set gradient background view.backgroundColor = .clear - let gradient = Gradients.defaultLokiBackground + let gradient = Gradients.defaultBackground view.setGradient(gradient) self.logoView = UIImageView(image: #imageLiteral(resourceName: "SessionGreen64")) diff --git a/Session/Shared/UserCell.swift b/Session/Shared/UserCell.swift index b9ebce538..fecfa6ca0 100644 --- a/Session/Shared/UserCell.swift +++ b/Session/Shared/UserCell.swift @@ -89,7 +89,7 @@ final class UserCell : UITableViewCell { case .none: accessoryImageView.isHidden = true case .lock: accessoryImageView.isHidden = false - accessoryImageView.image = #imageLiteral(resourceName: "ic_lock_outline").asTintedImage(color: Colors.text.withAlphaComponent(Values.unimportantElementOpacity))! + accessoryImageView.image = #imageLiteral(resourceName: "ic_lock_outline").asTintedImage(color: Colors.text.withAlphaComponent(Values.mediumOpacity))! case .tick(let isSelected): accessoryImageView.isHidden = false let icon = isSelected ? #imageLiteral(resourceName: "CircleCheck") : #imageLiteral(resourceName: "Circle") diff --git a/Session/Sheets & Modals/KeyPairMigrationSuccessSheet.swift b/Session/Sheets & Modals/KeyPairMigrationSuccessSheet.swift index 842eef20d..3c863a67c 100644 --- a/Session/Sheets & Modals/KeyPairMigrationSuccessSheet.swift +++ b/Session/Sheets & Modals/KeyPairMigrationSuccessSheet.swift @@ -50,8 +50,8 @@ final class KeyPairMigrationSuccessSheet : Sheet { let sessionIDContainer = UIView() sessionIDContainer.addSubview(sessionIDLabel) sessionIDLabel.pin(to: sessionIDContainer, withInset: Values.mediumSpacing) - sessionIDContainer.layer.cornerRadius = Values.textFieldCornerRadius - sessionIDContainer.layer.borderWidth = Values.borderThickness + sessionIDContainer.layer.cornerRadius = TextField.cornerRadius + sessionIDContainer.layer.borderWidth = 1 sessionIDContainer.layer.borderColor = Colors.text.cgColor // OK button let okButton = Button(style: .prominentOutline, size: .large) diff --git a/Session/Sheets & Modals/Modal.swift b/Session/Sheets & Modals/Modal.swift index 80e0e6317..88593b1f9 100644 --- a/Session/Sheets & Modals/Modal.swift +++ b/Session/Sheets & Modals/Modal.swift @@ -10,7 +10,7 @@ class Modal : BaseVC { result.layer.cornerRadius = Values.modalCornerRadius result.layer.masksToBounds = false result.layer.borderColor = isLightMode ? UIColor.white.cgColor : Colors.modalBorder.cgColor - result.layer.borderWidth = Values.borderThickness + result.layer.borderWidth = 1 result.layer.shadowColor = UIColor.black.cgColor result.layer.shadowRadius = isLightMode ? 2 : 8 result.layer.shadowOpacity = isLightMode ? 0.1 : 0.64 @@ -31,7 +31,7 @@ class Modal : BaseVC { // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() - let alpha = isLightMode ? CGFloat(0.1) : Values.modalBackgroundOpacity + let alpha = isLightMode ? CGFloat(0.1) : Values.highOpacity view.backgroundColor = UIColor(hex: 0x000000).withAlphaComponent(alpha) cancelButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close)) diff --git a/Session/Sheets & Modals/Sheet.swift b/Session/Sheets & Modals/Sheet.swift index f9cc8056a..9b938b962 100644 --- a/Session/Sheets & Modals/Sheet.swift +++ b/Session/Sheets & Modals/Sheet.swift @@ -13,7 +13,7 @@ class Sheet : BaseVC { result.layer.cornerRadius = 24 result.layer.masksToBounds = false result.layer.borderColor = isLightMode ? UIColor.white.cgColor : Colors.modalBorder.cgColor - result.layer.borderWidth = Values.borderThickness + result.layer.borderWidth = 1 result.layer.shadowColor = UIColor.black.cgColor result.layer.shadowRadius = isLightMode ? 2 : 8 result.layer.shadowOpacity = isLightMode ? 0.1 : 0.64 @@ -23,7 +23,7 @@ class Sheet : BaseVC { // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() - let alpha = isLightMode ? CGFloat(0.1) : Values.modalBackgroundOpacity + let alpha = isLightMode ? CGFloat(0.1) : Values.highOpacity view.backgroundColor = UIColor(hex: 0x000000).withAlphaComponent(alpha) if type(of: self).isDismissable { let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close)) @@ -35,8 +35,8 @@ class Sheet : BaseVC { private func setUpViewHierarchy() { view.addSubview(contentView) - contentView.pin(.leading, to: .leading, of: view, withInset: -Values.borderThickness) - contentView.pin(.trailing, to: .trailing, of: view, withInset: Values.borderThickness) + contentView.pin(.leading, to: .leading, of: view, withInset: -1) + contentView.pin(.trailing, to: .trailing, of: view, withInset: 1) bottomConstraint = contentView.pin(.bottom, to: .bottom, of: view, withInset: overshoot) populateContentView() } diff --git a/Session/Utilities/MentionUtilities.swift b/Session/Utilities/MentionUtilities.swift index 9390d6027..7cc53b98b 100644 --- a/Session/Utilities/MentionUtilities.swift +++ b/Session/Utilities/MentionUtilities.swift @@ -1,16 +1,16 @@ @objc(LKMentionUtilities) public final class MentionUtilities : NSObject { - + override private init() { } - + @objc public static func highlightMentions(in string: String, threadID: String) -> String { return highlightMentions(in: string, isOutgoingMessage: false, threadID: threadID, attributes: [:]).string // isOutgoingMessage and attributes are irrelevant } - + @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { let userPublicKey = getUserHexEncodedPublicKey() - let publicChat = Storage.shared.getOpenGroup(for: threadID) + let openGroup = Storage.shared.getOpenGroup(for: threadID) OWSPrimaryStorage.shared().dbReadConnection.read { transaction in MentionsManager.populateUserPublicKeyCacheIfNeeded(for: threadID, in: transaction) } @@ -27,7 +27,7 @@ public final class MentionUtilities : NSObject { if publicKey == userPublicKey { displayName = OWSProfileManager.shared().localProfileName() } else { - if let publicChat = publicChat { + if let publicChat = openGroup { displayName = UserDisplayNameUtilities.getPublicChatDisplayName(for: publicKey, in: publicChat.channel, on: publicChat.server) } else { displayName = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey) @@ -47,9 +47,9 @@ public final class MentionUtilities : NSObject { } let result = NSMutableAttributedString(string: string, attributes: attributes) mentions.forEach { mention in - let color = isLightMode && isOutgoingMessage ? UIColor.black : Colors.accent + let color = isOutgoingMessage ? (isLightMode ? .white : .black) : Colors.accent result.addAttribute(.foregroundColor, value: color, range: mention.range) - result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.mediumFontSize), range: mention.range) + result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.smallFontSize), range: mention.range) } return result } diff --git a/SessionMessagingKit/Configuration.swift b/SessionMessagingKit/Configuration.swift index dc4a80ec6..2af8d4c48 100644 --- a/SessionMessagingKit/Configuration.swift +++ b/SessionMessagingKit/Configuration.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit @objc public final class SNMessagingKitConfiguration : NSObject { diff --git a/SessionMessagingKit/Database/Storage+ClosedGroups.swift b/SessionMessagingKit/Database/Storage+ClosedGroups.swift index cda71072d..f584fe987 100644 --- a/SessionMessagingKit/Database/Storage+ClosedGroups.swift +++ b/SessionMessagingKit/Database/Storage+ClosedGroups.swift @@ -1,9 +1,6 @@ -import SessionProtocolKit extension Storage { - - // MARK: - V2 - + private static func getClosedGroupEncryptionKeyPairCollection(for groupPublicKey: String) -> String { return "SNClosedGroupEncryptionKeyPairCollection-\(groupPublicKey)" } @@ -45,84 +42,10 @@ extension Storage { (transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: groupPublicKey, inCollection: Storage.closedGroupPublicKeyCollection) } - - - // MARK: - Ratchets - - private static func getClosedGroupRatchetCollection(_ collection: ClosedGroupRatchetCollectionType, for groupPublicKey: String) -> String { - switch collection { - case .old: return "LokiOldClosedGroupRatchetCollection.\(groupPublicKey)" - case .current: return "LokiClosedGroupRatchetCollection.\(groupPublicKey)" - } - } - - public func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> ClosedGroupRatchet? { - let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey) - var result: ClosedGroupRatchet? - Storage.read { transaction in - result = transaction.object(forKey: senderPublicKey, inCollection: collection) as? ClosedGroupRatchet - } - return result - } - - public func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType = .current, using transaction: Any) { - let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey) - (transaction as! YapDatabaseReadWriteTransaction).setObject(ratchet, forKey: senderPublicKey, inCollection: collection) - } - - public func getAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] { - let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey) - var result: [(senderPublicKey: String, ratchet: ClosedGroupRatchet)] = [] - Storage.read { transaction in - transaction.enumerateRows(inCollection: collection) { key, object, _, _ in - guard let ratchet = object as? ClosedGroupRatchet else { return } - let senderPublicKey = key - result.append((senderPublicKey: senderPublicKey, ratchet: ratchet)) - } - } - return result - } - - public func removeAllClosedGroupRatchets(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current, using transaction: Any) { - let collection = Storage.getClosedGroupRatchetCollection(collection, for: groupPublicKey) - (transaction as! YapDatabaseReadWriteTransaction).removeAllObjects(inCollection: collection) - } - - // MARK: - Private Keys - - private static let closedGroupPrivateKeyCollection = "LokiClosedGroupPrivateKeyCollection" - - public func getClosedGroupPrivateKey(for publicKey: String) -> String? { - var result: String? - Storage.read { transaction in - result = transaction.object(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection) as? String - } - return result - } - - public func setClosedGroupPrivateKey(_ privateKey: String, for publicKey: String, using transaction: Any) { - (transaction as! YapDatabaseReadWriteTransaction).setObject(privateKey, forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection) - } - - public func removeClosedGroupPrivateKey(for publicKey: String, using transaction: Any) { - (transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: publicKey, inCollection: Storage.closedGroupPrivateKeyCollection) - } - - - - // MARK: - Convenience - - public func getAllClosedGroupSenderKeys(for groupPublicKey: String, from collection: ClosedGroupRatchetCollectionType = .current) -> Set { - return Set(getAllClosedGroupRatchets(for: groupPublicKey, from: collection).map { senderPublicKey, ratchet in - ClosedGroupSenderKey(chainKey: Data(hex: ratchet.chainKey), keyIndex: ratchet.keyIndex, publicKey: Data(hex: senderPublicKey)) - }) - } - public func getUserClosedGroupPublicKeys() -> Set { var result: Set = [] Storage.read { transaction in result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPublicKeyCollection))) - result = result.union(Set(transaction.allKeys(inCollection: Storage.closedGroupPrivateKeyCollection))) } return result } diff --git a/SessionMessagingKit/Database/Storage+Contacts.swift b/SessionMessagingKit/Database/Storage+Contacts.swift index 9a41ea138..df8744fa4 100644 --- a/SessionMessagingKit/Database/Storage+Contacts.swift +++ b/SessionMessagingKit/Database/Storage+Contacts.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit extension Storage { diff --git a/SessionMessagingKit/Database/Storage+Messaging.swift b/SessionMessagingKit/Database/Storage+Messaging.swift index 041e1b0f1..858ac13aa 100644 --- a/SessionMessagingKit/Database/Storage+Messaging.swift +++ b/SessionMessagingKit/Database/Storage+Messaging.swift @@ -1,10 +1,6 @@ import PromiseKit extension Storage { - - public func getOrGenerateRegistrationID(using transaction: Any) -> UInt32 { - SSKEnvironment.shared.tsAccountManager.getOrGenerateRegistrationId(transaction as! YapDatabaseReadWriteTransaction) - } /// Returns the ID of the thread. public func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { diff --git a/SessionMessagingKit/Database/TSDatabaseView.m b/SessionMessagingKit/Database/TSDatabaseView.m index d2c8562cd..43a61e541 100644 --- a/SessionMessagingKit/Database/TSDatabaseView.m +++ b/SessionMessagingKit/Database/TSDatabaseView.m @@ -12,7 +12,6 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift b/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift index 4d4723047..18b8b8222 100644 --- a/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit import SessionUtilitiesKit public final class ClosedGroupControlMessage : ControlMessage { diff --git a/SessionMessagingKit/Messages/Control Messages/ControlMessage.swift b/SessionMessagingKit/Messages/Control Messages/ControlMessage.swift index 2d0dd2df7..efa4d3862 100644 --- a/SessionMessagingKit/Messages/Control Messages/ControlMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ControlMessage.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit @objc(SNControlMessage) public class ControlMessage : Message { } diff --git a/SessionMessagingKit/Messages/Signal/TSIncomingMessage.m b/SessionMessagingKit/Messages/Signal/TSIncomingMessage.m index 6be45f3a8..065042c09 100644 --- a/SessionMessagingKit/Messages/Signal/TSIncomingMessage.m +++ b/SessionMessagingKit/Messages/Signal/TSIncomingMessage.m @@ -11,7 +11,6 @@ #import "TSContactThread.h" #import "TSDatabaseSecondaryIndexes.h" #import "TSGroupThread.h" -#import #import #import diff --git a/SessionMessagingKit/Messages/Signal/TSInfoMessage.m b/SessionMessagingKit/Messages/Signal/TSInfoMessage.m index f7870c696..6d4d2116c 100644 --- a/SessionMessagingKit/Messages/Signal/TSInfoMessage.m +++ b/SessionMessagingKit/Messages/Signal/TSInfoMessage.m @@ -4,7 +4,6 @@ #import "TSInfoMessage.h" #import "SSKEnvironment.h" -#import #import #import diff --git a/Session/Conversations/Views & Cells/TypingIndicatorInteraction.swift b/SessionMessagingKit/Messages/Signal/TypingIndicatorInteraction.swift similarity index 100% rename from Session/Conversations/Views & Cells/TypingIndicatorInteraction.swift rename to SessionMessagingKit/Messages/Signal/TypingIndicatorInteraction.swift diff --git a/SessionMessagingKit/Sending & Receiving/Expiration/OWSDisappearingMessagesJob.m b/SessionMessagingKit/Sending & Receiving/Expiration/OWSDisappearingMessagesJob.m index ead08f7ed..a16f299e8 100644 --- a/SessionMessagingKit/Sending & Receiving/Expiration/OWSDisappearingMessagesJob.m +++ b/SessionMessagingKit/Sending & Receiving/Expiration/OWSDisappearingMessagesJob.m @@ -16,7 +16,6 @@ #import "TSThread.h" #import #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift index 2c254c0ae..20846e9d5 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift @@ -1,5 +1,4 @@ import CryptoSwift -import SessionProtocolKit import SessionUtilitiesKit import Sodium diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 2308e5ab3..1e1d15070 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit import SignalCoreKit extension MessageReceiver { diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift index 57386e521..75a4f5d0a 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift @@ -1,5 +1,4 @@ import PromiseKit -import SessionProtocolKit extension MessageSender { diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift index ed61dbd41..6fee9e1c9 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit import SessionUtilitiesKit import Sodium diff --git a/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSReadReceiptManager.m b/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSReadReceiptManager.m index d869af4cd..13a1b39c2 100644 --- a/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSReadReceiptManager.m +++ b/SessionMessagingKit/Sending & Receiving/Read Tracking/OWSReadReceiptManager.m @@ -14,7 +14,6 @@ #import "TSDatabaseView.h" #import "TSIncomingMessage.h" #import "YapDatabaseConnection+OWS.h" -#import #import #import @@ -183,15 +182,13 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE - (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self markAsReadBeforeSortId:sortId - thread:thread - readTimestamp:[NSDate millisecondTimestamp] - wasLocal:YES - transaction:transaction]; - }]; - }); + [LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self markAsReadBeforeSortId:sortId + thread:thread + readTimestamp:[NSDate millisecondTimestamp] + wasLocal:YES + transaction:transaction]; + }]; } - (void)messageWasReadLocally:(TSIncomingMessage *)message diff --git a/SessionMessagingKit/Storage.swift b/SessionMessagingKit/Storage.swift index a835578fe..b21953e26 100644 --- a/SessionMessagingKit/Storage.swift +++ b/SessionMessagingKit/Storage.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit import PromiseKit import Sodium @@ -21,13 +20,8 @@ public protocol SessionMessagingKitStorageProtocol { func getUserProfileKey() -> Data? func getUserProfilePictureURL() -> String? - // MARK: - Signal Protocol + // MARK: - Closed Groups - func getOrGenerateRegistrationID(using transaction: Any) -> UInt32 - - // MARK: - Shared Sender Keys - - func getClosedGroupPrivateKey(for publicKey: String) -> String? func getUserClosedGroupPublicKeys() -> Set func isClosedGroup(_ publicKey: String) -> Bool @@ -61,6 +55,7 @@ public protocol SessionMessagingKitStorageProtocol { func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) // MARK: - Last Message Server ID + func getLastMessageServerID(for group: UInt64, on server: String) -> UInt64? func setLastMessageServerID(for group: UInt64, on server: String, to newValue: UInt64, using transaction: Any) func removeLastMessageServerID(for group: UInt64, on server: String, using transaction: Any) diff --git a/SessionMessagingKit/Utilities/DotNetAPI.swift b/SessionMessagingKit/Utilities/DotNetAPI.swift index 93a2d8370..2ef017a8e 100644 --- a/SessionMessagingKit/Utilities/DotNetAPI.swift +++ b/SessionMessagingKit/Utilities/DotNetAPI.swift @@ -1,7 +1,6 @@ import AFNetworking import CryptoSwift import PromiseKit -import SessionProtocolKit import SessionSnodeKit import SessionUtilitiesKit import SignalCoreKit diff --git a/SessionMessagingKit/Utilities/OWSAudioPlayer.h b/SessionMessagingKit/Utilities/OWSAudioPlayer.h index 5a9ccc2e9..4e2c64854 100644 --- a/SessionMessagingKit/Utilities/OWSAudioPlayer.h +++ b/SessionMessagingKit/Utilities/OWSAudioPlayer.h @@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN +@class OWSAudioPlayer; + typedef NS_ENUM(NSInteger, AudioPlaybackState) { AudioPlaybackState_Stopped, AudioPlaybackState_Playing, @@ -19,6 +21,7 @@ typedef NS_ENUM(NSInteger, AudioPlaybackState) { - (void)setAudioPlaybackState:(AudioPlaybackState)state; - (void)setAudioProgress:(CGFloat)progress duration:(CGFloat)duration; - (void)showInvalidAudioFileAlert; +- (void)audioPlayerDidFinishPlaying:(OWSAudioPlayer *)player successfully:(BOOL)flag; @end @@ -35,19 +38,14 @@ typedef NS_ENUM(NSUInteger, OWSAudioBehavior) { @interface OWSAudioPlayer : NSObject @property (nonatomic, readonly, weak) id delegate; - -// This property can be used to associate instances of the player with view -// or model objects. +// This property can be used to associate instances of the player with view or model objects. @property (nonatomic, weak) id owner; - @property (nonatomic) BOOL isLooping; +@property (nonatomic) BOOL isPlaying; +@property (nonatomic) float playbackRate; - (instancetype)initWithMediaUrl:(NSURL *)mediaUrl audioBehavior:(OWSAudioBehavior)audioBehavior; - -- (instancetype)initWithMediaUrl:(NSURL *)mediaUrl - audioBehavior:(OWSAudioBehavior)audioBehavior - delegate:(id)delegate; - +- (instancetype)initWithMediaUrl:(NSURL *)mediaUrl audioBehavior:(OWSAudioBehavior)audioBehavior delegate:(id)delegate; - (void)play; - (void)setCurrentTime:(NSTimeInterval)currentTime; - (void)pause; diff --git a/SessionMessagingKit/Utilities/OWSAudioPlayer.m b/SessionMessagingKit/Utilities/OWSAudioPlayer.m index c22e1cd97..d7b93e7c9 100644 --- a/SessionMessagingKit/Utilities/OWSAudioPlayer.m +++ b/SessionMessagingKit/Utilities/OWSAudioPlayer.m @@ -31,6 +31,11 @@ NS_ASSUME_NONNULL_BEGIN // Do nothing } +- (void)audioPlayerDidFinishPlaying:(OWSAudioPlayer *)player successfully:(BOOL)flag +{ + // Do nothing +} + @end #pragma mark - @@ -102,9 +107,13 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Methods +- (BOOL)isPlaying +{ + return (self.delegate.audioPlaybackState == AudioPlaybackState_Playing); +} + - (void)play { - // get current audio activity [self playWithAudioActivity:self.audioActivity]; } @@ -120,6 +129,7 @@ NS_ASSUME_NONNULL_BEGIN if (!self.audioPlayer) { NSError *error; self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:self.mediaUrl error:&error]; + self.audioPlayer.enableRate = YES; if (error) { [self stop]; @@ -153,6 +163,16 @@ NS_ASSUME_NONNULL_BEGIN [self.audioPlayer setCurrentTime:currentTime]; } +- (float)getPlaybackRate +{ + return self.audioPlayer.rate; +} + +- (void)setPlaybackRate:(float)rate +{ + [self.audioPlayer setRate:rate]; +} + - (void)pause { self.delegate.audioPlaybackState = AudioPlaybackState_Paused; @@ -182,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)togglePlayState { - if (self.delegate.audioPlaybackState == AudioPlaybackState_Playing) { + if (self.isPlaying) { [self pause]; } else { [self playWithAudioActivity:self.audioActivity]; @@ -199,6 +219,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { [self stop]; + [self.delegate audioPlayerDidFinishPlaying:self successfully:flag]; } @end diff --git a/SessionMessagingKit/Utilities/OWSBackgroundTask.m b/SessionMessagingKit/Utilities/OWSBackgroundTask.m index cd14f1b5f..3ce898431 100644 --- a/SessionMessagingKit/Utilities/OWSBackgroundTask.m +++ b/SessionMessagingKit/Utilities/OWSBackgroundTask.m @@ -4,7 +4,6 @@ #import "OWSBackgroundTask.h" #import "AppContext.h" -#import #import #import diff --git a/SessionMessagingKit/Utilities/OWSDisappearingMessagesFinder.m b/SessionMessagingKit/Utilities/OWSDisappearingMessagesFinder.m index 3cd9f7518..bd58d4abc 100644 --- a/SessionMessagingKit/Utilities/OWSDisappearingMessagesFinder.m +++ b/SessionMessagingKit/Utilities/OWSDisappearingMessagesFinder.m @@ -12,7 +12,6 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Utilities/OWSIdentityManager.m b/SessionMessagingKit/Utilities/OWSIdentityManager.m index fb7cc7586..802fb0ac4 100644 --- a/SessionMessagingKit/Utilities/OWSIdentityManager.m +++ b/SessionMessagingKit/Utilities/OWSIdentityManager.m @@ -17,7 +17,6 @@ #import "TSErrorMessage.h" #import "TSGroupThread.h" #import "TSMessage.h" -#import #import "YapDatabaseConnection+OWS.h" #import "YapDatabaseTransaction+OWS.h" #import diff --git a/SessionMessagingKit/Utilities/OWSIncomingMessageFinder.m b/SessionMessagingKit/Utilities/OWSIncomingMessageFinder.m index 3b9d32274..f51d944a8 100644 --- a/SessionMessagingKit/Utilities/OWSIncomingMessageFinder.m +++ b/SessionMessagingKit/Utilities/OWSIncomingMessageFinder.m @@ -7,7 +7,6 @@ #import "TSIncomingMessage.h" #import #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Utilities/OWSMediaGalleryFinder.m b/SessionMessagingKit/Utilities/OWSMediaGalleryFinder.m index 34face9e7..26670ab6a 100644 --- a/SessionMessagingKit/Utilities/OWSMediaGalleryFinder.m +++ b/SessionMessagingKit/Utilities/OWSMediaGalleryFinder.m @@ -11,7 +11,6 @@ #import #import #import -#import #import "TSAttachment.h" NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Utilities/SSKEnvironment.m b/SessionMessagingKit/Utilities/SSKEnvironment.m index 662a12556..a638d3047 100644 --- a/SessionMessagingKit/Utilities/SSKEnvironment.m +++ b/SessionMessagingKit/Utilities/SSKEnvironment.m @@ -5,7 +5,6 @@ #import "SSKEnvironment.h" #import "AppContext.h" #import "OWSPrimaryStorage.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Utilities/YapDatabaseConnection+OWS.m b/SessionMessagingKit/Utilities/YapDatabaseConnection+OWS.m index 77e95a89c..3f30cf2a4 100644 --- a/SessionMessagingKit/Utilities/YapDatabaseConnection+OWS.m +++ b/SessionMessagingKit/Utilities/YapDatabaseConnection+OWS.m @@ -5,7 +5,6 @@ #import "YapDatabaseConnection+OWS.h" #import #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionMessagingKit/Utilities/YapDatabaseTransaction+OWS.m b/SessionMessagingKit/Utilities/YapDatabaseTransaction+OWS.m index 3697adb5b..c95a67a1a 100644 --- a/SessionMessagingKit/Utilities/YapDatabaseTransaction+OWS.m +++ b/SessionMessagingKit/Utilities/YapDatabaseTransaction+OWS.m @@ -4,7 +4,6 @@ #import "YapDatabaseTransaction+OWS.h" #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SessionProtocolKit/ClosedGroupRatchet.swift b/SessionProtocolKit/ClosedGroupRatchet.swift deleted file mode 100644 index 0764da3ab..000000000 --- a/SessionProtocolKit/ClosedGroupRatchet.swift +++ /dev/null @@ -1,45 +0,0 @@ -import SessionUtilitiesKit - -public final class ClosedGroupRatchet : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility - public let chainKey: String - public let keyIndex: UInt - public let messageKeys: [String] - - // MARK: Initialization - public init(chainKey: String, keyIndex: UInt, messageKeys: [String]) { - self.chainKey = chainKey - self.keyIndex = keyIndex - self.messageKeys = messageKeys - } - - // MARK: Coding - public init?(coder: NSCoder) { - guard let chainKey = coder.decodeObject(forKey: "chainKey") as? String, - let keyIndex = coder.decodeObject(forKey: "keyIndex") as? UInt, - let messageKeys = coder.decodeObject(forKey: "messageKeys") as? [String] else { return nil } - self.chainKey = chainKey - self.keyIndex = UInt(keyIndex) - self.messageKeys = messageKeys - super.init() - } - - public func encode(with coder: NSCoder) { - coder.encode(chainKey, forKey: "chainKey") - coder.encode(keyIndex, forKey: "keyIndex") - coder.encode(messageKeys, forKey: "messageKeys") - } - - // MARK: Equality - override public func isEqual(_ other: Any?) -> Bool { - guard let other = other as? ClosedGroupRatchet else { return false } - return chainKey == other.chainKey && keyIndex == other.keyIndex && messageKeys == other.messageKeys - } - - // MARK: Hashing - override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) - return chainKey.hashValue ^ keyIndex.hashValue ^ messageKeys.hashValue - } - - // MARK: Description - override public var description: String { "[ chainKey : \(chainKey), keyIndex : \(keyIndex), messageKeys : \(messageKeys.prettifiedDescription) ]" } -} diff --git a/SessionProtocolKit/ClosedGroupSenderKey.swift b/SessionProtocolKit/ClosedGroupSenderKey.swift deleted file mode 100644 index 332ba87d2..000000000 --- a/SessionProtocolKit/ClosedGroupSenderKey.swift +++ /dev/null @@ -1,47 +0,0 @@ -import CryptoSwift - -public final class ClosedGroupSenderKey : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility - public let chainKey: Data - public let keyIndex: UInt - public let publicKey: Data - - // MARK: Initialization - public init(chainKey: Data, keyIndex: UInt, publicKey: Data) { - self.chainKey = chainKey - self.keyIndex = keyIndex - self.publicKey = publicKey - } - - // MARK: Coding - public init?(coder: NSCoder) { - guard let chainKey = coder.decodeObject(forKey: "chainKey") as? Data, - let keyIndex = coder.decodeObject(forKey: "keyIndex") as? UInt, - let publicKey = coder.decodeObject(forKey: "publicKey") as? Data else { return nil } - self.chainKey = chainKey - self.keyIndex = UInt(keyIndex) - self.publicKey = publicKey - super.init() - } - - public func encode(with coder: NSCoder) { - coder.encode(chainKey, forKey: "chainKey") - coder.encode(keyIndex, forKey: "keyIndex") - coder.encode(publicKey, forKey: "publicKey") - } - - // MARK: Equality - override public func isEqual(_ other: Any?) -> Bool { - guard let other = other as? ClosedGroupSenderKey else { return false } - return chainKey == other.chainKey && keyIndex == other.keyIndex && publicKey == other.publicKey - } - - // MARK: Hashing - override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:) - return chainKey.hashValue ^ keyIndex.hashValue ^ publicKey.hashValue - } - - // MARK: Description - override public var description: String { - return "[ chainKey : \(chainKey), keyIndex : \(keyIndex), publicKey: \(publicKey.toHexString()) ]" - } -} diff --git a/SessionProtocolKit/Configuration.swift b/SessionProtocolKit/Configuration.swift deleted file mode 100644 index 0cd23d5ed..000000000 --- a/SessionProtocolKit/Configuration.swift +++ /dev/null @@ -1,14 +0,0 @@ - -public struct SNProtocolKitConfiguration { - public let storage: SessionProtocolKitStorageProtocol - public let sharedSenderKeysDelegate: SharedSenderKeysDelegate - - internal static var shared: SNProtocolKitConfiguration! -} - -public enum SNProtocolKit { // Just to make the external API nice - - public static func configure(storage: SessionProtocolKitStorageProtocol, sharedSenderKeysDelegate: SharedSenderKeysDelegate) { - SNProtocolKitConfiguration.shared = SNProtocolKitConfiguration(storage: storage, sharedSenderKeysDelegate: sharedSenderKeysDelegate) - } -} diff --git a/SessionProtocolKit/Meta/Info.plist b/SessionProtocolKit/Meta/Info.plist deleted file mode 100644 index 9bcb24442..000000000 --- a/SessionProtocolKit/Meta/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - - diff --git a/SessionProtocolKit/Meta/SessionProtocolKit.h b/SessionProtocolKit/Meta/SessionProtocolKit.h deleted file mode 100644 index c1fcd3157..000000000 --- a/SessionProtocolKit/Meta/SessionProtocolKit.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -FOUNDATION_EXPORT double SessionProtocolKitVersionNumber; -FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[]; diff --git a/SessionProtocolKit/SharedSenderKeys.swift b/SessionProtocolKit/SharedSenderKeys.swift deleted file mode 100644 index c22d34ba7..000000000 --- a/SessionProtocolKit/SharedSenderKeys.swift +++ /dev/null @@ -1,164 +0,0 @@ -import CryptoSwift -import PromiseKit -import SessionUtilitiesKit - -public protocol SharedSenderKeysDelegate { - - func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) -} - -public enum SharedSenderKeys { - private static let gcmTagSize: UInt = 16 - private static let ivSize: UInt = 12 - - // MARK: Ratcheting Error - public enum RatchetingError : LocalizedError { - case loadingFailed(groupPublicKey: String, senderPublicKey: String) - case messageKeyMissing(targetKeyIndex: UInt, groupPublicKey: String, senderPublicKey: String) - case generic - - public var errorDescription: String? { - switch self { - case .loadingFailed(let groupPublicKey, let senderPublicKey): return "Couldn't get ratchet for closed group with public key: \(groupPublicKey), sender public key: \(senderPublicKey)." - case .messageKeyMissing(let targetKeyIndex, let groupPublicKey, let senderPublicKey): return "Couldn't find message key for old key index: \(targetKeyIndex), public key: \(groupPublicKey), sender public key: \(senderPublicKey)." - case .generic: return "An error occurred" - } - } - } - - // MARK: Private/Internal API - public static func generateRatchet(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) -> ClosedGroupRatchet { - let rootChainKey = Data.getSecureRandomData(ofSize: 32)!.toHexString() - let ratchet = ClosedGroupRatchet(chainKey: rootChainKey, keyIndex: 0, messageKeys: []) - SNProtocolKitConfiguration.shared.storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: ratchet, in: .current, using: transaction) - return ratchet - } - - private static func step(_ ratchet: ClosedGroupRatchet) throws -> ClosedGroupRatchet { - let nextMessageKey = try HMAC(key: Data(hex: ratchet.chainKey).bytes, variant: .sha256).authenticate([ UInt8(1) ]) - let nextChainKey = try HMAC(key: Data(hex: ratchet.chainKey).bytes, variant: .sha256).authenticate([ UInt8(2) ]) - let nextKeyIndex = ratchet.keyIndex + 1 - let messageKeys = ratchet.messageKeys + [ nextMessageKey.toHexString() ] - return ClosedGroupRatchet(chainKey: nextChainKey.toHexString(), keyIndex: nextKeyIndex, messageKeys: messageKeys) - } - - /// - Note: Sync. Don't call from the main thread. - private static func stepRatchetOnce(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) throws -> ClosedGroupRatchet { - #if DEBUG - assert(!Thread.isMainThread) - #endif - guard let ratchet = SNProtocolKitConfiguration.shared.storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, from: .current) else { - let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - SNLog("\(error.errorDescription!)") - throw error - } - do { - let result = try step(ratchet) - SNProtocolKitConfiguration.shared.storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, in: .current, using: transaction) - return result - } catch { - SNLog("Couldn't step ratchet due to error: \(error).") - throw error - } - } - - /// - Note: Sync. Don't call from the main thread. - private static func stepRatchet(for groupPublicKey: String, senderPublicKey: String, until targetKeyIndex: UInt, using transaction: Any, isRetry: Bool = false) throws -> ClosedGroupRatchet { - #if DEBUG - assert(!Thread.isMainThread) - #endif - let collection: ClosedGroupRatchetCollectionType = (isRetry) ? .old : .current - guard let ratchet = SNProtocolKitConfiguration.shared.storage.getClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, from: collection) else { - let error = RatchetingError.loadingFailed(groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - SNLog("\(error.errorDescription!)") - throw error - } - if targetKeyIndex < ratchet.keyIndex { - // There's no need to advance the ratchet if this is invoked for an old key index - guard ratchet.messageKeys.count > targetKeyIndex else { - let error = RatchetingError.messageKeyMissing(targetKeyIndex: targetKeyIndex, groupPublicKey: groupPublicKey, senderPublicKey: senderPublicKey) - SNLog("\(error.errorDescription!)") - throw error - } - return ratchet - } else { - var currentKeyIndex = ratchet.keyIndex - var result = ratchet - while currentKeyIndex < targetKeyIndex { - do { - result = try step(result) - currentKeyIndex = result.keyIndex - } catch { - SNLog("Couldn't step ratchet due to error: \(error).") - throw error - } - } - let collection: ClosedGroupRatchetCollectionType = (isRetry) ? .old : .current - SNProtocolKitConfiguration.shared.storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, in: collection, using: transaction) - return result - } - } - - // MARK: Public API - public static func encrypt(_ plaintext: Data, for groupPublicKey: String, senderPublicKey: String, using transaction: Any) throws -> (ivAndCiphertext: Data, keyIndex: UInt) { - let ratchet: ClosedGroupRatchet - do { - ratchet = try stepRatchetOnce(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } catch { - if case RatchetingError.loadingFailed(_, _) = error { - SNProtocolKitConfiguration.shared.sharedSenderKeysDelegate.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } - throw error - } - let iv = Data.getSecureRandomData(ofSize: ivSize)! - let gcm = GCM(iv: iv.bytes, tagLength: Int(gcmTagSize), mode: .combined) - let messageKey = ratchet.messageKeys.last! - let aes = try AES(key: Data(hex: messageKey).bytes, blockMode: gcm, padding: .noPadding) - let ciphertext = try aes.encrypt(plaintext.bytes) - return (ivAndCiphertext: iv + Data(ciphertext), ratchet.keyIndex) - } - - public static func decrypt(_ ivAndCiphertext: Data, for groupPublicKey: String, senderPublicKey: String, keyIndex: UInt, using transaction: Any, isRetry: Bool = false) throws -> Data { - let ratchet: ClosedGroupRatchet - do { - ratchet = try stepRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, until: keyIndex, using: transaction, isRetry: isRetry) - } catch { - if !isRetry { - return try decrypt(ivAndCiphertext, for: groupPublicKey, senderPublicKey: senderPublicKey, keyIndex: keyIndex, using: transaction, isRetry: true) - } else { - if case RatchetingError.loadingFailed(_, _) = error { - SNProtocolKitConfiguration.shared.sharedSenderKeysDelegate.requestSenderKey(for: groupPublicKey, senderPublicKey: senderPublicKey, using: transaction) - } - throw error - } - } - let iv = ivAndCiphertext[0.. 16 { // Pick an arbitrary number of message keys to try; this helps resolve issues caused by messages arriving out of order - lastNMessageKeys = [String](messageKeys[messageKeys.index(messageKeys.endIndex, offsetBy: -16).. Void) - - func getUserKeyPair() -> ECKeyPair? - func getClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, from collection: ClosedGroupRatchetCollectionType) -> ClosedGroupRatchet? - func setClosedGroupRatchet(for groupPublicKey: String, senderPublicKey: String, ratchet: ClosedGroupRatchet, in collection: ClosedGroupRatchetCollectionType, using transaction: Any) -} diff --git a/SessionUIKit/Components/Button.swift b/SessionUIKit/Components/Button.swift index a486c9fc8..93aaaf6f6 100644 --- a/SessionUIKit/Components/Button.swift +++ b/SessionUIKit/Components/Button.swift @@ -68,7 +68,7 @@ public final class Button : UIButton { layer.cornerRadius = height / 2 backgroundColor = fillColor layer.borderColor = borderColor.cgColor - layer.borderWidth = Values.borderThickness + layer.borderWidth = 1 let fontSize = (size == .small) ? Values.smallFontSize : Values.mediumFontSize titleLabel!.font = .boldSystemFont(ofSize: fontSize) setTitleColor(textColor, for: UIControl.State.normal) diff --git a/SessionUIKit/Components/Separator.swift b/SessionUIKit/Components/Separator.swift index af5cea655..602cca355 100644 --- a/SessionUIKit/Components/Separator.swift +++ b/SessionUIKit/Components/Separator.swift @@ -6,7 +6,7 @@ public final class Separator : UIView { // MARK: Components private lazy var titleLabel: UILabel = { let result = UILabel() - result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) result.font = .systemFont(ofSize: Values.smallFontSize) result.textAlignment = .center return result diff --git a/SessionUIKit/Components/TabBar.swift b/SessionUIKit/Components/TabBar.swift index 489ba145f..68e4a0ba0 100644 --- a/SessionUIKit/Components/TabBar.swift +++ b/SessionUIKit/Components/TabBar.swift @@ -9,7 +9,7 @@ public final class TabBar : UIView { private lazy var tabLabels: [UILabel] = tabs.map { tab in let result = UILabel() result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) result.textAlignment = .center result.text = tab.title result.set(.height, to: Values.tabBarHeight - Values.separatorThickness - Values.accentLineThickness) @@ -93,7 +93,7 @@ public final class TabBar : UIView { tabLabelsCopy.remove(at: index) UIView.animate(withDuration: isAnimated ? 0.25 : 0) { tabLabel.textColor = Colors.text - tabLabelsCopy.forEach { $0.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) } + tabLabelsCopy.forEach { $0.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) } self.layoutIfNeeded() } } diff --git a/SessionUIKit/Components/TextField.swift b/SessionUIKit/Components/TextField.swift index 95e4bb20a..42dfb9b39 100644 --- a/SessionUIKit/Components/TextField.swift +++ b/SessionUIKit/Components/TextField.swift @@ -6,9 +6,12 @@ public final class TextField : UITextField { private let horizontalInset: CGFloat private let verticalInset: CGFloat + static let height: CGFloat = isIPhone5OrSmaller ? CGFloat(48) : CGFloat(80) + public static let cornerRadius: CGFloat = 8 + public init(placeholder: String, usesDefaultHeight: Bool = true, customHeight: CGFloat? = nil, customHorizontalInset: CGFloat? = nil, customVerticalInset: CGFloat? = nil) { self.usesDefaultHeight = usesDefaultHeight - self.height = customHeight ?? Values.textFieldHeight + self.height = customHeight ?? TextField.height self.horizontalInset = customHorizontalInset ?? (isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) self.verticalInset = customVerticalInset ?? (isIPhone5OrSmaller ? Values.smallSpacing : Values.largeSpacing) super.init(frame: CGRect.zero) @@ -28,7 +31,7 @@ public final class TextField : UITextField { textColor = Colors.text font = .systemFont(ofSize: Values.smallFontSize) let placeholder = NSMutableAttributedString(string: self.placeholder!) - let placeholderColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + let placeholderColor = Colors.text.withAlphaComponent(Values.mediumOpacity) placeholder.addAttribute(.foregroundColor, value: placeholderColor, range: NSRange(location: 0, length: placeholder.length)) attributedPlaceholder = placeholder tintColor = Colors.accent @@ -36,9 +39,9 @@ public final class TextField : UITextField { if usesDefaultHeight { set(.height, to: height) } - layer.borderColor = isLightMode ? Colors.text.cgColor : Colors.border.withAlphaComponent(Values.textFieldBorderOpacity).cgColor - layer.borderWidth = Values.borderThickness - layer.cornerRadius = Values.textFieldCornerRadius + layer.borderColor = isLightMode ? Colors.text.cgColor : Colors.border.withAlphaComponent(Values.lowOpacity).cgColor + layer.borderWidth = 1 + layer.cornerRadius = TextField.cornerRadius } public override func textRect(forBounds bounds: CGRect) -> CGRect { diff --git a/SessionUIKit/Components/TextView.swift b/SessionUIKit/Components/TextView.swift index bb35f1f76..45e063931 100644 --- a/SessionUIKit/Components/TextView.swift +++ b/SessionUIKit/Components/TextView.swift @@ -12,13 +12,13 @@ public final class TextView : UITextView, UITextViewDelegate { private lazy var placeholderLabel: UILabel = { let result = UILabel() result.font = .systemFont(ofSize: Values.smallFontSize) - result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity) return result }() public init(placeholder: String, usesDefaultHeight: Bool = true, customHeight: CGFloat? = nil, customHorizontalInset: CGFloat? = nil, customVerticalInset: CGFloat? = nil) { self.usesDefaultHeight = usesDefaultHeight - self.height = customHeight ?? Values.textFieldHeight + self.height = customHeight ?? TextField.height self.horizontalInset = customHorizontalInset ?? (isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) self.verticalInset = customVerticalInset ?? (isIPhone5OrSmaller ? Values.smallSpacing : Values.largeSpacing) self.placeholder = placeholder @@ -47,9 +47,9 @@ public final class TextView : UITextView, UITextViewDelegate { if usesDefaultHeight { set(.height, to: height) } - layer.borderColor = isLightMode ? Colors.text.cgColor : Colors.border.withAlphaComponent(Values.textFieldBorderOpacity).cgColor - layer.borderWidth = Values.borderThickness - layer.cornerRadius = Values.textFieldCornerRadius + layer.borderColor = isLightMode ? Colors.text.cgColor : Colors.border.withAlphaComponent(Values.lowOpacity).cgColor + layer.borderWidth = 1 + layer.cornerRadius = TextField.cornerRadius let horizontalInset = usesDefaultHeight ? self.horizontalInset : Values.mediumSpacing textContainerInset = UIEdgeInsets(top: 0, left: horizontalInset, bottom: 0, right: horizontalInset) addSubview(placeholderLabel) diff --git a/SessionUIKit/Style Guide/Colors.swift b/SessionUIKit/Style Guide/Colors.swift index c7876635a..4d25e7cc5 100644 --- a/SessionUIKit/Style Guide/Colors.swift +++ b/SessionUIKit/Style Guide/Colors.swift @@ -23,7 +23,7 @@ public final class Colors : NSObject { @objc public static var navigationBarBackground: UIColor { UIColor(named: "session_navigation_bar_background")! } @objc public static var searchBarPlaceholder: UIColor { UIColor(named: "session_search_bar_placeholder")! } // Also used for the icons @objc public static var searchBarBackground: UIColor { UIColor(named: "session_search_bar_background")! } - @objc public static var newConversationButtonShadow: UIColor { UIColor(named: "session_new_conversation_button_shadow")! } + @objc public static var expandedButtonGlowColor: UIColor { UIColor(named: "session_expanded_button_glow_color")! } @objc public static var separator: UIColor { UIColor(named: "session_separator")! } @objc public static var unimportantButtonBackground: UIColor { UIColor(named: "session_unimportant_button_background")! } @objc public static var buttonBackground: UIColor { UIColor(named: "session_button_background")! } diff --git a/SessionUIKit/Style Guide/Colors.xcassets/session_new_conversation_button_shadow.colorset/Contents.json b/SessionUIKit/Style Guide/Colors.xcassets/session_expanded_button_glow_color.colorset/Contents.json similarity index 100% rename from SessionUIKit/Style Guide/Colors.xcassets/session_new_conversation_button_shadow.colorset/Contents.json rename to SessionUIKit/Style Guide/Colors.xcassets/session_expanded_button_glow_color.colorset/Contents.json diff --git a/SessionUIKit/Style Guide/Colors.xcassets/session_received_message_background.colorset/Contents.json b/SessionUIKit/Style Guide/Colors.xcassets/session_received_message_background.colorset/Contents.json index 07b1767dd..5c89f71ca 100644 --- a/SessionUIKit/Style Guide/Colors.xcassets/session_received_message_background.colorset/Contents.json +++ b/SessionUIKit/Style Guide/Colors.xcassets/session_received_message_background.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x25", - "green" : "0x23", - "red" : "0x22" + "blue" : "0x2D", + "green" : "0x2D", + "red" : "0x2D" } }, "idiom" : "universal" diff --git a/SessionUIKit/Style Guide/Colors.xcassets/session_sent_message_background.colorset/Contents.json b/SessionUIKit/Style Guide/Colors.xcassets/session_sent_message_background.colorset/Contents.json index 3ecf00826..97b7d83d2 100644 --- a/SessionUIKit/Style Guide/Colors.xcassets/session_sent_message_background.colorset/Contents.json +++ b/SessionUIKit/Style Guide/Colors.xcassets/session_sent_message_background.colorset/Contents.json @@ -5,8 +5,8 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x7B", - "green" : "0xE9", + "blue" : "0x76", + "green" : "0xE0", "red" : "0x00" } }, @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x46", - "green" : "0x41", - "red" : "0x3F" + "blue" : "0x7B", + "green" : "0xE9", + "red" : "0x00" } }, "idiom" : "universal" diff --git a/SessionUIKit/Style Guide/Gradients.swift b/SessionUIKit/Style Guide/Gradients.swift index 65730c225..42994a427 100644 --- a/SessionUIKit/Style Guide/Gradients.swift +++ b/SessionUIKit/Style Guide/Gradients.swift @@ -31,7 +31,7 @@ public final class Gradient : NSObject { @objc(LKGradients) final public class Gradients : NSObject { - @objc public static var defaultLokiBackground: Gradient { + @objc public static var defaultBackground: Gradient { switch AppModeManager.shared.currentAppMode { case .light: return Gradient(start: UIColor(hex: 0xFCFCFC), end: UIColor(hex: 0xFFFFFF)) case .dark: return Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex: 0x121212)) diff --git a/SessionUIKit/Style Guide/Values.swift b/SessionUIKit/Style Guide/Values.swift index fd680f4d2..7b8c490c5 100644 --- a/SessionUIKit/Style Guide/Values.swift +++ b/SessionUIKit/Style Guide/Values.swift @@ -4,12 +4,10 @@ import UIKit public final class Values : NSObject { // MARK: - Alpha Values - @objc public static let unimportantElementOpacity = CGFloat(0.6) - @objc public static let conversationCellTimestampOpacity = CGFloat(0.4) - @objc public static let textFieldBorderOpacity = CGFloat(0.4) - @objc public static let modalBackgroundOpacity = CGFloat(0.75) - @objc public static let composeViewTextFieldBorderOpacity = CGFloat(0.12) - @objc public static let composeViewTextFieldPlaceholderOpacity = CGFloat(0.4) + @objc public static let veryLowOpacity = CGFloat(0.12) + @objc public static let lowOpacity = CGFloat(0.4) + @objc public static let mediumOpacity = CGFloat(0.6) + @objc public static let highOpacity = CGFloat(0.75) // MARK: - Font Sizes @objc public static let verySmallFontSize = isIPhone5OrSmaller ? CGFloat(10) : CGFloat(12) @@ -23,18 +21,18 @@ public final class Values : NSObject { @objc public static let smallButtonHeight = isIPhone5OrSmaller ? CGFloat(24) : CGFloat(27) @objc public static let mediumButtonHeight = isIPhone5OrSmaller ? CGFloat(30) : CGFloat(34) @objc public static let largeButtonHeight = isIPhone5OrSmaller ? CGFloat(40) : CGFloat(45) + @objc public static let accentLineThickness = CGFloat(4) + @objc public static let verySmallProfilePictureSize = CGFloat(26) - @objc public static let smallProfilePictureSize = CGFloat(35) + @objc public static let smallProfilePictureSize = CGFloat(33) @objc public static let mediumProfilePictureSize = CGFloat(45) @objc public static let largeProfilePictureSize = CGFloat(75) - @objc public static let borderThickness = CGFloat(1) - @objc public static let conversationCellStatusIndicatorSize = CGFloat(14) + @objc public static let searchBarHeight = CGFloat(36) - @objc public static let newConversationButtonCollapsedSize = CGFloat(60) - @objc public static let newConversationButtonExpandedSize = CGFloat(72) - @objc public static let textFieldHeight = isIPhone5OrSmaller ? CGFloat(48) : CGFloat(80) - @objc public static let textFieldCornerRadius = CGFloat(8) + + // TODO ---------------- + @objc public static let separatorLabelHeight = CGFloat(24) @objc public static var separatorThickness: CGFloat { return 1 / UIScreen.main.scale } @objc public static let tabBarHeight = isIPhone5OrSmaller ? CGFloat(32) : CGFloat(48) @@ -46,7 +44,7 @@ public final class Values : NSObject { @objc public static let fakeChatBubbleCornerRadius = CGFloat(10) @objc public static let fakeChatViewHeight = isIPhone5OrSmaller ? CGFloat(234) : CGFloat(260) @objc public static let composeViewTextFieldBorderThickness = 1 / UIScreen.main.scale - @objc public static let messageBubbleCornerRadius: CGFloat = 10 + @objc public static let messageBubbleCornerRadius: CGFloat = 8 @objc public static let progressBarThickness: CGFloat = 2 @objc public static let pnOptionCornerRadius = CGFloat(8) @objc public static let pathStatusViewSize = CGFloat(8) diff --git a/SessionUtilitiesKit/General/General.swift b/SessionUtilitiesKit/General/General.swift index d70410bdb..08c97811c 100644 --- a/SessionUtilitiesKit/General/General.swift +++ b/SessionUtilitiesKit/General/General.swift @@ -1,3 +1,5 @@ /// Returns `f(x!)` if `x != nil`, or `nil` otherwise. public func given(_ x: T?, _ f: (T) throws -> U) rethrows -> U? { return try x.map(f) } + +public func with(_ x: T, _ f: (T) throws -> U) rethrows -> U { return try f(x) } diff --git a/SignalUtilitiesKit/Configuration.swift b/SignalUtilitiesKit/Configuration.swift index a498baeb8..0ae9bde47 100644 --- a/SignalUtilitiesKit/Configuration.swift +++ b/SignalUtilitiesKit/Configuration.swift @@ -5,19 +5,10 @@ extension OWSPrimaryStorage : OWSPrimaryStorageProtocol { } @objc(SNConfiguration) public final class Configuration : NSObject { - private static let sharedSenderKeysDelegate = SharedSenderKeysImpl() - - private final class SharedSenderKeysImpl : SharedSenderKeysDelegate { - - func requestSenderKey(for groupPublicKey: String, senderPublicKey: String, using transaction: Any) { - // Do nothing - } - } @objc public static func performMainSetup() { SNMessagingKit.configure(storage: Storage.shared) SNSnodeKit.configure(storage: Storage.shared) - SNProtocolKit.configure(storage: Storage.shared, sharedSenderKeysDelegate: sharedSenderKeysDelegate) SNUtilitiesKit.configure(owsPrimaryStorage: OWSPrimaryStorage.shared(), maxFileSize: UInt(Double(FileServerAPI.maxFileSize) / FileServerAPI.fileSizeORMultiplier)) } } diff --git a/SignalUtilitiesKit/Database/Migrations/ClosedGroupsV2Migration.swift b/SignalUtilitiesKit/Database/Migrations/ClosedGroupsV2Migration.swift deleted file mode 100644 index 95e47eb17..000000000 --- a/SignalUtilitiesKit/Database/Migrations/ClosedGroupsV2Migration.swift +++ /dev/null @@ -1,38 +0,0 @@ - -@objc(SNClosedGroupsV2Migration) -public class ClosedGroupsV2Migration : OWSDatabaseMigration { - - @objc - class func migrationId() -> String { - return "006" - } - - override public func runUp(completion: @escaping OWSDatabaseMigrationCompletion) { - self.doMigrationAsync(completion: completion) - } - - private func doMigrationAsync(completion: @escaping OWSDatabaseMigrationCompletion) { - let publicKeys = Storage.shared.getUserClosedGroupPublicKeys() - var keyPairs: [ECKeyPair] = [] - for publicKey in publicKeys { - guard let privateKey = Storage.shared.getClosedGroupPrivateKey(for: publicKey) else { continue } - do { - let keyPair = try ECKeyPair(publicKeyData: Data(hex: publicKey.removing05PrefixIfNeeded()), privateKeyData: Data(hex: privateKey)) - keyPairs.append(keyPair) - } catch { - // Do nothing - } - } - Storage.write(with: { transaction in - for publicKey in publicKeys { - Storage.shared.addClosedGroupPublicKey(publicKey, using: transaction) - } - for keyPair in keyPairs { - Storage.shared.addClosedGroupEncryptionKeyPair(keyPair, for: keyPair.hexEncodedPublicKey, using: transaction) // In this particular case keyPair.publicKey == groupPublicKey - } - self.save(with: transaction) // Intentionally capture self - }, completion: { - completion() - }) - } -} diff --git a/SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigrationRunner.m b/SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigrationRunner.m index 94b8afd4d..fd8ea2d2c 100644 --- a/SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigrationRunner.m +++ b/SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigrationRunner.m @@ -26,8 +26,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)allMigrations { return @[ - [SNContactsMigration new], - [SNClosedGroupsV2Migration new] + [SNContactsMigration new] ]; } diff --git a/SignalUtilitiesKit/Database/Storage+Conformances.swift b/SignalUtilitiesKit/Database/Storage+Conformances.swift index 2834f277d..21c74ee43 100644 --- a/SignalUtilitiesKit/Database/Storage+Conformances.swift +++ b/SignalUtilitiesKit/Database/Storage+Conformances.swift @@ -1,5 +1,5 @@ -extension Storage : SessionMessagingKitStorageProtocol, SessionProtocolKitStorageProtocol, SessionSnodeKitStorageProtocol { +extension Storage : SessionMessagingKitStorageProtocol, SessionSnodeKitStorageProtocol { public func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set, using transaction: Any) { let transaction = transaction as! YapDatabaseReadWriteTransaction diff --git a/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift b/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift index 9f088e1f8..7c13aae4e 100644 --- a/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift +++ b/SignalUtilitiesKit/Media Viewing & Editing/MediaMessageView.swift @@ -415,6 +415,10 @@ public class MediaMessageView: UIView, OWSAudioPlayerDelegate { OWSAlerts.showErrorAlert(message: NSLocalizedString("INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE", comment: "Message for the alert indicating that an audio file is invalid.")) } + public func audioPlayerDidFinishPlaying(_ player: OWSAudioPlayer, successfully flag: Bool) { + // Do nothing + } + private func ensureButtonState() { if playbackState == .playing { setAudioIconToPause() diff --git a/SignalUtilitiesKit/Messaging/ConversationStyle.swift b/SignalUtilitiesKit/Messaging/ConversationStyle.swift index f64786c2b..d64113a20 100644 --- a/SignalUtilitiesKit/Messaging/ConversationStyle.swift +++ b/SignalUtilitiesKit/Messaging/ConversationStyle.swift @@ -90,18 +90,13 @@ public class ConversationStyle: NSObject { @objc public func updateProperties() { - if thread.isGroupThread() { - gutterLeading = 12 + Values.smallProfilePictureSize + 12 - gutterTrailing = 16 - } else { - gutterLeading = 16 - gutterTrailing = 16 - } - fullWidthGutterLeading = 16 - fullWidthGutterTrailing = 16 - headerGutterLeading = 16 - headerGutterTrailing = 16 - errorGutterTrailing = 16 + gutterLeading = thread.isGroupThread() ? (12 + Values.smallProfilePictureSize + 12) : Values.mediumSpacing + gutterTrailing = Values.mediumSpacing + fullWidthGutterLeading = Values.mediumSpacing + fullWidthGutterTrailing = Values.mediumSpacing + headerGutterLeading = Values.mediumSpacing + headerGutterTrailing = Values.mediumSpacing + errorGutterTrailing = Values.mediumSpacing if thread is TSGroupThread { maxMessageWidth = floor(contentWidth) @@ -206,7 +201,7 @@ public class ConversationStyle: NSObject { @objc public func bubbleSecondaryTextColor(isIncoming: Bool) -> UIColor { - return bubbleTextColor(isIncoming: isIncoming).withAlphaComponent(Values.unimportantElementOpacity) + return bubbleTextColor(isIncoming: isIncoming).withAlphaComponent(Values.mediumOpacity) } @objc diff --git a/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift b/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift index ee7742dd9..7ae6e9ec6 100644 --- a/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift +++ b/SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift @@ -1,4 +1,3 @@ -import SessionProtocolKit import PromiseKit extension MessageSender { diff --git a/SignalUtilitiesKit/Meta/SignalUtilitiesKit-Prefix.pch b/SignalUtilitiesKit/Meta/SignalUtilitiesKit-Prefix.pch index 13ba93736..4ea96ba51 100644 --- a/SignalUtilitiesKit/Meta/SignalUtilitiesKit-Prefix.pch +++ b/SignalUtilitiesKit/Meta/SignalUtilitiesKit-Prefix.pch @@ -12,7 +12,6 @@ @import SignalCoreKit; @import SessionMessagingKit; - @import SessionProtocolKit; @import SessionSnodeKit; @import SessionUtilitiesKit; #endif diff --git a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h index 670ac0efe..a9ac0f9a7 100644 --- a/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h +++ b/SignalUtilitiesKit/Meta/SignalUtilitiesKit.h @@ -4,7 +4,6 @@ FOUNDATION_EXPORT double SignalUtilitiesKitVersionNumber; FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[]; @import SessionMessagingKit; -@import SessionProtocolKit; @import SessionSnodeKit; @import SessionUtilitiesKit; diff --git a/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift b/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift index bb4d30f96..66dcb95dd 100644 --- a/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift +++ b/SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift @@ -139,7 +139,7 @@ public final class ProfilePictureView : UIView { result.layer.masksToBounds = true result.backgroundColor = Colors.unimportant result.layer.borderColor = Colors.text.withAlphaComponent(0.35).cgColor - result.layer.borderWidth = Values.borderThickness + result.layer.borderWidth = 1 result.contentMode = .scaleAspectFit return result } diff --git a/SignalUtilitiesKit/Utilities/AppSetup.m b/SignalUtilitiesKit/Utilities/AppSetup.m index 60f40a92d..c2cf1ffa7 100644 --- a/SignalUtilitiesKit/Utilities/AppSetup.m +++ b/SignalUtilitiesKit/Utilities/AppSetup.m @@ -7,7 +7,6 @@ #import "VersionMigrations.h" #import #import -#import #import #import #import diff --git a/SignalUtilitiesKit/Utilities/AppVersion.m b/SignalUtilitiesKit/Utilities/AppVersion.m index a671e59b6..efea79e1d 100755 --- a/SignalUtilitiesKit/Utilities/AppVersion.m +++ b/SignalUtilitiesKit/Utilities/AppVersion.m @@ -4,7 +4,6 @@ #import "AppVersion.h" #import "NSUserDefaults+OWS.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/ByteParser.m b/SignalUtilitiesKit/Utilities/ByteParser.m index f70baa02a..f8f1f6b10 100644 --- a/SignalUtilitiesKit/Utilities/ByteParser.m +++ b/SignalUtilitiesKit/Utilities/ByteParser.m @@ -3,7 +3,6 @@ // #import "ByteParser.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/FunctionalUtil.m b/SignalUtilitiesKit/Utilities/FunctionalUtil.m index 5ff42f203..830b47812 100644 --- a/SignalUtilitiesKit/Utilities/FunctionalUtil.m +++ b/SignalUtilitiesKit/Utilities/FunctionalUtil.m @@ -3,7 +3,6 @@ // #import "FunctionalUtil.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/NSArray+OWS.m b/SignalUtilitiesKit/Utilities/NSArray+OWS.m index 191e915c4..cb6c06376 100644 --- a/SignalUtilitiesKit/Utilities/NSArray+OWS.m +++ b/SignalUtilitiesKit/Utilities/NSArray+OWS.m @@ -4,7 +4,6 @@ #import "NSArray+OWS.h" #import "TSYapDatabaseObject.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/OWSError.m b/SignalUtilitiesKit/Utilities/OWSError.m index b5cfc1435..b089f235d 100644 --- a/SignalUtilitiesKit/Utilities/OWSError.m +++ b/SignalUtilitiesKit/Utilities/OWSError.m @@ -3,7 +3,6 @@ // #import "OWSError.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/OWSOperation.m b/SignalUtilitiesKit/Utilities/OWSOperation.m index 3b7df2079..5c496949a 100644 --- a/SignalUtilitiesKit/Utilities/OWSOperation.m +++ b/SignalUtilitiesKit/Utilities/OWSOperation.m @@ -5,7 +5,6 @@ #import "OWSOperation.h" #import "OWSBackgroundTask.h" #import "OWSError.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/SignalAccount.m b/SignalUtilitiesKit/Utilities/SignalAccount.m index 36fad15a5..28c9ce906 100644 --- a/SignalUtilitiesKit/Utilities/SignalAccount.m +++ b/SignalUtilitiesKit/Utilities/SignalAccount.m @@ -7,7 +7,6 @@ #import "NSString+SSK.h" #import "OWSPrimaryStorage.h" #import "SignalRecipient.h" -#import NS_ASSUME_NONNULL_BEGIN diff --git a/SignalUtilitiesKit/Utilities/UIViewController+Utilities.swift b/SignalUtilitiesKit/Utilities/UIViewController+Utilities.swift index d9a3a42b6..582f9d38f 100644 --- a/SignalUtilitiesKit/Utilities/UIViewController+Utilities.swift +++ b/SignalUtilitiesKit/Utilities/UIViewController+Utilities.swift @@ -9,7 +9,7 @@ public final class ViewControllerUtilities : NSObject { public static func setUpDefaultSessionStyle(for vc: UIViewController, title: String?, hasCustomBackButton: Bool) { // Set gradient background vc.view.backgroundColor = .clear - let gradient = Gradients.defaultLokiBackground + let gradient = Gradients.defaultBackground vc.view.setGradient(gradient) // Set navigation bar background color if let navigationBar = vc.navigationController?.navigationBar {