Start home screen redesign
This commit is contained in:
parent
10defafc2f
commit
177b194d67
|
@ -574,7 +574,7 @@
|
|||
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
|
||||
B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; };
|
||||
B885D5F62334A32100EE0D8E /* UIView+Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */; };
|
||||
B885D5F62334A32100EE0D8E /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */; };
|
||||
B891105C2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; };
|
||||
B891105E2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; };
|
||||
B891105F2320872800F15FCC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B891105B2320872800F15FCC /* GoogleService-Info.plist */; };
|
||||
|
@ -584,8 +584,12 @@
|
|||
B89841E322B7579F00B1BDC6 /* NewConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */; };
|
||||
B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */; };
|
||||
B8B26C91234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */; };
|
||||
B8BB82A0238F322400BA5194 /* UIColor+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB829F238F322400BA5194 /* UIColor+Loki.swift */; };
|
||||
B8BB82A2238F356100BA5194 /* CGFloat+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A1238F356100BA5194 /* CGFloat+Loki.swift */; };
|
||||
B8BB82A0238F322400BA5194 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB829F238F322400BA5194 /* Colors.swift */; };
|
||||
B8BB82A2238F356100BA5194 /* Values.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A1238F356100BA5194 /* Values.swift */; };
|
||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A4238F627000BA5194 /* HomeVC.swift */; };
|
||||
B8BB82A9238F62FB00BA5194 /* Gradients.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82A8238F62FB00BA5194 /* Gradients.swift */; };
|
||||
B8BB82AB238F669C00BA5194 /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AA238F669C00BA5194 /* ConversationCell.swift */; };
|
||||
B8BB82AD238F734800BA5194 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */; };
|
||||
B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
|
||||
B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; };
|
||||
BFF3FB9730634F37D25903F4 /* Pods_Signal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17BB5C25D615AB49813100C /* Pods_Signal.framework */; };
|
||||
|
@ -1390,7 +1394,7 @@
|
|||
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
||||
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
|
||||
B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = "<group>"; };
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraint.swift"; sourceTree = "<group>"; };
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = "<group>"; };
|
||||
B891105B2320872800F15FCC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeWrapperVC.swift; sourceTree = "<group>"; };
|
||||
B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModalDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -1398,8 +1402,12 @@
|
|||
B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVC.swift; sourceTree = "<group>"; };
|
||||
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionCandidateSelectionView.swift; sourceTree = "<group>"; };
|
||||
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionCandidateSelectionViewDelegate.swift; sourceTree = "<group>"; };
|
||||
B8BB829F238F322400BA5194 /* UIColor+Loki.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Loki.swift"; sourceTree = "<group>"; };
|
||||
B8BB82A1238F356100BA5194 /* CGFloat+Loki.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Loki.swift"; sourceTree = "<group>"; };
|
||||
B8BB829F238F322400BA5194 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
|
||||
B8BB82A1238F356100BA5194 /* Values.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Values.swift; sourceTree = "<group>"; };
|
||||
B8BB82A4238F627000BA5194 /* HomeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeVC.swift; sourceTree = "<group>"; };
|
||||
B8BB82A8238F62FB00BA5194 /* Gradients.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gradients.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>"; };
|
||||
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>"; };
|
||||
B97940251832BD2400BD66CB /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIUtil.h; sourceTree = "<group>"; };
|
||||
|
@ -2660,6 +2668,9 @@
|
|||
B86BD0872339A1ED000F5AE3 /* Onboarding */,
|
||||
B86BD08223399ABF000F5AE3 /* Settings */,
|
||||
B86BD0882339A253000F5AE3 /* Utilities */,
|
||||
B8BB82A4238F627000BA5194 /* HomeVC.swift */,
|
||||
B8BB82AA238F669C00BA5194 /* ConversationCell.swift */,
|
||||
B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */,
|
||||
);
|
||||
path = Loki;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2705,7 +2716,7 @@
|
|||
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
||||
B86BD08323399ACF000F5AE3 /* Modal.swift */,
|
||||
B8BB82A3238F356800BA5194 /* Style Guide */,
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */,
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2713,8 +2724,9 @@
|
|||
B8BB82A3238F356800BA5194 /* Style Guide */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B8BB82A1238F356100BA5194 /* CGFloat+Loki.swift */,
|
||||
B8BB829F238F322400BA5194 /* UIColor+Loki.swift */,
|
||||
B8BB829F238F322400BA5194 /* Colors.swift */,
|
||||
B8BB82A8238F62FB00BA5194 /* Gradients.swift */,
|
||||
B8BB82A1238F356100BA5194 /* Values.swift */,
|
||||
);
|
||||
path = "Style Guide";
|
||||
sourceTree = "<group>";
|
||||
|
@ -3777,7 +3789,7 @@
|
|||
4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */,
|
||||
EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */,
|
||||
45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */,
|
||||
B8BB82A0238F322400BA5194 /* UIColor+Loki.swift in Sources */,
|
||||
B8BB82A0238F322400BA5194 /* Colors.swift in Sources */,
|
||||
34D2CCE0206939B400CB1A14 /* DebugUIMessagesAssetLoader.m in Sources */,
|
||||
4CEB78C92178EBAB00F315D2 /* OWSSessionResetJobRecord.m in Sources */,
|
||||
45794E861E00620000066731 /* CallUIAdapter.swift in Sources */,
|
||||
|
@ -3809,7 +3821,8 @@
|
|||
B80C6B572384A56D00FDBC8B /* DeviceLinksVC.swift in Sources */,
|
||||
34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */,
|
||||
34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */,
|
||||
B8BB82A2238F356100BA5194 /* CGFloat+Loki.swift in Sources */,
|
||||
B8BB82A2238F356100BA5194 /* Values.swift in Sources */,
|
||||
B8BB82AB238F669C00BA5194 /* ConversationCell.swift in Sources */,
|
||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
|
||||
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
|
||||
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
|
||||
|
@ -3822,6 +3835,7 @@
|
|||
D221A09A169C9E5E00537ABF /* main.m in Sources */,
|
||||
3496957221A301A100DCFE74 /* OWSBackup.m in Sources */,
|
||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */,
|
||||
B8BB82A9238F62FB00BA5194 /* Gradients.swift in Sources */,
|
||||
34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */,
|
||||
3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */,
|
||||
34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */,
|
||||
|
@ -3863,6 +3877,7 @@
|
|||
4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */,
|
||||
3448E16022134C89004B052E /* OnboardingSplashViewController.swift in Sources */,
|
||||
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
|
||||
B8BB82AD238F734800BA5194 /* ProfilePictureView.swift in Sources */,
|
||||
B8162F0322891AD600D46544 /* FriendRequestView.swift in Sources */,
|
||||
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
|
||||
34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */,
|
||||
|
@ -3880,7 +3895,7 @@
|
|||
34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */,
|
||||
B821F2F82272CED3002C88C0 /* DisplayNameVC.swift in Sources */,
|
||||
34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */,
|
||||
B885D5F62334A32100EE0D8E /* UIView+Constraint.swift in Sources */,
|
||||
B885D5F62334A32100EE0D8E /* UIView+Constraints.swift in Sources */,
|
||||
24BD2609234DA2050008EB0A /* NewPublicChatVC.swift in Sources */,
|
||||
34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */,
|
||||
34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */,
|
||||
|
@ -3934,6 +3949,7 @@
|
|||
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
||||
34BECE2B1F74C12700D7438D /* DebugUIStress.m in Sources */,
|
||||
340FC8B9204DAC8D007AEB0F /* UpdateGroupViewController.m in Sources */,
|
||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
||||
3448E1662215B313004B052E /* OnboardingCaptchaViewController.swift in Sources */,
|
||||
4574A5D61DD6704700C6B692 /* CallService.swift in Sources */,
|
||||
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
|
||||
|
|
|
@ -877,7 +877,7 @@ static NSTimeInterval launchStartedAt;
|
|||
return;
|
||||
}
|
||||
|
||||
[SignalApp.sharedApp.homeViewController showNewConversationVC];
|
||||
// [SignalApp.sharedApp.homeViewController showNewConversationVC];
|
||||
|
||||
completionHandler(YES);
|
||||
}];
|
||||
|
@ -1418,7 +1418,7 @@ static NSTimeInterval launchStartedAt;
|
|||
if (self.backup.hasPendingRestoreDecision) {
|
||||
rootViewController = [BackupRestoreViewController new];
|
||||
} else {
|
||||
rootViewController = [HomeViewController new];
|
||||
rootViewController = [HomeVC new];
|
||||
}
|
||||
} else {
|
||||
rootViewController = [[OnboardingController new] initialViewController];
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
final class ConversationCell : UITableViewCell {
|
||||
public var threadViewModel: ThreadViewModel! { didSet { update() } }
|
||||
|
||||
public static let reuseIdentifier = "ConversationCell"
|
||||
|
||||
// MARK: Components
|
||||
private lazy var unreadMessagesIndicator: UIView = {
|
||||
let result = UIView()
|
||||
result.backgroundColor = Colors.accent
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var profilePictureView = ProfilePictureView()
|
||||
|
||||
private lazy var displayNameLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
result.textColor = Colors.text
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var snippetLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.textColor = Colors.text
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var typingIndicatorView = TypingIndicatorView()
|
||||
|
||||
// MARK: Initialization
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
// Make the cell transparent
|
||||
backgroundColor = .clear
|
||||
// Set up the unread messages indicator
|
||||
unreadMessagesIndicator.set(.width, to: Values.accentLineThickness)
|
||||
// Set up the profile picture view
|
||||
let profilePictureViewSize = Values.mediumProfilePictureSize
|
||||
profilePictureView.set(.width, to: profilePictureViewSize)
|
||||
profilePictureView.set(.height, to: profilePictureViewSize)
|
||||
profilePictureView.size = profilePictureViewSize
|
||||
// Set up the label stack view
|
||||
let snippetLabelContainer = UIView()
|
||||
snippetLabelContainer.addSubview(snippetLabel)
|
||||
snippetLabelContainer.addSubview(typingIndicatorView)
|
||||
let labelStackView = UIStackView(arrangedSubviews: [ UIView.spacer(withHeight: Values.smallSpacing), displayNameLabel, snippetLabelContainer, UIView.spacer(withHeight: Values.smallSpacing) ])
|
||||
labelStackView.axis = .vertical
|
||||
labelStackView.spacing = Values.smallSpacing
|
||||
// Set up the main stack view
|
||||
let stackView = UIStackView(arrangedSubviews: [ unreadMessagesIndicator, profilePictureView, labelStackView ])
|
||||
stackView.axis = .horizontal
|
||||
stackView.alignment = .center
|
||||
stackView.spacing = Values.mediumSpacing
|
||||
contentView.addSubview(stackView)
|
||||
// Set up the constraints
|
||||
unreadMessagesIndicator.pin(.top, to: .top, of: stackView)
|
||||
unreadMessagesIndicator.pin(.bottom, to: .bottom, of: stackView)
|
||||
snippetLabel.pin(to: snippetLabelContainer)
|
||||
typingIndicatorView.pin(.leading, to: .leading, of: snippetLabelContainer)
|
||||
typingIndicatorView.centerYAnchor.constraint(equalTo: snippetLabel.centerYAnchor).isActive = true
|
||||
stackView.pin(.leading, to: .leading, of: contentView)
|
||||
stackView.pin(.top, to: .top, of: contentView)
|
||||
contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.mediumSpacing)
|
||||
contentView.pin(.bottom, to: .bottom, of: stackView)
|
||||
stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing) // Workaround for weird constraints issue
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
private func update() {
|
||||
LokiAPI.populateUserHexEncodedPublicKeyCacheIfNeeded(for: threadViewModel.threadRecord.uniqueId!) // FIXME: This is a terrible place to do this
|
||||
unreadMessagesIndicator.isHidden = !threadViewModel.hasUnreadMessages
|
||||
if threadViewModel.hasUnreadMessages {
|
||||
backgroundColor = UIColor(hex: 0x1B1B1B)
|
||||
} else {
|
||||
backgroundColor = .clear
|
||||
}
|
||||
if threadViewModel.isGroupThread {
|
||||
let users = LokiAPI.userHexEncodedPublicKeyCache[threadViewModel.threadRecord.uniqueId!] ?? []
|
||||
let randomUsers = users.sorted().prefix(2) // Sort to provide a level of stability
|
||||
if !randomUsers.isEmpty {
|
||||
profilePictureView.hexEncodedPublicKey = randomUsers[0]
|
||||
profilePictureView.additionalHexEncodedPublicKey = randomUsers.count == 2 ? randomUsers[1] : nil
|
||||
} else {
|
||||
// TODO: Handle
|
||||
}
|
||||
} else {
|
||||
profilePictureView.hexEncodedPublicKey = threadViewModel.contactIdentifier!
|
||||
profilePictureView.additionalHexEncodedPublicKey = nil
|
||||
}
|
||||
profilePictureView.update()
|
||||
displayNameLabel.text = getDisplayName()
|
||||
if SSKEnvironment.shared.typingIndicators.typingRecipientId(forThread: self.threadViewModel.threadRecord) != nil {
|
||||
snippetLabel.text = ""
|
||||
typingIndicatorView.isHidden = false
|
||||
typingIndicatorView.startAnimation()
|
||||
} else {
|
||||
snippetLabel.attributedText = getSnippet()
|
||||
typingIndicatorView.isHidden = true
|
||||
typingIndicatorView.stopAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
private func getDisplayName() -> String {
|
||||
if threadViewModel.isGroupThread {
|
||||
if threadViewModel.name.isEmpty {
|
||||
return NSLocalizedString("New Group", comment: "")
|
||||
} else {
|
||||
return threadViewModel.name
|
||||
}
|
||||
} else {
|
||||
if threadViewModel.threadRecord.isNoteToSelf() {
|
||||
return NSLocalizedString("Note to Self", comment: "")
|
||||
} else {
|
||||
let hexEncodedPublicKey = threadViewModel.contactIdentifier!
|
||||
return DisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func getSnippet() -> NSMutableAttributedString {
|
||||
let result = NSMutableAttributedString()
|
||||
if threadViewModel.isMuted {
|
||||
result.append(NSAttributedString(string: "\u{e067} ", attributes: [ .font : UIFont.ows_elegantIconsFont(10), .foregroundColor : Colors.unimportant ]))
|
||||
}
|
||||
if let rawSnippet = threadViewModel.lastMessageText {
|
||||
let snippet = MentionUtilities.highlightMentions(in: rawSnippet, threadID: threadViewModel.threadRecord.uniqueId!)
|
||||
result.append(NSAttributedString(string: snippet, attributes: [ .font : UIFont.systemFont(ofSize: Values.smallFontSize), .foregroundColor : Colors.text ]))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
public final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
private var threadViewModelCache: [String:ThreadViewModel] = [:]
|
||||
|
||||
private var threads: YapDatabaseViewMappings = {
|
||||
let result = YapDatabaseViewMappings(groups: [ TSInboxGroup ], view: TSThreadDatabaseViewExtensionName)
|
||||
result.setIsReversed(true, forGroup: TSInboxGroup)
|
||||
return result
|
||||
}()
|
||||
|
||||
private let uiDatabaseConnection: YapDatabaseConnection = {
|
||||
let result = OWSPrimaryStorage.shared().newDatabaseConnection()
|
||||
result.objectCacheLimit = 500
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Settings
|
||||
public override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
|
||||
|
||||
// MARK: Components
|
||||
private lazy var tableView: UITableView = {
|
||||
let result = UITableView()
|
||||
result.backgroundColor = .clear
|
||||
result.separatorStyle = .none
|
||||
result.register(ConversationCell.self, forCellReuseIdentifier: ConversationCell.reuseIdentifier)
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
public override func viewDidLoad() {
|
||||
// Set gradient background
|
||||
view.backgroundColor = .clear
|
||||
let gradient = Gradients.defaultLokiBackground
|
||||
view.setGradient(gradient)
|
||||
// Customize title
|
||||
navigationItem.title = NSLocalizedString("Messages", comment: "")
|
||||
navigationController?.navigationBar.titleTextAttributes = [ .foregroundColor : Colors.text, .font : UIFont.boldSystemFont(ofSize: Values.veryLargeFontSize) ]
|
||||
// Set up table view
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
view.addSubview(tableView)
|
||||
tableView.pin(to: view)
|
||||
// Do initial update
|
||||
uiDatabaseConnection.beginLongLivedReadTransaction()
|
||||
uiDatabaseConnection.read { transaction in
|
||||
self.threads.update(with: transaction)
|
||||
}
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
// MARK: Data
|
||||
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return Int(threads.numberOfItems(inGroup: TSInboxGroup))
|
||||
}
|
||||
|
||||
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: ConversationCell.reuseIdentifier) as! ConversationCell
|
||||
cell.threadViewModel = threadViewModel(at: indexPath.row)
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: Convenience
|
||||
private func thread(at index: Int) -> TSThread? {
|
||||
var thread: TSThread? = nil
|
||||
uiDatabaseConnection.read { transaction in
|
||||
thread = ((transaction as YapDatabaseReadTransaction).ext(TSThreadDatabaseViewExtensionName) as! YapDatabaseViewTransaction).object(atRow: UInt(index), inSection: 0, with: self.threads) as! TSThread?
|
||||
}
|
||||
return thread
|
||||
}
|
||||
|
||||
private func threadViewModel(at index: Int) -> ThreadViewModel? {
|
||||
guard let thread = thread(at: index) else { return nil }
|
||||
if let cachedThreadViewModel = threadViewModelCache[thread.uniqueId!] {
|
||||
return cachedThreadViewModel
|
||||
} else {
|
||||
var threadViewModel: ThreadViewModel? = nil
|
||||
uiDatabaseConnection.read { transaction in
|
||||
threadViewModel = ThreadViewModel(thread: thread, transaction: transaction)
|
||||
}
|
||||
threadViewModelCache[thread.uniqueId!] = threadViewModel
|
||||
return threadViewModel
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
public final class ProfilePictureView : UIView {
|
||||
private var imageViewWidthConstraint: NSLayoutConstraint!
|
||||
private var imageViewHeightConstraint: NSLayoutConstraint!
|
||||
public var size: CGFloat!
|
||||
public var hexEncodedPublicKey: String!
|
||||
public var additionalHexEncodedPublicKey: String?
|
||||
|
||||
// MARK: Components
|
||||
private lazy var imageView = getImageView()
|
||||
private lazy var additionalImageView = getImageView()
|
||||
|
||||
// MARK: Lifecycle
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
initialize()
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
initialize()
|
||||
}
|
||||
|
||||
private func initialize() {
|
||||
// Set up image view
|
||||
addSubview(imageView)
|
||||
imageView.pin(.leading, to: .leading, of: self)
|
||||
imageView.pin(.top, to: .top, of: self)
|
||||
// Set up additional image view
|
||||
addSubview(additionalImageView)
|
||||
additionalImageView.pin(.trailing, to: .trailing, of: self)
|
||||
additionalImageView.pin(.bottom, to: .bottom, of: self)
|
||||
let additionalImageViewSize = Values.smallProfilePictureSize
|
||||
additionalImageView.set(.width, to: additionalImageViewSize)
|
||||
additionalImageView.set(.height, to: additionalImageViewSize)
|
||||
additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
public func update() {
|
||||
if let imageViewWidthConstraint = imageViewWidthConstraint, let imageViewHeightConstraint = imageViewHeightConstraint {
|
||||
imageView.removeConstraint(imageViewWidthConstraint)
|
||||
imageView.removeConstraint(imageViewHeightConstraint)
|
||||
}
|
||||
let size: CGFloat
|
||||
if let additionalHexEncodedPublicKey = additionalHexEncodedPublicKey {
|
||||
size = Values.smallProfilePictureSize
|
||||
imageViewWidthConstraint = imageView.set(.width, to: size)
|
||||
imageViewHeightConstraint = imageView.set(.height, to: size)
|
||||
additionalImageView.isHidden = false
|
||||
additionalImageView.image = Identicon.generateIcon(string: additionalHexEncodedPublicKey, size: size)
|
||||
} else {
|
||||
size = self.size
|
||||
imageViewWidthConstraint = imageView.pin(.trailing, to: .trailing, of: self)
|
||||
imageViewHeightConstraint = imageView.pin(.bottom, to: .bottom, of: self)
|
||||
additionalImageView.isHidden = true
|
||||
additionalImageView.image = nil
|
||||
}
|
||||
imageView.image = Identicon.generateIcon(string: hexEncodedPublicKey, size: size)
|
||||
imageView.layer.cornerRadius = size / 2
|
||||
}
|
||||
|
||||
// MARK: Convenience
|
||||
private func getImageView() -> UIImageView {
|
||||
let result = UIImageView()
|
||||
result.layer.masksToBounds = true
|
||||
result.backgroundColor = Colors.unimportant
|
||||
result.layer.borderColor = Colors.border.cgColor
|
||||
result.layer.borderWidth = Values.profilePictureBorderThickness
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
|
||||
@objc public extension CGFloat {
|
||||
|
||||
// MARK: - Alpha Values
|
||||
@objc public static let inactiveElementOpacity = CGFloat(0.6)
|
||||
|
||||
// MARK: - Font Sizes
|
||||
@objc public static let smallFontSize = CGFloat(13)
|
||||
@objc public static let regularFontSize = CGFloat(15)
|
||||
@objc public static let largeFontSize = CGFloat(20)
|
||||
@objc public static let veryLargeFontSize = CGFloat(25)
|
||||
@objc public static let massiveFontSize = CGFloat(50)
|
||||
|
||||
// MARK: - Element Sizes
|
||||
@objc public static let buttonHeight = CGFloat(34)
|
||||
@objc public static let accentLineThickness = CGFloat(4)
|
||||
@objc public static let regularAvatarSize = CGFloat(45)
|
||||
@objc public static let largeAvatarSize = CGFloat(65)
|
||||
}
|
|
@ -1,13 +1,19 @@
|
|||
|
||||
@objc public extension UIColor {
|
||||
|
||||
@objc public convenience init(hex value: UInt) {
|
||||
@objc convenience init(hex value: UInt) { // Doesn't need to be declared public because the extension is already public
|
||||
let red = CGFloat((value >> 16) & 0xff) / 255
|
||||
let green = CGFloat((value >> 8) & 0xff) / 255
|
||||
let blue = CGFloat((value >> 0) & 0xff) / 255
|
||||
self.init(red: red, green: green, blue: blue, alpha: 1)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(LKColors)
|
||||
public final class Colors : NSObject {
|
||||
|
||||
@objc public static let accent = UIColor(hex: 0x00F782)
|
||||
@objc public static let text = UIColor(hex: 0xFFFFFF)
|
||||
@objc public static let unimportant = UIColor(hex: 0xD8D8D8)
|
||||
@objc public static let border = UIColor(hex: 0x979797)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
@objc(LKGradient)
|
||||
public final class Gradient : NSObject {
|
||||
public let start: UIColor
|
||||
public let end: UIColor
|
||||
|
||||
private override init() { preconditionFailure("Use init(start:end:) instead.") }
|
||||
|
||||
@objc public init(start: UIColor, end: UIColor) {
|
||||
self.start = start
|
||||
self.end = end
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
|
||||
@objc public extension UIView {
|
||||
|
||||
@objc func setGradient(_ gradient: Gradient) { // Doesn't need to be declared public because the extension is already public
|
||||
let layer = CAGradientLayer()
|
||||
layer.frame = UIScreen.main.bounds
|
||||
layer.colors = [ gradient.start.cgColor, gradient.end.cgColor ]
|
||||
layer.startPoint = CGPoint(x: 0.5, y: 0)
|
||||
layer.endPoint = CGPoint(x: 0.5, y: 1)
|
||||
let index = UInt32((self.layer.sublayers ?? []).count)
|
||||
self.layer.insertSublayer(layer, at: index)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(LKGradients)
|
||||
public final class Gradients : NSObject {
|
||||
|
||||
@objc public static let defaultLokiBackground = Gradient(start: UIColor(hex: 0x171717), end: UIColor(hex:0x121212))
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
@objc(LKValues)
|
||||
public final class Values : NSObject {
|
||||
|
||||
// MARK: - Alpha Values
|
||||
@objc public static let inactiveElementOpacity = CGFloat(0.6)
|
||||
|
||||
// MARK: - Font Sizes
|
||||
@objc public static let smallFontSize = CGFloat(13)
|
||||
@objc public static let mediumFontSize = CGFloat(15)
|
||||
@objc public static let largeFontSize = CGFloat(20)
|
||||
@objc public static let veryLargeFontSize = CGFloat(25)
|
||||
@objc public static let massiveFontSize = CGFloat(50)
|
||||
|
||||
// MARK: - Element Sizes
|
||||
@objc public static let buttonHeight = CGFloat(34)
|
||||
@objc public static let accentLineThickness = CGFloat(4)
|
||||
@objc public static let smallProfilePictureSize = CGFloat(35)
|
||||
@objc public static let mediumProfilePictureSize = CGFloat(45)
|
||||
@objc public static let largeProfilePictureSize = CGFloat(65)
|
||||
@objc public static let profilePictureBorderThickness = CGFloat(1)
|
||||
|
||||
// MARK: - Distances
|
||||
@objc public static let smallSpacing = CGFloat(8)
|
||||
@objc public static let mediumSpacing = CGFloat(16)
|
||||
}
|
|
@ -276,7 +276,7 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
|
|||
|
||||
// TODO: Remove this.
|
||||
if (self.homeViewMode == HomeViewMode_Inbox) {
|
||||
[SignalApp.sharedApp setHomeViewController:self];
|
||||
// [SignalApp.sharedApp setHomeViewController:self];
|
||||
}
|
||||
|
||||
UIStackView *reminderStackView = [UIStackView new];
|
||||
|
|
|
@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class AccountManager;
|
||||
@class CallService;
|
||||
@class CallUIAdapter;
|
||||
@class HomeViewController;
|
||||
@class HomeVC;
|
||||
@class OWSMessageFetcherJob;
|
||||
@class OWSNavigationController;
|
||||
@class OWSWebRTCCallMessageHandler;
|
||||
|
@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@interface SignalApp : NSObject
|
||||
|
||||
@property (nonatomic, nullable, weak) HomeViewController *homeViewController;
|
||||
@property (nonatomic, nullable, weak) HomeVC *homeViewController;
|
||||
@property (nonatomic, nullable, weak) OWSNavigationController *signUpFlowNavigationController;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#import "SignalApp.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "ConversationViewController.h"
|
||||
#import "HomeViewController.h"
|
||||
#import "Session-Swift.h"
|
||||
#import "SignalsNavigationController.h"
|
||||
#import <SignalCoreKit/Threading.h>
|
||||
|
@ -118,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
[self.homeViewController presentThread:thread action:action focusMessageId:focusMessageId animated:isAnimated];
|
||||
// [self.homeViewController presentThread:thread action:action focusMessageId:focusMessageId animated:isAnimated];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -146,10 +145,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
[self.homeViewController presentThread:thread
|
||||
action:ConversationViewActionNone
|
||||
focusMessageId:nil
|
||||
animated:isAnimated];
|
||||
// [self.homeViewController presentThread:thread
|
||||
// action:ConversationViewActionNone
|
||||
// focusMessageId:nil
|
||||
// animated:isAnimated];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -177,12 +176,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)showHomeView
|
||||
{
|
||||
HomeViewController *homeView = [HomeViewController new];
|
||||
HomeVC *homeView = [HomeVC new];
|
||||
SignalsNavigationController *navigationController =
|
||||
[[SignalsNavigationController alloc] initWithRootViewController:homeView];
|
||||
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
|
||||
appDelegate.window.rootViewController = navigationController;
|
||||
OWSAssertDebug([navigationController.topViewController isKindOfClass:[HomeViewController class]]);
|
||||
OWSAssertDebug([navigationController.topViewController isKindOfClass:[HomeVC class]]);
|
||||
|
||||
// Clear the signUpFlowNavigationController.
|
||||
[self setSignUpFlowNavigationController:nil];
|
||||
|
|
|
@ -127,9 +127,7 @@
|
|||
fileprivate func startAnimation() {
|
||||
stopAnimation()
|
||||
|
||||
let baseColor = (Theme.isDarkThemeEnabled
|
||||
? UIColor(rgbHex: 0xBBBDBE)
|
||||
: UIColor(rgbHex: 0x636467))
|
||||
let baseColor = UIColor.white
|
||||
let timeIncrement: CFTimeInterval = 0.15
|
||||
var colorValues = [CGColor]()
|
||||
var pathValues = [CGPath]()
|
||||
|
|
|
@ -2672,3 +2672,8 @@
|
|||
"Your device was unlinked successfully" = "Your device was unlinked successfully";
|
||||
"Unnamed Device" = "Unnamed Device";
|
||||
"Linked device (%@)" = "Linked device (%@)";
|
||||
|
||||
// MARK: - Redesign
|
||||
"Messages" = "Messages";
|
||||
"Note to Self" = "Note to Self";
|
||||
"New Group" = "New Group";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
@objc(LKIdenticon)
|
||||
final class Identicon : NSObject {
|
||||
public final class Identicon : NSObject {
|
||||
|
||||
@objc static func generateIcon(string: String, size: CGFloat) -> UIImage {
|
||||
@objc public static func generateIcon(string: String, size: CGFloat) -> UIImage {
|
||||
let icon = JazzIcon(seed: string)
|
||||
let iconLayer = icon.generateLayer(ofSize: size)
|
||||
let rect = CGRect(origin: CGPoint.zero, size: iconLayer.frame.size)
|
||||
|
|
Loading…
Reference in New Issue