Implement group member count, fix QR code scanning & clean
This commit is contained in:
parent
0f22b4b36c
commit
7cfd43ff6b
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "SESSION_ICON_WHT.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -1,5 +1,6 @@
|
|||
|
||||
@objc final class ConversationTitleView : UIView {
|
||||
@objc(LKConversationTitleView)
|
||||
final class ConversationTitleView : UIView {
|
||||
private let thread: TSThread
|
||||
private var currentStatus: Status? { didSet { updateSubtitleForCurrentStatus() } }
|
||||
|
||||
|
@ -141,7 +142,7 @@
|
|||
self.currentStatus = nil
|
||||
}
|
||||
|
||||
private func updateSubtitleForCurrentStatus() {
|
||||
@objc func updateSubtitleForCurrentStatus() {
|
||||
DispatchQueue.main.async {
|
||||
self.subtitleLabel.isHidden = false
|
||||
switch self.currentStatus {
|
||||
|
@ -159,13 +160,29 @@
|
|||
dateFormatter.timeStyle = .medium
|
||||
dateFormatter.dateStyle = .medium
|
||||
subtitle.append(NSAttributedString(string: "Muted until " + dateFormatter.string(from: muteEndDate)))
|
||||
} else if self.thread.isGroupThread() {
|
||||
subtitle.append(NSAttributedString(string: "26 members")) // TODO: Implement
|
||||
} else if self.thread.isGroupThread() && !(self.thread as! TSGroupThread).isRSSFeed {
|
||||
let storage = OWSPrimaryStorage.shared()
|
||||
var userCount: Int?
|
||||
storage.dbReadConnection.readWrite { transaction in
|
||||
if let publicChat = LokiDatabaseUtilities.getPublicChat(for: self.thread.uniqueId!, in: transaction) {
|
||||
userCount = storage.getUserCount(for: publicChat, in: transaction)
|
||||
}
|
||||
}
|
||||
if let userCount = userCount {
|
||||
if userCount > 2500 {
|
||||
subtitle.append(NSAttributedString(string: "2500+ members"))
|
||||
} else {
|
||||
subtitle.append(NSAttributedString(string: "\(userCount) members"))
|
||||
}
|
||||
} else {
|
||||
self.subtitleLabel.isHidden = true
|
||||
}
|
||||
} else {
|
||||
self.subtitleLabel.isHidden = true
|
||||
}
|
||||
self.subtitleLabel.attributedText = subtitle
|
||||
}
|
||||
self.titleLabel.font = .boldSystemFont(ofSize: self.subtitleLabel.isHidden ? Values.veryLargeFontSize : Values.mediumFontSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -326,6 +326,9 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
|
|||
thread.remove(with: transaction)
|
||||
}
|
||||
NotificationCenter.default.post(name: .threadDeleted, object: nil, userInfo: [ "threadId" : thread.uniqueId! ])
|
||||
if let publicChat = publicChat {
|
||||
let _ = LokiPublicChatAPI.leave(publicChat.channel, on: publicChat.server)
|
||||
}
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .default) { _ in })
|
||||
guard let self = self else { return }
|
||||
|
|
|
@ -153,6 +153,7 @@ final class JoinPublicChatVC : UIViewController, UIPageViewControllerDataSource,
|
|||
.done(on: .main) { [weak self] _ in
|
||||
let _ = LokiPublicChatAPI.getMessages(for: channelID, on: urlAsString)
|
||||
let _ = LokiPublicChatAPI.setDisplayName(to: displayName, on: urlAsString)
|
||||
let _ = LokiPublicChatAPI.join(channelID, on: urlAsString)
|
||||
self?.presentingViewController!.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
.catch(on: .main) { [weak self] _ in
|
||||
|
|
|
@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (atomic) ZXCapture *capture;
|
||||
@property (nonatomic) BOOL captureEnabled;
|
||||
@property (nonatomic) UIView *maskingView;
|
||||
@property (nonatomic) dispatch_queue_t captureQueue;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -34,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
_captureEnabled = NO;
|
||||
_captureQueue = dispatch_get_main_queue();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -46,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
_captureEnabled = NO;
|
||||
_captureQueue = dispatch_get_main_queue();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -102,7 +105,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
self.captureEnabled = YES;
|
||||
if (!self.capture) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
dispatch_async(self.captureQueue, ^{
|
||||
self.capture = [[ZXCapture alloc] init];
|
||||
// self.capture.invert = YES;
|
||||
self.capture.camera = self.capture.back;
|
||||
|
@ -124,7 +127,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)stopCapture
|
||||
{
|
||||
self.captureEnabled = NO;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
dispatch_async(self.captureQueue, ^{
|
||||
[self.capture stop];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
@property (nonatomic, nullable) NSTimer *readTimer;
|
||||
@property (nonatomic) NSCache *cellMediaCache;
|
||||
@property (nonatomic) ConversationTitleView *headerView;
|
||||
@property (nonatomic) LKConversationTitleView *headerView;
|
||||
@property (nonatomic, nullable) UIView *bannerView;
|
||||
@property (nonatomic, nullable) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
|
||||
|
||||
|
@ -662,6 +662,19 @@ typedef enum : NSUInteger {
|
|||
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Gear"] style:UIBarButtonItemStylePlain target:self action:@selector(showConversationSettings)];
|
||||
settingsButton.tintColor = LKColors.text;
|
||||
self.navigationItem.rightBarButtonItem = settingsButton;
|
||||
|
||||
if (self.thread.isGroupThread) {
|
||||
TSGroupThread *thread = (TSGroupThread *)self.thread;
|
||||
if (thread.isRSSFeed) { return; }
|
||||
__block LKPublicChat *publicChat;
|
||||
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction];
|
||||
}];
|
||||
[LKPublicChatAPI getUserCountForGroup:publicChat.channel onServer:publicChat.server]
|
||||
.thenOn(dispatch_get_main_queue(), ^(id userCount) {
|
||||
[self.headerView updateSubtitleForCurrentStatus];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createContents
|
||||
|
@ -1410,7 +1423,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)createHeaderViews
|
||||
{
|
||||
ConversationTitleView *headerView = [[ConversationTitleView alloc] initWithThread:self.thread];
|
||||
LKConversationTitleView *headerView = [[LKConversationTitleView alloc] initWithThread:self.thread];
|
||||
self.headerView = headerView;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, headerView);
|
||||
|
||||
|
|
|
@ -12,15 +12,6 @@ public final class ProfilePictureView : UIView {
|
|||
private lazy var imageView = getImageView()
|
||||
private lazy var additionalImageView = getImageView()
|
||||
|
||||
private lazy var rssLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = UIColor(rgbHex: 0xFFFFFF) // Colors.text
|
||||
result.font = .systemFont(ofSize: 13) // Values.smallFontSize
|
||||
result.textAlignment = .center
|
||||
result.text = "RSS"
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
@ -45,12 +36,6 @@ public final class ProfilePictureView : UIView {
|
|||
additionalImageView.set(.width, to: additionalImageViewSize)
|
||||
additionalImageView.set(.height, to: additionalImageViewSize)
|
||||
additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
|
||||
// Set up RSS label
|
||||
addSubview(rssLabel)
|
||||
rssLabel.pin(.leading, to: .leading, of: self)
|
||||
rssLabel.pin(.top, to: .top, of: self)
|
||||
rssLabel.autoPinWidth(toWidthOf: imageView)
|
||||
rssLabel.autoPinHeight(toHeightOf: imageView)
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
|
@ -80,8 +65,10 @@ public final class ProfilePictureView : UIView {
|
|||
imageView.image = isRSSFeed ? nil : getProfilePicture(of: size, for: hexEncodedPublicKey)
|
||||
imageView.backgroundColor = isRSSFeed ? UIColor(rgbHex: 0x353535) : UIColor(rgbHex: 0xD8D8D8) // UIColor(rgbHex: 0xD8D8D8) = Colors.unimportant
|
||||
imageView.layer.cornerRadius = size / 2
|
||||
rssLabel.isHidden = !isRSSFeed
|
||||
rssLabel.font = size == (75) ? .systemFont(ofSize: 20) : .systemFont(ofSize: 13) // Values.largeProfilePictureSize / Values.largeFontSize / Values.smallFontSize
|
||||
imageView.contentMode = isRSSFeed ? .center : .scaleAspectFit
|
||||
if isRSSFeed {
|
||||
imageView.image = #imageLiteral(resourceName: "SessionWhite").resizedImage(to: CGSize(width: (303*24)/337, height: 24))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Convenience
|
||||
|
|
|
@ -242,6 +242,49 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
|
|||
}
|
||||
}
|
||||
|
||||
public static func join(_ channel: UInt64, on server: String) -> Promise<Void> {
|
||||
return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Void> in
|
||||
let url = URL(string: "\(server)/channels/\(channel)/subscribe")!
|
||||
let request = TSRequest(url: url, method: "POST", parameters: [:])
|
||||
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
|
||||
print("[Loki] Joined channel with ID: \(channel) on server: \(server).")
|
||||
}.retryingIfNeeded(maxRetryCount: maxRetryCount)
|
||||
}
|
||||
}
|
||||
|
||||
public static func leave(_ channel: UInt64, on server: String) -> Promise<Void> {
|
||||
return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Void> in
|
||||
let url = URL(string: "\(server)/channels/\(channel)/subscribe")!
|
||||
let request = TSRequest(url: url, method: "DELETE", parameters: [:])
|
||||
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).done(on: DispatchQueue.global()) { result -> Void in
|
||||
print("[Loki] Left channel with ID: \(channel) on server: \(server).")
|
||||
}.retryingIfNeeded(maxRetryCount: maxRetryCount)
|
||||
}
|
||||
}
|
||||
|
||||
public static func getUserCount(for channel: UInt64, on server: String) -> Promise<Int> {
|
||||
return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Int> in
|
||||
let queryParameters = "count=2500"
|
||||
let url = URL(string: "\(server)/channels/\(channel)/subscribers?\(queryParameters)")!
|
||||
let request = TSRequest(url: url)
|
||||
request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ]
|
||||
return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { $0.responseObject }.map { rawResponse in
|
||||
guard let json = rawResponse as? JSON, let users = json["data"] as? [JSON] else {
|
||||
print("[Loki] Couldn't parse user count for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).")
|
||||
throw Error.parsingFailed
|
||||
}
|
||||
let userCount = users.count
|
||||
let storage = OWSPrimaryStorage.shared()
|
||||
storage.dbReadWriteConnection.readWrite { transaction in
|
||||
storage.setUserCount(userCount, forPublicChatWithID: "\(server).\(channel)", in: transaction)
|
||||
}
|
||||
return userCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func getDisplayNames(for channel: UInt64, on server: String) -> Promise<Void> {
|
||||
let publicChatID = "\(server).\(channel)"
|
||||
guard let hexEncodedPublicKeys = displayNameUpdatees[publicChatID] else { return Promise.value(()) }
|
||||
|
@ -344,6 +387,11 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
|
|||
return AnyPromise.from(deleteMessage(with: messageID, for: group, on: server, isSentByUser: isSentByUser))
|
||||
}
|
||||
|
||||
@objc(getUserCountForGroup:onServer:)
|
||||
public static func objc_getUserCount(for group: UInt64, on server: String) -> AnyPromise {
|
||||
return AnyPromise.from(getUserCount(for: group, on: server))
|
||||
}
|
||||
|
||||
@objc(setDisplayName:on:)
|
||||
public static func objc_setDisplayName(to newDisplayName: String?, on server: String) -> AnyPromise {
|
||||
return AnyPromise.from(setDisplayName(to: newDisplayName, on: server))
|
||||
|
|
|
@ -71,17 +71,6 @@ public final class LokiPublicChatManager : NSObject {
|
|||
// Store the group chat mapping
|
||||
self.storage.dbReadWriteConnection.readWrite { transaction in
|
||||
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
|
||||
|
||||
// Mute the thread
|
||||
if let utc = TimeZone(identifier: "UTC") {
|
||||
var calendar = Calendar.current
|
||||
calendar.timeZone = utc
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.setValue(999, for: .year)
|
||||
if let date = calendar.date(byAdding: dateComponents, to: Date()) {
|
||||
thread.updateWithMuted(until: date, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// Save the group chat
|
||||
LokiDatabaseUtilities.setPublicChat(chat, for: thread.uniqueId!, in: transaction)
|
||||
|
|
|
@ -51,4 +51,12 @@ public extension OWSPrimaryStorage {
|
|||
public func getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? {
|
||||
return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey
|
||||
}
|
||||
|
||||
public func getUserCount(for publicChat: LokiPublicChat, in transaction: YapDatabaseReadTransaction) -> Int? {
|
||||
return transaction.object(forKey: publicChat.id, inCollection: "LokiPublicChatUserCountCollection") as? Int
|
||||
}
|
||||
|
||||
public func setUserCount(_ userCount: Int, forPublicChatWithID publicChatID: String, in transaction: YapDatabaseReadWriteTransaction) {
|
||||
transaction.setObject(userCount, forKey: publicChatID, inCollection: "LokiPublicChatUserCountCollection")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue