mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Start new conversation screen redesign
This commit is contained in:
parent
a292aedf7b
commit
823d6de2e6
12 changed files with 342 additions and 36 deletions
|
@ -591,6 +591,9 @@
|
||||||
B8BB82AB238F669C00BA5194 /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AA238F669C00BA5194 /* ConversationCell.swift */; };
|
B8BB82AB238F669C00BA5194 /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AA238F669C00BA5194 /* ConversationCell.swift */; };
|
||||||
B8BB82AD238F734800BA5194 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */; };
|
B8BB82AD238F734800BA5194 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */; };
|
||||||
B8BB82B12390C37000BA5194 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.swift */; };
|
B8BB82B12390C37000BA5194 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.swift */; };
|
||||||
|
B8BB82B323947E6B00BA5194 /* NewConversationVCV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */; };
|
||||||
|
B8BB82B523947F2D00BA5194 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B423947F2D00BA5194 /* TextField.swift */; };
|
||||||
|
B8BB82B92394911B00BA5194 /* Separator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B82394911B00BA5194 /* Separator.swift */; };
|
||||||
B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
|
B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
|
||||||
B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
|
B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
|
||||||
BFF3FB9730634F37D25903F4 /* Pods_Signal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17BB5C25D615AB49813100C /* Pods_Signal.framework */; };
|
BFF3FB9730634F37D25903F4 /* Pods_Signal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17BB5C25D615AB49813100C /* Pods_Signal.framework */; };
|
||||||
|
@ -1410,6 +1413,9 @@
|
||||||
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationCell.swift; sourceTree = "<group>"; };
|
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationCell.swift; sourceTree = "<group>"; };
|
||||||
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureView.swift; sourceTree = "<group>"; };
|
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureView.swift; sourceTree = "<group>"; };
|
||||||
B8BB82B02390C37000BA5194 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
|
B8BB82B02390C37000BA5194 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
|
||||||
|
B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVCV2.swift; sourceTree = "<group>"; };
|
||||||
|
B8BB82B423947F2D00BA5194 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
|
||||||
|
B8BB82B82394911B00BA5194 /* Separator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Separator.swift; sourceTree = "<group>"; };
|
||||||
B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = "<group>"; };
|
B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = "<group>"; };
|
||||||
B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = "<group>"; };
|
B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = "<group>"; };
|
||||||
B97940251832BD2400BD66CB /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIUtil.h; sourceTree = "<group>"; };
|
B97940251832BD2400BD66CB /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIUtil.h; sourceTree = "<group>"; };
|
||||||
|
@ -2673,6 +2679,8 @@
|
||||||
B8BB82A4238F627000BA5194 /* HomeVC.swift */,
|
B8BB82A4238F627000BA5194 /* HomeVC.swift */,
|
||||||
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */,
|
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */,
|
||||||
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
||||||
|
B8BB82B423947F2D00BA5194 /* TextField.swift */,
|
||||||
|
B8BB82B82394911B00BA5194 /* Separator.swift */,
|
||||||
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */,
|
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */,
|
||||||
);
|
);
|
||||||
path = Loki;
|
path = Loki;
|
||||||
|
@ -2744,6 +2752,7 @@
|
||||||
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
|
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
|
||||||
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
|
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
|
||||||
B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */,
|
B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */,
|
||||||
|
B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */,
|
||||||
24BD2608234DA2050008EB0A /* JoinPublicChatVC.swift */,
|
24BD2608234DA2050008EB0A /* JoinPublicChatVC.swift */,
|
||||||
B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */,
|
B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */,
|
||||||
);
|
);
|
||||||
|
@ -3753,6 +3762,7 @@
|
||||||
files = (
|
files = (
|
||||||
4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */,
|
4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */,
|
||||||
3461293E1FD1D72B00532771 /* ExperienceUpgradeFinder.swift in Sources */,
|
3461293E1FD1D72B00532771 /* ExperienceUpgradeFinder.swift in Sources */,
|
||||||
|
B8BB82B323947E6B00BA5194 /* NewConversationVCV2.swift in Sources */,
|
||||||
34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */,
|
34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */,
|
||||||
34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */,
|
34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */,
|
||||||
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */,
|
452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */,
|
||||||
|
@ -3785,6 +3795,7 @@
|
||||||
B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */,
|
B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */,
|
||||||
4CFD151D22415AA400F2450F /* CallVideoHintView.swift in Sources */,
|
4CFD151D22415AA400F2450F /* CallVideoHintView.swift in Sources */,
|
||||||
34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */,
|
34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */,
|
||||||
|
B8BB82B92394911B00BA5194 /* Separator.swift in Sources */,
|
||||||
343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */,
|
343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */,
|
||||||
34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */,
|
34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */,
|
||||||
34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */,
|
34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */,
|
||||||
|
@ -3937,6 +3948,7 @@
|
||||||
3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */,
|
3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */,
|
||||||
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
|
45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */,
|
||||||
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */,
|
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */,
|
||||||
|
B8BB82B523947F2D00BA5194 /* TextField.swift in Sources */,
|
||||||
34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */,
|
34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */,
|
||||||
457F671B20746193000EABCD /* QuotedReplyPreview.swift in Sources */,
|
457F671B20746193000EABCD /* QuotedReplyPreview.swift in Sources */,
|
||||||
34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */,
|
34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */,
|
||||||
|
|
|
@ -877,7 +877,7 @@ static NSTimeInterval launchStartedAt;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [SignalApp.sharedApp.homeViewController showNewConversationVC];
|
[SignalApp.sharedApp.homeViewController createPrivateChat];
|
||||||
|
|
||||||
completionHandler(YES);
|
completionHandler(YES);
|
||||||
}];
|
}];
|
||||||
|
|
|
@ -32,6 +32,26 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
|
||||||
return result
|
return result
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var newConversationButton: UIButton = {
|
||||||
|
let result = UIButton()
|
||||||
|
result.setTitle("+", for: UIControl.State.normal)
|
||||||
|
result.titleLabel!.font = .systemFont(ofSize: 35)
|
||||||
|
result.setTitleColor(UIColor(hex: 0x121212), for: UIControl.State.normal)
|
||||||
|
result.titleEdgeInsets = UIEdgeInsets(top: 0, left: 1, bottom: 4, right: 0) // Slight adjustment to make the plus exactly centered
|
||||||
|
result.backgroundColor = Colors.accent
|
||||||
|
let size = Values.newConversationButtonSize
|
||||||
|
result.layer.cornerRadius = size / 2
|
||||||
|
result.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size, height: size))).cgPath
|
||||||
|
result.layer.shadowColor = Colors.newConversationButtonShadow.cgColor
|
||||||
|
result.layer.shadowOffset = CGSize(width: 0, height: 0.8)
|
||||||
|
result.layer.shadowOpacity = 1
|
||||||
|
result.layer.shadowRadius = 6
|
||||||
|
result.layer.masksToBounds = false
|
||||||
|
result.set(.width, to: size)
|
||||||
|
result.set(.height, to: size)
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
// MARK: Lifecycle
|
// MARK: Lifecycle
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
// Set gradient background
|
// Set gradient background
|
||||||
|
@ -63,22 +83,7 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
|
||||||
searchBar.sizeToFit()
|
searchBar.sizeToFit()
|
||||||
tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height)
|
tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height)
|
||||||
// Set up new conversation button
|
// Set up new conversation button
|
||||||
let newConversationButton = UIButton()
|
newConversationButton.addTarget(self, action: #selector(createPrivateChat), for: UIControl.Event.touchUpInside)
|
||||||
newConversationButton.setTitle("+", for: UIControl.State.normal)
|
|
||||||
newConversationButton.titleLabel!.font = .systemFont(ofSize: 35)
|
|
||||||
newConversationButton.setTitleColor(UIColor(hex: 0x121212), for: UIControl.State.normal)
|
|
||||||
newConversationButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 1, bottom: 4, right: 0) // Slight adjustment to make the plus exactly centered
|
|
||||||
newConversationButton.backgroundColor = Colors.accent
|
|
||||||
let newConversationButtonSize = Values.newConversationButtonSize
|
|
||||||
newConversationButton.layer.cornerRadius = newConversationButtonSize / 2
|
|
||||||
newConversationButton.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: newConversationButtonSize, height: newConversationButtonSize))).cgPath
|
|
||||||
newConversationButton.layer.shadowColor = Colors.newConversationButtonShadow.cgColor
|
|
||||||
newConversationButton.layer.shadowOffset = CGSize(width: 0, height: 0.8)
|
|
||||||
newConversationButton.layer.shadowOpacity = 1
|
|
||||||
newConversationButton.layer.shadowRadius = 6
|
|
||||||
newConversationButton.layer.masksToBounds = false
|
|
||||||
newConversationButton.set(.width, to: newConversationButtonSize)
|
|
||||||
newConversationButton.set(.height, to: newConversationButtonSize)
|
|
||||||
view.addSubview(newConversationButton)
|
view.addSubview(newConversationButton)
|
||||||
newConversationButton.center(.horizontal, in: view)
|
newConversationButton.center(.horizontal, in: view)
|
||||||
newConversationButton.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up
|
newConversationButton.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up
|
||||||
|
@ -229,14 +234,14 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
guard let thread = self.thread(at: indexPath.row) else { return }
|
guard let thread = self.thread(at: indexPath.row) else { return }
|
||||||
show(thread, with: ConversationViewAction.none, animated: true)
|
show(thread, with: ConversationViewAction.none, highlightedMessageID: nil, animated: true)
|
||||||
tableView.deselectRow(at: indexPath, animated: true)
|
tableView.deselectRow(at: indexPath, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func show(_ thread: TSThread, with action: ConversationViewAction, animated: Bool) {
|
@objc func show(_ thread: TSThread, with action: ConversationViewAction, highlightedMessageID: String?, animated: Bool) {
|
||||||
DispatchMainThreadSafe {
|
DispatchMainThreadSafe {
|
||||||
let conversationVC = ConversationViewController()
|
let conversationVC = ConversationViewController()
|
||||||
conversationVC.configure(for: thread, action: action, focusMessageId: nil) // TODO: focusMessageId
|
conversationVC.configure(for: thread, action: action, focusMessageId: highlightedMessageID)
|
||||||
self.navigationController?.setViewControllers([ self, conversationVC ], animated: true)
|
self.navigationController?.setViewControllers([ self, conversationVC ], animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,6 +300,12 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func createPrivateChat() {
|
||||||
|
let newConversationVC = NewConversationVCV2()
|
||||||
|
let navigationController = OWSNavigationController(rootViewController: newConversationVC)
|
||||||
|
present(navigationController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Convenience
|
// MARK: Convenience
|
||||||
private func thread(at index: Int) -> TSThread? {
|
private func thread(at index: Int) -> TSThread? {
|
||||||
var thread: TSThread? = nil
|
var thread: TSThread? = nil
|
||||||
|
|
170
Signal/src/Loki/Messaging/NewConversationVCV2.swift
Normal file
170
Signal/src/Loki/Messaging/NewConversationVCV2.swift
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
|
||||||
|
@objc(LKNewConversationVCV2)
|
||||||
|
final class NewConversationVCV2 : OWSViewController, OWSQRScannerDelegate {
|
||||||
|
|
||||||
|
private lazy var userHexEncodedPublicKey: String = {
|
||||||
|
let userDefaults = UserDefaults.standard
|
||||||
|
if let masterHexEncodedPublicKey = userDefaults.string(forKey: "masterDeviceHexEncodedPublicKey") {
|
||||||
|
return masterHexEncodedPublicKey
|
||||||
|
} else {
|
||||||
|
return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: Components
|
||||||
|
private lazy var publicKeyTextField = TextField(placeholder: NSLocalizedString("Enter public key of recipient", comment: ""))
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
override func viewDidLoad() {
|
||||||
|
// Set gradient background
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
let gradient = Gradients.defaultLokiBackground
|
||||||
|
view.setGradient(gradient)
|
||||||
|
// Set navigation bar background color
|
||||||
|
if let navigationBar = navigationController?.navigationBar {
|
||||||
|
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||||
|
navigationBar.shadowImage = UIImage()
|
||||||
|
navigationBar.isTranslucent = false
|
||||||
|
navigationBar.barTintColor = Colors.navigationBarBackground
|
||||||
|
}
|
||||||
|
// Set up the navigation bar buttons
|
||||||
|
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||||
|
closeButton.tintColor = Colors.text
|
||||||
|
navigationItem.leftBarButtonItem = closeButton
|
||||||
|
// Customize title
|
||||||
|
let titleLabel = UILabel()
|
||||||
|
titleLabel.text = NSLocalizedString("New Conversation", comment: "")
|
||||||
|
titleLabel.textColor = Colors.text
|
||||||
|
titleLabel.font = UIFont.boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||||
|
navigationItem.titleView = titleLabel
|
||||||
|
// Set up explanation label
|
||||||
|
let explanationLabel = UILabel()
|
||||||
|
explanationLabel.textColor = Colors.unimportant
|
||||||
|
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
explanationLabel.text = NSLocalizedString("Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code.", comment: "")
|
||||||
|
explanationLabel.numberOfLines = 0
|
||||||
|
explanationLabel.textAlignment = .center
|
||||||
|
explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
// Set up separator
|
||||||
|
let separator = Separator(title: NSLocalizedString("Your Public Key", comment: ""))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // Background color & margins
|
||||||
|
// view.backgroundColor = Theme.backgroundColor
|
||||||
|
// view.layoutMargins = .zero
|
||||||
|
// // Navigation bar
|
||||||
|
// navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close))
|
||||||
|
// title = NSLocalizedString("New Conversation", comment: "")
|
||||||
|
// // Separator
|
||||||
|
// let separator = UIView()
|
||||||
|
// separator.autoSetDimension(.height, toSize: 1 / UIScreen.main.scale)
|
||||||
|
// separator.backgroundColor = Theme.hairlineColor
|
||||||
|
// // Explanation label
|
||||||
|
// let explanationLabel = UILabel()
|
||||||
|
// explanationLabel.textColor = Theme.primaryColor
|
||||||
|
// explanationLabel.font = UIFont.ows_dynamicTypeSubheadlineClamped
|
||||||
|
// explanationLabel.text = NSLocalizedString("Enter the public key of the person you'd like to securely message. They can share their public key with you by going into Loki Messenger's in-app settings and clicking \"Share Public Key\".", comment: "")
|
||||||
|
// explanationLabel.numberOfLines = 0
|
||||||
|
// explanationLabel.lineBreakMode = .byWordWrapping
|
||||||
|
// // QR code button
|
||||||
|
// let qrCodeButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight()
|
||||||
|
// let qrCodeButtonHeight = qrCodeButtonFont.pointSize * 48 / 17
|
||||||
|
// let qrCodeButton = OWSFlatButton.button(title: NSLocalizedString("Scan a QR Code Instead", comment: ""), font: qrCodeButtonFont, titleColor: .lokiGreen(), backgroundColor: .clear, target: self, selector: #selector(scanQRCode))
|
||||||
|
// qrCodeButton.setBackgroundColors(upColor: .clear, downColor: .clear)
|
||||||
|
// qrCodeButton.autoSetDimension(.height, toSize: qrCodeButtonHeight)
|
||||||
|
// qrCodeButton.button.contentHorizontalAlignment = .left
|
||||||
|
// // Next button
|
||||||
|
// let nextButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight()
|
||||||
|
// let nextButtonHeight = nextButtonFont.pointSize * 48 / 17
|
||||||
|
// let nextButton = OWSFlatButton.button(title: NSLocalizedString("Next", comment: ""), font: nextButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleNextButtonTapped))
|
||||||
|
// nextButton.autoSetDimension(.height, toSize: nextButtonHeight)
|
||||||
|
// Stack view
|
||||||
|
let stackView = UIStackView(arrangedSubviews: [ publicKeyTextField, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, UIView.spacer(withHeight: Values.veryLargeSpacing), separator, UIView.vStretchingSpacer() ])
|
||||||
|
stackView.axis = .vertical
|
||||||
|
stackView.alignment = .fill
|
||||||
|
stackView.layoutMargins = UIEdgeInsets(top: Values.mediumSpacing, left: Values.largeSpacing, bottom: Values.mediumSpacing, right: Values.largeSpacing)
|
||||||
|
stackView.isLayoutMarginsRelativeArrangement = true
|
||||||
|
view.addSubview(stackView)
|
||||||
|
stackView.pin(to: view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
publicKeyTextField.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: General
|
||||||
|
@objc private func enableCopyButton() {
|
||||||
|
// copyButton.isUserInteractionEnabled = true
|
||||||
|
// UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||||
|
// self.copyButton.setTitle(NSLocalizedString("Copy", comment: ""))
|
||||||
|
// }, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Interaction
|
||||||
|
@objc private func close() {
|
||||||
|
dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func copyPublicKey() {
|
||||||
|
// UIPasteboard.general.string = userHexEncodedPublicKey
|
||||||
|
// copyButton.isUserInteractionEnabled = false
|
||||||
|
// UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||||
|
// self.copyButton.setTitle(NSLocalizedString("Copied ✓", comment: ""))
|
||||||
|
// }, completion: nil)
|
||||||
|
// Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func sharePublicKey() {
|
||||||
|
let shareVC = UIActivityViewController(activityItems: [ userHexEncodedPublicKey ], applicationActivities: nil)
|
||||||
|
present(shareVC, animated: true, completion: nil)
|
||||||
|
// NSString *hexEncodedPublicKey;
|
||||||
|
// NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"];
|
||||||
|
// if (masterDeviceHexEncodedPublicKey != nil) {
|
||||||
|
// hexEncodedPublicKey = masterDeviceHexEncodedPublicKey;
|
||||||
|
// } else {
|
||||||
|
// hexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
|
||||||
|
// }
|
||||||
|
// UIActivityViewController *shareVC = [[UIActivityViewController alloc] initWithActivityItems:@[ hexEncodedPublicKey ] applicationActivities:nil];
|
||||||
|
// [self presentViewController:shareVC animated:YES completion:nil];
|
||||||
|
// [LKAnalytics.shared track:@"Public Key Shared"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// @objc private func scanQRCode() {
|
||||||
|
// ows_ask(forCameraPermissions: { [weak self] hasCameraAccess in
|
||||||
|
// if hasCameraAccess {
|
||||||
|
// let message = NSLocalizedString("Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\".", comment: "")
|
||||||
|
// let scanQRCodeWrapperVC = ScanQRCodeWrapperVC(message: message)
|
||||||
|
// scanQRCodeWrapperVC.delegate = self
|
||||||
|
// self?.navigationController!.pushViewController(scanQRCodeWrapperVC, animated: true)
|
||||||
|
// } else {
|
||||||
|
// // Do nothing
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
|
||||||
|
// Analytics.shared.track("QR Code Scanned")
|
||||||
|
// let hexEncodedPublicKey = string
|
||||||
|
// startNewConversationIfPossible(with: hexEncodedPublicKey)
|
||||||
|
// }
|
||||||
|
|
||||||
|
@objc private func handleNextButtonTapped() {
|
||||||
|
let hexEncodedPublicKey = publicKeyTextField.text?.trimmingCharacters(in: .whitespaces) ?? ""
|
||||||
|
startNewConversationIfPossible(with: hexEncodedPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startNewConversationIfPossible(with hexEncodedPublicKey: String) {
|
||||||
|
if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
|
||||||
|
let alert = UIAlertController(title: NSLocalizedString("Invalid Public Key", comment: ""), message: NSLocalizedString("Please check the public key you entered and try again.", comment: ""), preferredStyle: .alert)
|
||||||
|
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
|
||||||
|
presentAlert(alert)
|
||||||
|
} else {
|
||||||
|
let thread = TSContactThread.getOrCreateThread(contactId: hexEncodedPublicKey)
|
||||||
|
Analytics.shared.track("New Conversation Started")
|
||||||
|
SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false)
|
||||||
|
presentingViewController!.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,15 +14,15 @@ final class ProfilePictureView : UIView {
|
||||||
// MARK: Lifecycle
|
// MARK: Lifecycle
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
initialize()
|
setUpViewHierarchy()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
initialize()
|
setUpViewHierarchy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initialize() {
|
private func setUpViewHierarchy() {
|
||||||
// Set up image view
|
// Set up image view
|
||||||
addSubview(imageView)
|
addSubview(imageView)
|
||||||
imageView.pin(.leading, to: .leading, of: self)
|
imageView.pin(.leading, to: .leading, of: self)
|
||||||
|
@ -82,8 +82,8 @@ final class ProfilePictureView : UIView {
|
||||||
let result = UIImageView()
|
let result = UIImageView()
|
||||||
result.layer.masksToBounds = true
|
result.layer.masksToBounds = true
|
||||||
result.backgroundColor = Colors.unimportant
|
result.backgroundColor = Colors.unimportant
|
||||||
result.layer.borderColor = Colors.profilePictureBorder.cgColor
|
result.layer.borderColor = Colors.border.cgColor
|
||||||
result.layer.borderWidth = Values.profilePictureBorderThickness
|
result.layer.borderWidth = Values.borderThickness
|
||||||
result.contentMode = .scaleAspectFit
|
result.contentMode = .scaleAspectFit
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,15 @@ final class SearchBar : UISearchBar {
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
update()
|
setUpStyle()
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
update()
|
setUpStyle()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update() {
|
private func setUpStyle() {
|
||||||
searchBarStyle = .minimal // Hide the border around the search bar
|
searchBarStyle = .minimal // Hide the border around the search bar
|
||||||
barStyle = .black // Use Apple's black design as a base
|
barStyle = .black // Use Apple's black design as a base
|
||||||
tintColor = Colors.accent // The cursor color
|
tintColor = Colors.accent // The cursor color
|
||||||
|
|
65
Signal/src/Loki/Separator.swift
Normal file
65
Signal/src/Loki/Separator.swift
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
|
||||||
|
final class Separator : UIView {
|
||||||
|
private let title: String
|
||||||
|
|
||||||
|
// MARK: Components
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let result = UILabel()
|
||||||
|
result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
|
||||||
|
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
result.textAlignment = .center
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var lineLayer: CAShapeLayer = {
|
||||||
|
let result = CAShapeLayer()
|
||||||
|
result.lineWidth = Values.separatorThickness
|
||||||
|
result.strokeColor = Colors.separator.cgColor
|
||||||
|
result.fillColor = UIColor.clear.cgColor
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
// MARK: Initialization
|
||||||
|
init(title: String) {
|
||||||
|
self.title = title
|
||||||
|
super.init(frame: CGRect.zero)
|
||||||
|
setUpViewHierarchy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
preconditionFailure("Use init(title:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
preconditionFailure("Use init(title:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpViewHierarchy() {
|
||||||
|
titleLabel.text = title
|
||||||
|
addSubview(titleLabel)
|
||||||
|
titleLabel.center(.horizontal, in: self)
|
||||||
|
titleLabel.center(.vertical, in: self)
|
||||||
|
layer.insertSublayer(lineLayer, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Updating
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
updateLineLayer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLineLayer() {
|
||||||
|
let w = width()
|
||||||
|
let h = height()
|
||||||
|
let path = UIBezierPath()
|
||||||
|
path.move(to: CGPoint(x: 0, y: h / 2))
|
||||||
|
let titleLabelFrame = titleLabel.frame.insetBy(dx: -10, dy: -6)
|
||||||
|
path.addLine(to: CGPoint(x: titleLabelFrame.origin.x, y: h / 2))
|
||||||
|
let oval = UIBezierPath(roundedRect: titleLabelFrame, cornerRadius: Values.separatorCornerRadius)
|
||||||
|
path.append(oval)
|
||||||
|
path.move(to: CGPoint(x: titleLabelFrame.origin.x + titleLabelFrame.width, y: h / 2))
|
||||||
|
path.addLine(to: CGPoint(x: w, y: h / 2))
|
||||||
|
path.close()
|
||||||
|
lineLayer.path = path.cgPath
|
||||||
|
}
|
||||||
|
}
|
40
Signal/src/Loki/TextField.swift
Normal file
40
Signal/src/Loki/TextField.swift
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
final class TextField : UITextField {
|
||||||
|
|
||||||
|
init(placeholder: String) {
|
||||||
|
super.init(frame: CGRect.zero)
|
||||||
|
self.placeholder = placeholder
|
||||||
|
setUpStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
preconditionFailure("Use init(placeholder:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
preconditionFailure("Use init(placeholder:) instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpStyle() {
|
||||||
|
textColor = Colors.text
|
||||||
|
font = .systemFont(ofSize: Values.smallFontSize)
|
||||||
|
let placeholder = NSMutableAttributedString(string: self.placeholder!)
|
||||||
|
let placeholderColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
|
||||||
|
placeholder.addAttribute(.foregroundColor, value: placeholderColor, range: NSRange(location: 0, length: placeholder.length))
|
||||||
|
attributedPlaceholder = placeholder
|
||||||
|
tintColor = Colors.accent
|
||||||
|
keyboardAppearance = .dark
|
||||||
|
set(.height, to: Values.textFieldHeight)
|
||||||
|
layer.borderColor = Colors.border.withAlphaComponent(Values.textFieldBorderOpacity).cgColor
|
||||||
|
layer.borderWidth = Values.borderThickness
|
||||||
|
layer.cornerRadius = Values.textFieldCornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
override func textRect(forBounds bounds: CGRect) -> CGRect {
|
||||||
|
return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func editingRect(forBounds bounds: CGRect) -> CGRect {
|
||||||
|
return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,12 @@ final class Colors : NSObject {
|
||||||
@objc static let accent = UIColor(hex: 0x00F782)
|
@objc static let accent = UIColor(hex: 0x00F782)
|
||||||
@objc static let text = UIColor(hex: 0xFFFFFF)
|
@objc static let text = UIColor(hex: 0xFFFFFF)
|
||||||
@objc static let unimportant = UIColor(hex: 0xD8D8D8)
|
@objc static let unimportant = UIColor(hex: 0xD8D8D8)
|
||||||
@objc static let profilePictureBorder = UIColor(hex: 0x979797)
|
@objc static let border = UIColor(hex: 0x979797)
|
||||||
@objc static let conversationCellBackground = UIColor(hex: 0x1B1B1B)
|
@objc static let conversationCellBackground = UIColor(hex: 0x1B1B1B)
|
||||||
@objc static let conversationCellSelected = UIColor(hex: 0x0C0C0C)
|
@objc static let conversationCellSelected = UIColor(hex: 0x0C0C0C)
|
||||||
@objc static let navigationBarBackground = UIColor(hex: 0x161616)
|
@objc static let navigationBarBackground = UIColor(hex: 0x161616)
|
||||||
@objc static let searchBarPlaceholder = UIColor(hex: 0x8E8E93) // Also used for the icons
|
@objc static let searchBarPlaceholder = UIColor(hex: 0x8E8E93) // Also used for the icons
|
||||||
@objc static let searchBarBackground = UIColor(red: 142 / 255, green: 142 / 255, blue: 147 / 255, alpha: 0.12)
|
@objc static let searchBarBackground = UIColor(red: 142 / 255, green: 142 / 255, blue: 147 / 255, alpha: 0.12)
|
||||||
@objc static let newConversationButtonShadow = UIColor(hex: 0x077C44)
|
@objc static let newConversationButtonShadow = UIColor(hex: 0x077C44)
|
||||||
|
@objc static let separator = UIColor(hex: 0x36383C)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
final class Values : NSObject {
|
final class Values : NSObject {
|
||||||
|
|
||||||
// MARK: - Alpha Values
|
// MARK: - Alpha Values
|
||||||
@objc static let inactiveElementOpacity = CGFloat(0.6)
|
@objc static let unimportantElementOpacity = CGFloat(0.6)
|
||||||
@objc static let conversationCellTimestampOpacity = CGFloat(0.4)
|
@objc static let conversationCellTimestampOpacity = CGFloat(0.4)
|
||||||
|
@objc static let textFieldBorderOpacity = CGFloat(0.4)
|
||||||
|
|
||||||
// MARK: - Font Sizes
|
// MARK: - Font Sizes
|
||||||
@objc static let smallFontSize = CGFloat(13)
|
@objc static let smallFontSize = CGFloat(13)
|
||||||
|
@ -20,14 +21,19 @@ final class Values : NSObject {
|
||||||
@objc static let smallProfilePictureSize = CGFloat(35)
|
@objc static let smallProfilePictureSize = CGFloat(35)
|
||||||
@objc static let mediumProfilePictureSize = CGFloat(45)
|
@objc static let mediumProfilePictureSize = CGFloat(45)
|
||||||
@objc static let largeProfilePictureSize = CGFloat(65)
|
@objc static let largeProfilePictureSize = CGFloat(65)
|
||||||
@objc static let profilePictureBorderThickness = CGFloat(1)
|
@objc static let borderThickness = CGFloat(1)
|
||||||
@objc static let conversationCellStatusIndicatorSize = CGFloat(14)
|
@objc static let conversationCellStatusIndicatorSize = CGFloat(14)
|
||||||
@objc static let searchBarHeight = CGFloat(36)
|
@objc static let searchBarHeight = CGFloat(36)
|
||||||
@objc static let newConversationButtonSize = CGFloat(45)
|
@objc static let newConversationButtonSize = CGFloat(45)
|
||||||
|
@objc static let textFieldHeight = CGFloat(80)
|
||||||
|
@objc static let textFieldCornerRadius = CGFloat(8)
|
||||||
|
@objc static let separatorCornerRadius = CGFloat(10)
|
||||||
|
@objc static var separatorThickness: CGFloat { return 1 / UIScreen.main.scale }
|
||||||
|
|
||||||
// MARK: - Distances
|
// MARK: - Distances
|
||||||
@objc static let smallSpacing = CGFloat(8)
|
@objc static let smallSpacing = CGFloat(8)
|
||||||
@objc static let mediumSpacing = CGFloat(16)
|
@objc static let mediumSpacing = CGFloat(16)
|
||||||
@objc static let largeSpacing = CGFloat(24)
|
@objc static let largeSpacing = CGFloat(24)
|
||||||
|
@objc static let veryLargeSpacing = CGFloat(35)
|
||||||
@objc static let newConversationButtonBottomOffset = CGFloat(52)
|
@objc static let newConversationButtonBottomOffset = CGFloat(52)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [self.homeViewController presentThread:thread action:action focusMessageId:focusMessageId animated:isAnimated];
|
[self.homeViewController show:thread with:action highlightedMessageID:focusMessageId animated:isAnimated];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,10 +145,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [self.homeViewController presentThread:thread
|
[self.homeViewController show:thread with:ConversationViewActionNone highlightedMessageID:nil animated:isAnimated];
|
||||||
// action:ConversationViewActionNone
|
|
||||||
// focusMessageId:nil
|
|
||||||
// animated:isAnimated];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2679,3 +2679,7 @@
|
||||||
"New Group" = "New Group";
|
"New Group" = "New Group";
|
||||||
"Delete" = "Delete";
|
"Delete" = "Delete";
|
||||||
"Search" = "Search";
|
"Search" = "Search";
|
||||||
|
"New Conversation" = "New Conversation";
|
||||||
|
"Enter public key of recipient" = "Enter public key of recipient";
|
||||||
|
"Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code." = "Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code.";
|
||||||
|
"Your Public Key" = "Your Public Key";
|
||||||
|
|
Loading…
Reference in a new issue