Hook up suggestion grid
This commit is contained in:
parent
053f581bb2
commit
a7dd7e1bf0
|
@ -153,6 +153,8 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
|
|||
DispatchQueue.global(qos: .utility).async {
|
||||
let _ = IP2Country.shared.populateCacheIfNeeded()
|
||||
}
|
||||
// Get default open group rooms if needed
|
||||
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
|
|
@ -396,7 +396,7 @@ static NSTimeInterval launchStartedAt;
|
|||
[self startClosedGroupPollerIfNeeded];
|
||||
[self startOpenGroupPollersIfNeeded];
|
||||
|
||||
// Loki: Update profile picture if needed
|
||||
// Update profile picture if needed
|
||||
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
|
||||
NSDate *now = [NSDate new];
|
||||
NSDate *lastProfilePictureUpload = (NSDate *)[userDefaults objectForKey:@"lastProfilePictureUpload"];
|
||||
|
@ -410,6 +410,10 @@ static NSTimeInterval launchStartedAt;
|
|||
// Do nothing
|
||||
} requiresSync:YES];
|
||||
}
|
||||
|
||||
if (CurrentAppContext().isMainApp) {
|
||||
[SNOpenGroupAPIV2 getDefaultRoomsIfNeeded];
|
||||
}
|
||||
|
||||
if (![UIApplication sharedApplication].isRegisteredForRemoteNotifications) {
|
||||
OWSLogInfo(@"Retrying remote notification registration since user hasn't registered yet.");
|
||||
|
|
|
@ -149,6 +149,28 @@ final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageView
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func join(_ room: String, on server: String, with publicKey: String) {
|
||||
guard !isJoining else { return }
|
||||
isJoining = true
|
||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] _ in
|
||||
Storage.shared.write { transaction in
|
||||
OpenGroupManagerV2.shared.add(room: room, server: server, publicKey: publicKey, using: transaction)
|
||||
.done(on: DispatchQueue.main) { [weak self] _ in
|
||||
self?.presentingViewController!.dismiss(animated: true, completion: nil)
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...)
|
||||
}
|
||||
.catch(on: DispatchQueue.main) { [weak self] error in
|
||||
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
let title = "Couldn't Join"
|
||||
let message = error.localizedDescription
|
||||
self?.isJoining = false
|
||||
self?.showError(title: title, message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Convenience
|
||||
private func showError(title: String, message: String = "") {
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
@ -157,7 +179,7 @@ final class JoinOpenGroupVC : BaseVC, UIPageViewControllerDataSource, UIPageView
|
|||
}
|
||||
}
|
||||
|
||||
private final class EnterURLVC : UIViewController {
|
||||
private final class EnterURLVC : UIViewController, OpenGroupSuggestionGridDelegate {
|
||||
weak var joinOpenGroupVC: JoinOpenGroupVC!
|
||||
|
||||
// MARK: Components
|
||||
|
@ -170,13 +192,22 @@ private final class EnterURLVC : UIViewController {
|
|||
|
||||
private lazy var suggestionGrid: OpenGroupSuggestionGrid = {
|
||||
let maxWidth = UIScreen.main.bounds.width - Values.largeSpacing * 2
|
||||
return OpenGroupSuggestionGrid(maxWidth: maxWidth)
|
||||
let result = OpenGroupSuggestionGrid(maxWidth: maxWidth)
|
||||
result.delegate = self
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Lifecycle
|
||||
override func viewDidLoad() {
|
||||
// Remove background color
|
||||
view.backgroundColor = .clear
|
||||
// Suggestion grid title label
|
||||
let suggestionGridTitleLabel = UILabel()
|
||||
suggestionGridTitleLabel.textColor = Colors.text
|
||||
suggestionGridTitleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
|
||||
suggestionGridTitleLabel.text = "Or join one of these..."
|
||||
suggestionGridTitleLabel.numberOfLines = 0
|
||||
suggestionGridTitleLabel.lineBreakMode = .byWordWrapping
|
||||
// Next button
|
||||
let nextButton = Button(style: .prominentOutline, size: .large)
|
||||
nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
|
||||
|
@ -187,11 +218,9 @@ private final class EnterURLVC : UIViewController {
|
|||
nextButton.pin(.top, to: .top, of: nextButtonContainer)
|
||||
nextButtonContainer.pin(.trailing, to: .trailing, of: nextButton, withInset: 80)
|
||||
nextButtonContainer.pin(.bottom, to: .bottom, of: nextButton)
|
||||
// Spacers
|
||||
let spacer1 = UIView.vStretchingSpacer()
|
||||
let spacer2 = UIView.vStretchingSpacer()
|
||||
// Stack view
|
||||
let stackView = UIStackView(arrangedSubviews: [ urlTextField, spacer1, suggestionGrid, spacer2, nextButtonContainer ])
|
||||
let stackView = UIStackView(arrangedSubviews: [ urlTextField, UIView.spacer(withHeight: Values.mediumSpacing), suggestionGridTitleLabel,
|
||||
UIView.spacer(withHeight: Values.mediumSpacing), suggestionGrid, UIView.vStretchingSpacer(), nextButtonContainer ])
|
||||
stackView.axis = .vertical
|
||||
stackView.alignment = .fill
|
||||
stackView.layoutMargins = UIEdgeInsets(uniform: Values.largeSpacing)
|
||||
|
@ -200,7 +229,6 @@ private final class EnterURLVC : UIViewController {
|
|||
stackView.pin(to: view)
|
||||
// Constraints
|
||||
view.set(.width, to: UIScreen.main.bounds.width)
|
||||
spacer1.heightAnchor.constraint(equalTo: spacer2.heightAnchor).isActive = true
|
||||
// Dismiss keyboard on tap
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
|
||||
view.addGestureRecognizer(tapGestureRecognizer)
|
||||
|
@ -216,6 +244,10 @@ private final class EnterURLVC : UIViewController {
|
|||
}
|
||||
|
||||
// MARK: Interaction
|
||||
func join(_ room: OpenGroupAPIV2.Info) {
|
||||
joinOpenGroupVC.join(room.id, on: OpenGroupAPIV2.defaultServer, with: OpenGroupAPIV2.defaultServerPublicKey)
|
||||
}
|
||||
|
||||
@objc private func joinOpenGroupIfPossible() {
|
||||
let url = urlTextField.text?.trimmingCharacters(in: .whitespaces) ?? ""
|
||||
joinOpenGroupVC.joinOpenGroupIfPossible(with: url)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import PromiseKit
|
||||
import NVActivityIndicatorView
|
||||
|
||||
final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||
private let maxWidth: CGFloat
|
||||
private var rooms: [OpenGroupAPIV2.Info] = [] { didSet { reload() } }
|
||||
private var rooms: [OpenGroupAPIV2.Info] = [] { didSet { update() } }
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
var delegate: OpenGroupSuggestionGridDelegate?
|
||||
|
||||
// MARK: UI Components
|
||||
private lazy var layout: UICollectionViewFlowLayout = {
|
||||
|
@ -22,6 +25,13 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
|
|||
return result
|
||||
}()
|
||||
|
||||
private lazy var spinner: NVActivityIndicatorView = {
|
||||
let result = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: Colors.text, padding: nil)
|
||||
result.set(.width, to: 64)
|
||||
result.set(.height, to: 64)
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: Settings
|
||||
private static let cellHeight: CGFloat = 40
|
||||
private static let separatorWidth = 1 / UIScreen.main.scale
|
||||
|
@ -44,43 +54,59 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
|
|||
private func initialize() {
|
||||
addSubview(collectionView)
|
||||
collectionView.pin(to: self)
|
||||
heightConstraint = set(.height, to: 0)
|
||||
attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) {
|
||||
return OpenGroupAPIV2.getAllRooms(from: "https://sessionopengroup.com").done { [weak self] rooms in
|
||||
self?.rooms = rooms
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
addSubview(spinner)
|
||||
spinner.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top ], to: self)
|
||||
spinner.startAnimating()
|
||||
heightConstraint = set(.height, to: 64)
|
||||
widthAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true
|
||||
let _ = OpenGroupAPIV2.getDefaultRoomsPromise?.done { [weak self] rooms in
|
||||
self?.rooms = rooms
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Updating
|
||||
private func reload() {
|
||||
let height = OpenGroupSuggestionGrid.cellHeight * ceil(CGFloat(rooms.count) / 3)
|
||||
private func update() {
|
||||
spinner.stopAnimating()
|
||||
spinner.isHidden = true
|
||||
let height = OpenGroupSuggestionGrid.cellHeight * ceil(CGFloat(rooms.count) / 2)
|
||||
heightConstraint.constant = height
|
||||
collectionView.reloadData()
|
||||
}
|
||||
|
||||
// MARK: Layout
|
||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
return CGSize(width: maxWidth / 3, height: OpenGroupSuggestionGrid.cellHeight)
|
||||
return CGSize(width: maxWidth / 2, height: OpenGroupSuggestionGrid.cellHeight)
|
||||
}
|
||||
|
||||
// MARK: Data Source
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return min(rooms.count, 12) // Cap to a maximum of 12 (4 rows of 3)
|
||||
return min(rooms.count, 8) // Cap to a maximum of 8 (4 rows of 2)
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.identifier, for: indexPath) as! Cell
|
||||
cell.showRightSeparator = (indexPath.row % 2 != 0) || (indexPath.row % 2 == 0 && indexPath.row == rooms.count - 1)
|
||||
cell.showBottomSeparator = (indexPath.row >= rooms.count - 2)
|
||||
cell.room = rooms[indexPath.item]
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let room = rooms[indexPath.item]
|
||||
delegate?.join(room)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Cell
|
||||
extension OpenGroupSuggestionGrid {
|
||||
|
||||
fileprivate final class Cell : UICollectionViewCell {
|
||||
var showRightSeparator = false
|
||||
var showBottomSeparator = false
|
||||
var room: OpenGroupAPIV2.Info? { didSet { update() } }
|
||||
private var rightSeparator: UIView!
|
||||
private var bottomSeparator: UIView!
|
||||
|
||||
static let identifier = "OpenGroupSuggestionGridCell"
|
||||
|
||||
|
@ -107,11 +133,47 @@ extension OpenGroupSuggestionGrid {
|
|||
label.center(in: self)
|
||||
label.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: Values.smallSpacing).isActive = true
|
||||
trailingAnchor.constraint(greaterThanOrEqualTo: label.trailingAnchor, constant: Values.smallSpacing).isActive = true
|
||||
setUpSeparators()
|
||||
}
|
||||
|
||||
private func setUpSeparators() {
|
||||
func getVSeparator() -> UIView {
|
||||
let separator = UIView()
|
||||
separator.backgroundColor = Colors.separator
|
||||
separator.set(.height, to: 1 / UIScreen.main.scale)
|
||||
return separator
|
||||
}
|
||||
func getHSeparator() -> UIView {
|
||||
let separator = UIView()
|
||||
separator.backgroundColor = Colors.separator
|
||||
separator.set(.width, to: 1 / UIScreen.main.scale)
|
||||
return separator
|
||||
}
|
||||
let leftSeparator = getHSeparator()
|
||||
addSubview(leftSeparator)
|
||||
leftSeparator.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.VerticalEdge.bottom ], to: self)
|
||||
let topSeparator = getVSeparator()
|
||||
addSubview(topSeparator)
|
||||
topSeparator.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: self)
|
||||
rightSeparator = getHSeparator()
|
||||
addSubview(rightSeparator)
|
||||
rightSeparator.pin([ UIView.VerticalEdge.top, UIView.HorizontalEdge.right, UIView.VerticalEdge.bottom ], to: self)
|
||||
bottomSeparator = getVSeparator()
|
||||
addSubview(bottomSeparator)
|
||||
bottomSeparator.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.bottom, UIView.HorizontalEdge.right ], to: self)
|
||||
}
|
||||
|
||||
private func update() {
|
||||
guard let room = room else { return }
|
||||
label.text = room.name
|
||||
rightSeparator.alpha = showRightSeparator ? 1 :0
|
||||
bottomSeparator.alpha = showBottomSeparator ? 1 :0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Delegate
|
||||
protocol OpenGroupSuggestionGridDelegate {
|
||||
|
||||
func join(_ room: OpenGroupAPIV2.Info)
|
||||
}
|
||||
|
|
|
@ -11,4 +11,9 @@ extension OpenGroupAPIV2 {
|
|||
public static func objc_isUserModerator(_ publicKey: String, for room: String, on server: String) -> Bool {
|
||||
return isUserModerator(publicKey, for: room, on: server)
|
||||
}
|
||||
|
||||
@objc(getDefaultRoomsIfNeeded)
|
||||
public static func objc_getDefaultRoomsIfNeeded() {
|
||||
return getDefaultRoomsIfNeeded()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ import SessionSnodeKit
|
|||
@objc(SNOpenGroupAPIV2)
|
||||
public final class OpenGroupAPIV2 : NSObject {
|
||||
private static var moderators: [String:[String:Set<String>]] = [:] // Server URL to room ID to set of moderator IDs
|
||||
public static let defaultServer = "https://sessionopengroup.com"
|
||||
public static let defaultServerPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b"
|
||||
public static var getDefaultRoomsPromise: Promise<[Info]>?
|
||||
|
||||
// MARK: Error
|
||||
public enum Error : LocalizedError {
|
||||
|
@ -280,6 +283,16 @@ public final class OpenGroupAPIV2 : NSObject {
|
|||
}
|
||||
|
||||
// MARK: General
|
||||
public static func getDefaultRoomsIfNeeded() {
|
||||
Storage.shared.write(with: { transaction in
|
||||
Storage.shared.setOpenGroupPublicKey(for: defaultServer, to: defaultServerPublicKey, using: transaction)
|
||||
}, completion: {
|
||||
getDefaultRoomsPromise = attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) {
|
||||
OpenGroupAPIV2.getAllRooms(from: defaultServer)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public static func getInfo(for room: String, on server: String) -> Promise<Info> {
|
||||
let request = Request(verb: .get, room: room, server: server, endpoint: "rooms/\(room)", isAuthRequired: false)
|
||||
let promise: Promise<Info> = send(request).map(on: DispatchQueue.global(qos: .userInitiated)) { json in
|
||||
|
|
|
@ -33,30 +33,34 @@ public final class OpenGroupManagerV2 : NSObject {
|
|||
let storage = Storage.shared
|
||||
storage.removeLastMessageServerID(for: room, on: server, using: transaction)
|
||||
storage.removeLastDeletionServerID(for: room, on: server, using: transaction)
|
||||
storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction)
|
||||
storage.removeAuthToken(for: room, on: server, using: transaction)
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
OpenGroupAPIV2.getInfo(for: room, on: server).done(on: DispatchQueue.global(qos: .default)) { info in
|
||||
let openGroup = OpenGroupV2(server: server, room: room, name: info.name, imageID: info.imageID)
|
||||
let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
|
||||
let model = TSGroupModel(title: openGroup.name, memberIds: [ getUserHexEncodedPublicKey() ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
|
||||
storage.write(with: { transaction in
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
|
||||
thread.shouldThreadBeVisible = true
|
||||
thread.save(with: transaction)
|
||||
storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
|
||||
}, completion: {
|
||||
if let poller = OpenGroupManagerV2.shared.pollers[openGroup.id] {
|
||||
poller.stop()
|
||||
OpenGroupManagerV2.shared.pollers[openGroup.id] = nil
|
||||
}
|
||||
let poller = OpenGroupPollerV2(for: openGroup)
|
||||
poller.startIfNeeded()
|
||||
OpenGroupManagerV2.shared.pollers[openGroup.id] = poller
|
||||
seal.fulfill(())
|
||||
})
|
||||
}.catch(on: DispatchQueue.global(qos: .default)) { error in
|
||||
seal.reject(error)
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
transaction.addCompletionQueue(DispatchQueue.global(qos: .default)) {
|
||||
storage.setOpenGroupPublicKey(for: server, to: publicKey, using: transaction)
|
||||
OpenGroupAPIV2.getInfo(for: room, on: server).done(on: DispatchQueue.global(qos: .default)) { info in
|
||||
let openGroup = OpenGroupV2(server: server, room: room, name: info.name, imageID: info.imageID)
|
||||
let groupID = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroup.id)
|
||||
let model = TSGroupModel(title: openGroup.name, memberIds: [ getUserHexEncodedPublicKey() ], image: nil, groupId: groupID, groupType: .openGroup, adminIds: [])
|
||||
storage.write(with: { transaction in
|
||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||
let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction)
|
||||
thread.shouldThreadBeVisible = true
|
||||
thread.save(with: transaction)
|
||||
storage.setV2OpenGroup(openGroup, for: thread.uniqueId!, using: transaction)
|
||||
}, completion: {
|
||||
if let poller = OpenGroupManagerV2.shared.pollers[openGroup.id] {
|
||||
poller.stop()
|
||||
OpenGroupManagerV2.shared.pollers[openGroup.id] = nil
|
||||
}
|
||||
let poller = OpenGroupPollerV2(for: openGroup)
|
||||
poller.startIfNeeded()
|
||||
OpenGroupManagerV2.shared.pollers[openGroup.id] = poller
|
||||
seal.fulfill(())
|
||||
})
|
||||
}.catch(on: DispatchQueue.global(qos: .default)) { error in
|
||||
seal.reject(error)
|
||||
}
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue