Merge pull request #405 from oxen-io/open-group-invitations-2

Open Group Invitations
This commit is contained in:
Niels Andriesse 2021-05-07 15:47:55 +10:00 committed by GitHub
commit 16a01ccc00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 646 additions and 58 deletions

View File

@ -202,6 +202,7 @@
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; };
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */; };
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D448247E1BE300DB3608 /* PathVC.swift */; };
B87EF17126367CF800124B3C /* FileServerAPIV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B87EF17026367CF800124B3C /* FileServerAPIV2.swift */; };
@ -268,6 +269,8 @@
B8D64FCB25BA78A90029CFC0 /* SignalUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; };
B8D84EA325DF745A005A043E /* LinkPreviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D84EA225DF745A005A043E /* LinkPreviewState.swift */; };
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D84ECE25E3108A005A043E /* ExpandingAttachmentsButton.swift */; };
B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8EB20ED2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift */; };
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */; };
B8F5F52925EC4F8A003BF8D4 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F5F52825EC4F8A003BF8D4 /* BlockListUIUtils.m */; };
B8F5F54E25EC50A5003BF8D4 /* BlockListUIUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F5F52725EC4F6A003BF8D4 /* BlockListUIUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F5F56425EC8453003BF8D4 /* Notification+Contacts.swift */; };
@ -1196,6 +1199,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>"; };
B87588582644CA9D000E60D0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinOpenGroupModal.swift; sourceTree = "<group>"; };
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; };
B879D448247E1BE300DB3608 /* PathVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathVC.swift; sourceTree = "<group>"; };
B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = "<group>"; };
@ -1252,6 +1256,8 @@
B8D8F1BC25661C6F0092EF10 /* Storage+OnionRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+OnionRequests.swift"; sourceTree = "<group>"; };
B8D8F1EF256621180092EF10 /* MessageSender+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "MessageSender+Convenience.swift"; path = "../../SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift"; sourceTree = "<group>"; };
B8EB20E6263F7E4B00773E52 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
B8EB20ED2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+OpenGroupInvitation.swift"; sourceTree = "<group>"; };
B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupInvitationView.swift; sourceTree = "<group>"; };
B8F5F52725EC4F6A003BF8D4 /* BlockListUIUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BlockListUIUtils.h; sourceTree = "<group>"; };
B8F5F52825EC4F8A003BF8D4 /* BlockListUIUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BlockListUIUtils.m; sourceTree = "<group>"; };
B8F5F56425EC8453003BF8D4 /* Notification+Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Contacts.swift"; sourceTree = "<group>"; };
@ -2110,6 +2116,7 @@
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */,
B849789525D4A2F500D0D0B3 /* LinkPreviewView.swift */,
B8D84EA225DF745A005A043E /* LinkPreviewState.swift */,
B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */,
);
path = "Content Views";
sourceTree = "<group>";
@ -2134,6 +2141,7 @@
B8F5F72225F1B4CA003BF8D4 /* DownloadAttachmentModal.swift */,
C3A76A8C25DB83F90074CB90 /* PermissionMissingModal.swift */,
B821494525D4D6FF009C0F2A /* URLModal.swift */,
B8758859264503A6000E60D0 /* JoinOpenGroupModal.swift */,
B821494E25D4E163009C0F2A /* BodyTextView.swift */,
B82149B725D60393009C0F2A /* BlockedModal.swift */,
C374EEE125DA26740073A857 /* LinkPreviewModal.swift */,
@ -2380,6 +2388,7 @@
C3C2A75E2553A3C500C340D1 /* VisibleMessage+LinkPreview.swift */,
C3C2A7672553A3D900C340D1 /* VisibleMessage+Contact.swift */,
C300A5B12554AF9800555489 /* VisibleMessage+Profile.swift */,
B8EB20ED2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift */,
);
path = "Visible Messages";
sourceTree = "<group>";
@ -4732,6 +4741,7 @@
C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */,
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */,
C32C5B3F256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift in Sources */,
B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */,
C3C2A7712553A41E00C340D1 /* ControlMessage.swift in Sources */,
C32C5D19256DD493003C73A2 /* OWSLinkPreview.swift in Sources */,
C32C5CF0256DD3E4003C73A2 /* Storage+Shared.swift in Sources */,
@ -4920,6 +4930,7 @@
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */,
B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */,
B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */,
C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */,
3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */,
@ -4971,6 +4982,7 @@
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */,
C302093E25DCBF08001F572D /* MentionSelectionView.swift in Sources */,
C328251F25CA3A900062D0A7 /* QuoteView.swift in Sources */,
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */,
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */,
C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */,
3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */,

View File

@ -447,6 +447,9 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
// Scroll to the source of the reply
guard let indexPath = viewModel.ensureLoadWindowContainsQuotedReply(reply) else { return }
messagesTableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.middle, animated: true)
} else if let message = viewItem.interaction as? TSIncomingMessage, let name = message.openGroupInvitationName,
let url = message.openGroupInvitationURL {
joinOpenGroup(name: name, url: url)
}
default: break
}
@ -555,6 +558,14 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
present(urlModal, animated: true, completion: nil)
}
func joinOpenGroup(name: String, url: String) {
// Open groups can be unsafe, so always ask the user whether they want to join one
let joinOpenGroupModal = JoinOpenGroupModal(name: name, url: url)
joinOpenGroupModal.modalPresentationStyle = .overFullScreen
joinOpenGroupModal.modalTransitionStyle = .crossDissolve
present(joinOpenGroupModal, animated: true, completion: nil)
}
func handleReplyButtonTapped(for viewItem: ConversationViewItem) {
reply(viewItem)
}

View File

@ -0,0 +1,81 @@
final class OpenGroupInvitationView : UIView {
private let name: String
private let rawURL: String
private let textColor: UIColor
private let isOutgoing: Bool
private lazy var url: String = {
if let range = rawURL.range(of: "?public_key=") {
return String(rawURL[..<range.lowerBound])
} else {
return rawURL
}
}()
// MARK: Settings
private static let iconSize: CGFloat = 24
private static let iconImageViewSize: CGFloat = 48
// MARK: Lifecycle
init(name: String, url: String, textColor: UIColor, isOutgoing: Bool) {
self.name = name
self.rawURL = url
self.textColor = textColor
self.isOutgoing = isOutgoing
super.init(frame: CGRect.zero)
setUpViewHierarchy()
}
override init(frame: CGRect) {
preconditionFailure("Use init(name:url:textColor:) instead.")
}
required init?(coder: NSCoder) {
preconditionFailure("Use init(name:url:textColor:) instead.")
}
private func setUpViewHierarchy() {
// Title
let titleLabel = UILabel()
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.text = name
titleLabel.textColor = textColor
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
// Subtitle
let subtitleLabel = UILabel()
subtitleLabel.lineBreakMode = .byTruncatingTail
subtitleLabel.text = NSLocalizedString("view_open_group_invitation_description", comment: "")
subtitleLabel.textColor = textColor
subtitleLabel.font = .systemFont(ofSize: Values.smallFontSize)
// URL
let urlLabel = UILabel()
urlLabel.lineBreakMode = .byCharWrapping
urlLabel.text = url
urlLabel.textColor = textColor
urlLabel.numberOfLines = 0
urlLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
// Label stack
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, UIView.vSpacer(2), subtitleLabel, UIView.vSpacer(4), urlLabel ])
labelStackView.axis = .vertical
// Icon
let iconSize = OpenGroupInvitationView.iconSize
let iconName = isOutgoing ? "Globe" : "Plus"
let icon = UIImage(named: iconName)?.withTint(.white)?.resizedImage(to: CGSize(width: iconSize, height: iconSize))
let iconImageViewSize = OpenGroupInvitationView.iconImageViewSize
let iconImageView = UIImageView(image: icon)
iconImageView.contentMode = .center
iconImageView.layer.cornerRadius = iconImageViewSize / 2
iconImageView.layer.masksToBounds = true
iconImageView.backgroundColor = Colors.accent
iconImageView.set(.width, to: iconImageViewSize)
iconImageView.set(.height, to: iconImageViewSize)
// Main stack
let mainStackView = UIStackView(arrangedSubviews: [ iconImageView, labelStackView ])
mainStackView.axis = .horizontal
mainStackView.spacing = Values.mediumSpacing
mainStackView.alignment = .center
addSubview(mainStackView)
mainStackView.pin(to: self, withInset: Values.mediumSpacing)
}
}

View File

@ -241,7 +241,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
bubbleView.backgroundColor = (direction == .incoming) ? Colors.receivedMessageBackground : Colors.sentMessageBackground
updateBubbleViewCorners()
// Content view
populateContentView(for: viewItem)
populateContentView(for: viewItem, message: message)
// Date break
headerViewTopConstraint.constant = shouldInsetHeader ? Values.mediumSpacing : 1
headerView.subviews.forEach { $0.removeFromSuperview() }
@ -300,7 +300,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
dateBreakLabel.set(.height, to: dateBreakLabelSize.height)
}
private func populateContentView(for viewItem: ConversationViewItem) {
private func populateContentView(for viewItem: ConversationViewItem, message: TSMessage) {
snContentView.subviews.forEach { $0.removeFromSuperview() }
func showMediaPlaceholder() {
let mediaPlaceholderView = MediaPlaceholderView(viewItem: viewItem, textColor: bodyLabelTextColor)
@ -320,6 +320,10 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
linkPreviewView.linkPreviewState = LinkPreviewSent(linkPreview: linkPreview, imageAttachment: viewItem.linkPreviewAttachment)
snContentView.addSubview(linkPreviewView)
linkPreviewView.pin(to: snContentView)
} else if let openGroupInvitationName = message.openGroupInvitationName, let openGroupInvitationURL = message.openGroupInvitationURL {
let openGroupInvitationView = OpenGroupInvitationView(name: openGroupInvitationName, url: openGroupInvitationURL, textColor: bodyLabelTextColor, isOutgoing: isOutgoing)
snContentView.addSubview(openGroupInvitationView)
openGroupInvitationView.pin(to: snContentView)
} else {
// Stack view
let stackView = UIStackView(arrangedSubviews: [])

View File

@ -312,6 +312,20 @@ CGFloat kIconViewLength = 24;
actionBlock:^{
[weakSelf showMediaGallery];
}]];
if (self.isOpenGroup) {
[mainSection addItem:[OWSTableItem
itemWithCustomCellBlock:^{
return [weakSelf
disclosureCellWithName:NSLocalizedString(@"vc_conversation_settings_invite_button_title", "")
iconName:@"ic_plus_24"
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(
OWSConversationSettingsViewController, @"invite")];
}
actionBlock:^{
[weakSelf inviteUsersToOpenGroup];
}]];
}
[mainSection addItem:[OWSTableItem
itemWithCustomCellBlock:^{
@ -1089,6 +1103,31 @@ CGFloat kIconViewLength = 24;
UIPasteboard.generalPasteboard.string = ((TSContactThread *)self.thread).contactSessionID;
}
- (void)inviteUsersToOpenGroup
{
NSString *threadID = self.thread.uniqueId;
SNOpenGroupV2 *openGroup = [LKStorage.shared getV2OpenGroupForThreadID:threadID];
NSString *url = [NSString stringWithFormat:@"%@/%@?public_key=%@", openGroup.server, openGroup.room, openGroup.publicKey];
SNUserSelectionVC *userSelectionVC = [[SNUserSelectionVC alloc] initWithTitle:NSLocalizedString(@"vc_conversation_settings_invite_button_title", @"")
excluding:[NSSet new]
completion:^(NSSet<NSString *> *selectedUsers) {
for (NSString *user in selectedUsers) {
SNVisibleMessage *message = [SNVisibleMessage new];
message.sentTimestamp = [NSDate millisecondTimestamp];
message.openGroupInvitation = [[SNOpenGroupInvitation alloc] initWithName:openGroup.name url:url];
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactSessionID:user];
TSOutgoingMessage *tsMessage = [TSOutgoingMessage from:message associatedWith:thread];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[tsMessage saveWithTransaction:transaction];
}];
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[SNMessageSender send:message inThread:thread usingTransaction:transaction];
}];
}
}];
[self.navigationController pushViewController:userSelectionVC animated:YES];
}
- (void)showMediaGallery
{
OWSLogDebug(@"");

View File

@ -0,0 +1,85 @@
final class JoinOpenGroupModal : Modal {
private let name: String
private let url: String
// MARK: Lifecycle
init(name: String, url: String) {
self.name = name
self.url = url
super.init(nibName: nil, bundle: nil)
}
override init(nibName: String?, bundle: Bundle?) {
preconditionFailure("Use init(name:url:) instead.")
}
required init?(coder: NSCoder) {
preconditionFailure("Use init(name:url:) instead.")
}
override func populateContentView() {
// Title
let titleLabel = UILabel()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize)
titleLabel.text = "Join \(name)?"
titleLabel.textAlignment = .center
// Message
let messageLabel = UILabel()
messageLabel.textColor = Colors.text
messageLabel.font = .systemFont(ofSize: Values.smallFontSize)
let message = "Are you sure you want to join the \(name) open group?";
let attributedMessage = NSMutableAttributedString(string: message)
attributedMessage.addAttributes([ .font : UIFont.boldSystemFont(ofSize: Values.smallFontSize) ], range: (message as NSString).range(of: name))
messageLabel.attributedText = attributedMessage
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.textAlignment = .center
// Join button
let joinButton = UIButton()
joinButton.set(.height, to: Values.mediumButtonHeight)
joinButton.layer.cornerRadius = Modal.buttonCornerRadius
joinButton.backgroundColor = Colors.buttonBackground
joinButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize)
joinButton.setTitleColor(Colors.text, for: UIControl.State.normal)
joinButton.setTitle("Join", for: UIControl.State.normal)
joinButton.addTarget(self, action: #selector(joinOpenGroup), for: UIControl.Event.touchUpInside)
// Button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, joinButton ])
buttonStackView.axis = .horizontal
buttonStackView.spacing = Values.mediumSpacing
buttonStackView.distribution = .fillEqually
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ titleLabel, messageLabel, buttonStackView ])
mainStackView.axis = .vertical
mainStackView.spacing = Values.largeSpacing
contentView.addSubview(mainStackView)
mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing)
mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing)
contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.largeSpacing)
contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.largeSpacing)
}
// MARK: Interaction
@objc private func joinOpenGroup() {
guard let (room, server, publicKey) = OpenGroupManagerV2.parseV2OpenGroup(from: url), Features.useV2OpenGroups else {
let alert = UIAlertController(title: "Couldn't Join", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
return presentingViewController!.present(alert, animated: true, completion: nil)
}
presentingViewController!.dismiss(animated: true, completion: nil)
Storage.shared.write { [presentingViewController = self.presentingViewController!] transaction in
OpenGroupManagerV2.shared.add(room: room, server: server, publicKey: publicKey, using: transaction)
.done(on: DispatchQueue.main) { _ in
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete() // FIXME: It's probably cleaner to do this inside addOpenGroup(...)
}
.catch(on: DispatchQueue.main) { error in
let alert = UIAlertController(title: "Couldn't Join", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
presentingViewController.present(alert, animated: true, completion: nil)
}
}
}
}

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -525,3 +525,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "اشتراک گذاری با Session";
"vc_share_loading_message" = "آماده سازی پیوست‌ها...";
"vc_share_sending_message" = "در حال ارسال...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -525,3 +525,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Compartilhar no Session";
"vc_share_loading_message" = "Preparando anexos...";
"vc_share_sending_message" = "Enviando...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -523,3 +523,5 @@
"vc_share_title" = "Share to Session";
"vc_share_loading_message" = "Preparing attachments...";
"vc_share_sending_message" = "Sending...";
"view_open_group_invitation_description" = "Open group invitation";
"vc_conversation_settings_invite_button_title" = "Add Members";

View File

@ -1,4 +1,5 @@
@objc(SNUserSelectionVC)
final class UserSelectionVC : BaseVC, UITableViewDataSource, UITableViewDelegate {
private let navBarTitle: String
private let usersToExclude: Set<String>
@ -25,7 +26,8 @@ final class UserSelectionVC : BaseVC, UITableViewDataSource, UITableViewDelegate
}()
// MARK: Lifecycle
@objc init(with title: String, excluding usersToExclude: Set<String>, completion: @escaping (Set<String>) -> Void) {
@objc(initWithTitle:excluding:completion:)
init(with title: String, excluding usersToExclude: Set<String>, completion: @escaping (Set<String>) -> Void) {
self.navBarTitle = title
self.usersToExclude = usersToExclude
self.completion = completion

View File

@ -4,9 +4,11 @@ import SessionSnodeKit
@objc(SNFileServerAPIV2)
public final class FileServerAPIV2 : NSObject {
// MARK: Settings
@objc public static let server = "http://88.99.175.227"
public static let serverPublicKey = "7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69"
// MARK: Initialization
private override init() { }
// MARK: Error

View File

@ -1,6 +1,6 @@
@objc(SNJob)
public protocol Job : class, NSCoding {
public protocol Job : NSCoding {
var delegate: JobDelegate? { get set }
var id: String? { get set }
var failureCount: UInt { get set }

View File

@ -80,11 +80,11 @@ public final class ConfigurationMessage : ControlMessage {
public override var description: String {
"""
ConfigurationMessage(
closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription)
openGroups: \([String](openGroups).prettifiedDescription)
displayName: \(displayName ?? "null")
profilePictureURL: \(profilePictureURL ?? "null")
profileKey: \(profileKey?.toHexString() ?? "null")
closedGroups: \([ClosedGroup](closedGroups).prettifiedDescription),
openGroups: \([String](openGroups).prettifiedDescription),
displayName: \(displayName ?? "null"),
profilePictureURL: \(profilePictureURL ?? "null"),
profileKey: \(profileKey?.toHexString() ?? "null"),
contacts: \([Contact](contacts).prettifiedDescription)
)
"""

View File

@ -78,7 +78,7 @@ public final class ExpirationTimerUpdate : ControlMessage {
public override var description: String {
"""
ExpirationTimerUpdate(
syncTarget: \(syncTarget ?? "null")
syncTarget: \(syncTarget ?? "null"),
duration: \(duration?.description ?? "null")
)
"""

View File

@ -20,7 +20,9 @@ public extension TSIncomingMessage {
quotedMessage: quotedMessage,
linkPreview: linkPreview,
serverTimestamp: visibleMessage.openGroupServerTimestamp as NSNumber?,
wasReceivedByUD: true
wasReceivedByUD: true,
openGroupInvitationName: visibleMessage.openGroupInvitation?.name,
openGroupInvitationURL: visibleMessage.openGroupInvitation?.url
)
result.openGroupServerMessageID = openGroupServerMessageID
return result

View File

@ -58,7 +58,9 @@ NS_ASSUME_NONNULL_BEGIN
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview
serverTimestamp:(nullable NSNumber *)serverTimestamp
wasReceivedByUD:(BOOL)wasReceivedByUD NS_DESIGNATED_INITIALIZER;
wasReceivedByUD:(BOOL)wasReceivedByUD
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

View File

@ -53,6 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
linkPreview:(nullable OWSLinkPreview *)linkPreview
serverTimestamp:(nullable NSNumber *)serverTimestamp
wasReceivedByUD:(BOOL)wasReceivedByUD
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL
{
self = [super initMessageWithTimestamp:timestamp
inThread:thread
@ -61,7 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
expiresInSeconds:expiresInSeconds
expireStartedAt:0
quotedMessage:quotedMessage
linkPreview:linkPreview];
linkPreview:linkPreview
openGroupInvitationName:openGroupInvitationName
openGroupInvitationURL:openGroupInvitationURL];
if (!self) {
return self;

View File

@ -55,7 +55,9 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
expiresInSeconds:0
expireStartedAt:0
quotedMessage:nil
linkPreview:nil];
linkPreview:nil
openGroupInvitationName:nil
openGroupInvitationURL:nil];
if (!self) {
return self;

View File

@ -36,6 +36,8 @@ extern const NSUInteger kOversizeTextMessageSizeThreshold;
@property (nonatomic, nullable) OWSLinkPreview *linkPreview;
@property (nonatomic) uint64_t openGroupServerMessageID;
@property (nonatomic, readonly) BOOL isOpenGroupMessage;
@property (nonatomic, readonly, nullable) NSString *openGroupInvitationName;
@property (nonatomic, readonly, nullable) NSString *openGroupInvitationURL;
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;
@ -46,7 +48,9 @@ extern const NSUInteger kOversizeTextMessageSizeThreshold;
expiresInSeconds:(uint32_t)expiresInSeconds
expireStartedAt:(uint64_t)expireStartedAt
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview NS_DESIGNATED_INITIALIZER;
linkPreview:(nullable OWSLinkPreview *)linkPreview
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

View File

@ -63,6 +63,8 @@ const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
expireStartedAt:(uint64_t)expireStartedAt
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL
{
self = [super initInteractionWithTimestamp:timestamp inThread:thread];
@ -80,6 +82,8 @@ const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
_quotedMessage = quotedMessage;
_linkPreview = linkPreview;
_openGroupServerMessageID = 0;
_openGroupInvitationName = openGroupInvitationName;
_openGroupInvitationURL = openGroupInvitationURL;
return self;
}
@ -342,6 +346,8 @@ const NSUInteger kOversizeTextMessageSizeThreshold = 2 * 1024;
return bodyDescription;
} else if (attachmentDescription.length > 0) {
return attachmentDescription;
} else if (self.openGroupInvitationName != nil) {
return @"😎 Open group invitation";
} else {
// TODO: We should do better here.
return @"";

View File

@ -28,7 +28,9 @@ import SessionUtilitiesKit
isVoiceMessage: false,
groupMetaMessage: .unspecified,
quotedMessage: TSQuotedMessage.from(visibleMessage.quote),
linkPreview: OWSLinkPreview.from(visibleMessage.linkPreview)
linkPreview: OWSLinkPreview.from(visibleMessage.linkPreview),
openGroupInvitationName: visibleMessage.openGroupInvitation?.name,
openGroupInvitationURL: visibleMessage.openGroupInvitation?.url
)
}
}

View File

@ -95,7 +95,9 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
isVoiceMessage:(BOOL)isVoiceMessage
groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview NS_DESIGNATED_INITIALIZER;
linkPreview:(nullable OWSLinkPreview *)linkPreview
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

View File

@ -269,7 +269,9 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
isVoiceMessage:NO
groupMetaMessage:TSGroupMetaMessageUnspecified
quotedMessage:quotedMessage
linkPreview:linkPreview];
linkPreview:linkPreview
openGroupInvitationName:nil
openGroupInvitationURL:nil];
}
+ (instancetype)outgoingMessageInThread:(nullable TSThread *)thread
@ -286,7 +288,9 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
isVoiceMessage:NO
groupMetaMessage:groupMetaMessage
quotedMessage:nil
linkPreview:nil];
linkPreview:nil
openGroupInvitationName:nil
openGroupInvitationURL:nil];
}
- (instancetype)initOutgoingMessageWithTimestamp:(uint64_t)timestamp
@ -299,6 +303,8 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
groupMetaMessage:(TSGroupMetaMessage)groupMetaMessage
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL
{
self = [super initMessageWithTimestamp:timestamp
inThread:thread
@ -307,7 +313,9 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
expiresInSeconds:expiresInSeconds
expireStartedAt:expireStartedAt
quotedMessage:quotedMessage
linkPreview:linkPreview];
linkPreview:linkPreview
openGroupInvitationName:openGroupInvitationName
openGroupInvitationURL:openGroupInvitationURL];
if (!self) {
return self;
}

View File

@ -61,8 +61,8 @@ public extension VisibleMessage {
public override var description: String {
"""
LinkPreview(
title: \(title ?? "null")
url: \(url ?? "null")
title: \(title ?? "null"),
url: \(url ?? "null"),
attachmentID: \(attachmentID ?? "null")
)
"""

View File

@ -0,0 +1,56 @@
import SessionUtilitiesKit
public extension VisibleMessage {
@objc(SNOpenGroupInvitation)
class OpenGroupInvitation : NSObject, NSCoding {
public var name: String?
public var url: String?
@objc
public init(name: String, url: String) {
self.name = name
self.url = url
}
public required init?(coder: NSCoder) {
if let name = coder.decodeObject(forKey: "name") as! String? { self.name = name }
if let url = coder.decodeObject(forKey: "url") as! String? { self.url = url }
}
public func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(url, forKey: "url")
}
public static func fromProto(_ proto: SNProtoDataMessageOpenGroupInvitation) -> OpenGroupInvitation? {
let url = proto.url
let name = proto.name
return OpenGroupInvitation(name: name, url: url)
}
public func toProto() -> SNProtoDataMessageOpenGroupInvitation? {
guard let url = url, let name = name else {
SNLog("Couldn't construct open group invitation proto from: \(self).")
return nil
}
let openGroupInvitationProto = SNProtoDataMessageOpenGroupInvitation.builder(url: url, name: name)
do {
return try openGroupInvitationProto.build()
} catch {
SNLog("Couldn't construct open group invitation proto from: \(self).")
return nil
}
}
// MARK: Description
public override var description: String {
"""
OpenGroupInvitation(
name: \(name ?? "null"),
url: \(url ?? "null")
)
"""
}
}
}

View File

@ -62,8 +62,8 @@ public extension VisibleMessage {
public override var description: String {
"""
Profile(
displayName: \(displayName ?? "null")
profileKey: \(profileKey?.description ?? "null")
displayName: \(displayName ?? "null"),
profileKey: \(profileKey?.description ?? "null"),
profilePictureURL: \(profilePictureURL ?? "null")
)
"""

View File

@ -88,9 +88,9 @@ public extension VisibleMessage {
public override var description: String {
"""
Quote(
timestamp: \(timestamp?.description ?? "null")
publicKey: \(publicKey ?? "null")
text: \(text ?? "null")
timestamp: \(timestamp?.description ?? "null"),
publicKey: \(publicKey ?? "null"),
text: \(text ?? "null"),
attachmentID: \(attachmentID ?? "null")
)
"""

View File

@ -12,6 +12,7 @@ public final class VisibleMessage : Message {
@objc public var linkPreview: LinkPreview?
@objc public var contact: Contact?
@objc public var profile: Profile?
@objc public var openGroupInvitation: OpenGroupInvitation?
public override var isSelfSendValid: Bool { true }
@ -22,6 +23,7 @@ public final class VisibleMessage : Message {
public override var isValid: Bool {
guard super.isValid else { return false }
if !attachmentIDs.isEmpty { return true }
if openGroupInvitation != nil { return true }
if let text = text?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty { return true }
return false
}
@ -36,6 +38,7 @@ public final class VisibleMessage : Message {
if let linkPreview = coder.decodeObject(forKey: "linkPreview") as! LinkPreview? { self.linkPreview = linkPreview }
// TODO: Contact
if let profile = coder.decodeObject(forKey: "profile") as! Profile? { self.profile = profile }
if let openGroupInvitation = coder.decodeObject(forKey: "openGroupInvitation") as! OpenGroupInvitation? { self.openGroupInvitation = openGroupInvitation }
}
public override func encode(with coder: NSCoder) {
@ -47,6 +50,7 @@ public final class VisibleMessage : Message {
coder.encode(linkPreview, forKey: "linkPreview")
// TODO: Contact
coder.encode(profile, forKey: "profile")
coder.encode(openGroupInvitation, forKey: "openGroupInvitation")
}
// MARK: Proto Conversion
@ -59,6 +63,8 @@ public final class VisibleMessage : Message {
if let linkPreviewProto = dataMessage.preview.first, let linkPreview = LinkPreview.fromProto(linkPreviewProto) { result.linkPreview = linkPreview }
// TODO: Contact
if let profile = Profile.fromProto(dataMessage) { result.profile = profile }
if let openGroupInvitationProto = dataMessage.openGroupInvitation,
let openGroupInvitation = OpenGroupInvitation.fromProto(openGroupInvitationProto) { result.openGroupInvitation = openGroupInvitation }
result.syncTarget = dataMessage.syncTarget
return result
}
@ -95,6 +101,8 @@ public final class VisibleMessage : Message {
let attachmentProtos = attachments.compactMap { $0.buildProto() }
dataMessage.setAttachments(attachmentProtos)
// TODO: Contact
// Open group invitation
if let openGroupInvitation = openGroupInvitation, let openGroupInvitationProto = openGroupInvitation.toProto() { dataMessage.setOpenGroupInvitation(openGroupInvitationProto) }
// Expiration timer
// TODO: We * want * expiration timer updates to be explicit. But currently Android will disable the expiration timer for a conversation
// if it receives a message without the current expiration timer value attached to it...
@ -128,12 +136,13 @@ public final class VisibleMessage : Message {
public override var description: String {
"""
VisibleMessage(
text: \(text ?? "null")
attachmentIDs: \(attachmentIDs)
quote: \(quote?.description ?? "null")
linkPreview: \(linkPreview?.description ?? "null")
contact: \(contact?.description ?? "null")
text: \(text ?? "null"),
attachmentIDs: \(attachmentIDs),
quote: \(quote?.description ?? "null"),
linkPreview: \(linkPreview?.description ?? "null"),
contact: \(contact?.description ?? "null"),
profile: \(profile?.description ?? "null")
"openGroupInvitation": \(openGroupInvitation?.description ?? "null")
)
"""
}

View File

@ -4,8 +4,8 @@ public final class OpenGroupV2 : NSObject, NSCoding { // NSObject/NSCoding confo
@objc public let server: String
@objc public let room: String
public let id: String
public let name: String
public let publicKey: String
@objc public let name: String
@objc public let publicKey: String
/// The ID with which the image can be retrieved from the server.
public let imageID: String?

View File

@ -1294,6 +1294,118 @@ extension SNProtoDataMessageLokiProfile.SNProtoDataMessageLokiProfileBuilder {
#endif
// MARK: - SNProtoDataMessageOpenGroupInvitation
@objc public class SNProtoDataMessageOpenGroupInvitation: NSObject {
// MARK: - SNProtoDataMessageOpenGroupInvitationBuilder
@objc public class func builder(url: String, name: String) -> SNProtoDataMessageOpenGroupInvitationBuilder {
return SNProtoDataMessageOpenGroupInvitationBuilder(url: url, name: name)
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SNProtoDataMessageOpenGroupInvitationBuilder {
let builder = SNProtoDataMessageOpenGroupInvitationBuilder(url: url, name: name)
return builder
}
@objc public class SNProtoDataMessageOpenGroupInvitationBuilder: NSObject {
private var proto = SessionProtos_DataMessage.OpenGroupInvitation()
@objc fileprivate override init() {}
@objc fileprivate init(url: String, name: String) {
super.init()
setUrl(url)
setName(name)
}
@objc public func setUrl(_ valueParam: String) {
proto.url = valueParam
}
@objc public func setName(_ valueParam: String) {
proto.name = valueParam
}
@objc public func build() throws -> SNProtoDataMessageOpenGroupInvitation {
return try SNProtoDataMessageOpenGroupInvitation.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SNProtoDataMessageOpenGroupInvitation.parseProto(proto).serializedData()
}
}
fileprivate let proto: SessionProtos_DataMessage.OpenGroupInvitation
@objc public let url: String
@objc public let name: String
private init(proto: SessionProtos_DataMessage.OpenGroupInvitation,
url: String,
name: String) {
self.proto = proto
self.url = url
self.name = name
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SNProtoDataMessageOpenGroupInvitation {
let proto = try SessionProtos_DataMessage.OpenGroupInvitation(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SessionProtos_DataMessage.OpenGroupInvitation) throws -> SNProtoDataMessageOpenGroupInvitation {
guard proto.hasURL else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: url")
}
let url = proto.url
guard proto.hasName else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: name")
}
let name = proto.name
// MARK: - Begin Validation Logic for SNProtoDataMessageOpenGroupInvitation -
// MARK: - End Validation Logic for SNProtoDataMessageOpenGroupInvitation -
let result = SNProtoDataMessageOpenGroupInvitation(proto: proto,
url: url,
name: name)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SNProtoDataMessageOpenGroupInvitation {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SNProtoDataMessageOpenGroupInvitation.SNProtoDataMessageOpenGroupInvitationBuilder {
@objc public func buildIgnoringErrors() -> SNProtoDataMessageOpenGroupInvitation? {
return try! self.build()
}
}
#endif
// MARK: - SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper
@objc public class SNProtoDataMessageClosedGroupControlMessageKeyPairWrapper: NSObject {
@ -1696,6 +1808,9 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
if let _value = profile {
builder.setProfile(_value)
}
if let _value = openGroupInvitation {
builder.setOpenGroupInvitation(_value)
}
if let _value = closedGroupControlMessage {
builder.setClosedGroupControlMessage(_value)
}
@ -1763,6 +1878,10 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
proto.profile = valueParam.proto
}
@objc public func setOpenGroupInvitation(_ valueParam: SNProtoDataMessageOpenGroupInvitation) {
proto.openGroupInvitation = valueParam.proto
}
@objc public func setClosedGroupControlMessage(_ valueParam: SNProtoDataMessageClosedGroupControlMessage) {
proto.closedGroupControlMessage = valueParam.proto
}
@ -1792,6 +1911,8 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
@objc public let profile: SNProtoDataMessageLokiProfile?
@objc public let openGroupInvitation: SNProtoDataMessageOpenGroupInvitation?
@objc public let closedGroupControlMessage: SNProtoDataMessageClosedGroupControlMessage?
@objc public var body: String? {
@ -1851,6 +1972,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
quote: SNProtoDataMessageQuote?,
preview: [SNProtoDataMessagePreview],
profile: SNProtoDataMessageLokiProfile?,
openGroupInvitation: SNProtoDataMessageOpenGroupInvitation?,
closedGroupControlMessage: SNProtoDataMessageClosedGroupControlMessage?) {
self.proto = proto
self.attachments = attachments
@ -1858,6 +1980,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
self.quote = quote
self.preview = preview
self.profile = profile
self.openGroupInvitation = openGroupInvitation
self.closedGroupControlMessage = closedGroupControlMessage
}
@ -1893,6 +2016,11 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
profile = try SNProtoDataMessageLokiProfile.parseProto(proto.profile)
}
var openGroupInvitation: SNProtoDataMessageOpenGroupInvitation? = nil
if proto.hasOpenGroupInvitation {
openGroupInvitation = try SNProtoDataMessageOpenGroupInvitation.parseProto(proto.openGroupInvitation)
}
var closedGroupControlMessage: SNProtoDataMessageClosedGroupControlMessage? = nil
if proto.hasClosedGroupControlMessage {
closedGroupControlMessage = try SNProtoDataMessageClosedGroupControlMessage.parseProto(proto.closedGroupControlMessage)
@ -1908,6 +2036,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr
quote: quote,
preview: preview,
profile: profile,
openGroupInvitation: openGroupInvitation,
closedGroupControlMessage: closedGroupControlMessage)
return result
}

View File

@ -440,6 +440,15 @@ struct SessionProtos_DataMessage {
/// Clears the value of `profile`. Subsequent reads from it will return its default value.
mutating func clearProfile() {_uniqueStorage()._profile = nil}
var openGroupInvitation: SessionProtos_DataMessage.OpenGroupInvitation {
get {return _storage._openGroupInvitation ?? SessionProtos_DataMessage.OpenGroupInvitation()}
set {_uniqueStorage()._openGroupInvitation = newValue}
}
/// Returns true if `openGroupInvitation` has been explicitly set.
var hasOpenGroupInvitation: Bool {return _storage._openGroupInvitation != nil}
/// Clears the value of `openGroupInvitation`. Subsequent reads from it will return its default value.
mutating func clearOpenGroupInvitation() {_uniqueStorage()._openGroupInvitation = nil}
var closedGroupControlMessage: SessionProtos_DataMessage.ClosedGroupControlMessage {
get {return _storage._closedGroupControlMessage ?? SessionProtos_DataMessage.ClosedGroupControlMessage()}
set {_uniqueStorage()._closedGroupControlMessage = newValue}
@ -670,6 +679,39 @@ struct SessionProtos_DataMessage {
fileprivate var _profilePicture: String? = nil
}
struct OpenGroupInvitation {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// @required
var url: String {
get {return _url ?? String()}
set {_url = newValue}
}
/// Returns true if `url` has been explicitly set.
var hasURL: Bool {return self._url != nil}
/// Clears the value of `url`. Subsequent reads from it will return its default value.
mutating func clearURL() {self._url = nil}
/// @required
var name: String {
get {return _name ?? String()}
set {_name = newValue}
}
/// Returns true if `name` has been explicitly set.
var hasName: Bool {return self._name != nil}
/// Clears the value of `name`. Subsequent reads from it will return its default value.
mutating func clearName() {self._name = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _url: String? = nil
fileprivate var _name: String? = nil
}
struct ClosedGroupControlMessage {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@ -1633,6 +1675,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
8: .same(proto: "quote"),
10: .same(proto: "preview"),
101: .same(proto: "profile"),
102: .same(proto: "openGroupInvitation"),
104: .same(proto: "closedGroupControlMessage"),
105: .same(proto: "syncTarget"),
]
@ -1648,6 +1691,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
var _quote: SessionProtos_DataMessage.Quote? = nil
var _preview: [SessionProtos_DataMessage.Preview] = []
var _profile: SessionProtos_DataMessage.LokiProfile? = nil
var _openGroupInvitation: SessionProtos_DataMessage.OpenGroupInvitation? = nil
var _closedGroupControlMessage: SessionProtos_DataMessage.ClosedGroupControlMessage? = nil
var _syncTarget: String? = nil
@ -1666,6 +1710,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
_quote = source._quote
_preview = source._preview
_profile = source._profile
_openGroupInvitation = source._openGroupInvitation
_closedGroupControlMessage = source._closedGroupControlMessage
_syncTarget = source._syncTarget
}
@ -1684,6 +1729,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
if let v = _storage._group, !v.isInitialized {return false}
if let v = _storage._quote, !v.isInitialized {return false}
if !SwiftProtobuf.Internal.areAllInitialized(_storage._preview) {return false}
if let v = _storage._openGroupInvitation, !v.isInitialized {return false}
if let v = _storage._closedGroupControlMessage, !v.isInitialized {return false}
return true
}
@ -1704,6 +1750,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
case 8: try decoder.decodeSingularMessageField(value: &_storage._quote)
case 10: try decoder.decodeRepeatedMessageField(value: &_storage._preview)
case 101: try decoder.decodeSingularMessageField(value: &_storage._profile)
case 102: try decoder.decodeSingularMessageField(value: &_storage._openGroupInvitation)
case 104: try decoder.decodeSingularMessageField(value: &_storage._closedGroupControlMessage)
case 105: try decoder.decodeSingularStringField(value: &_storage._syncTarget)
default: break
@ -1744,6 +1791,9 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
if let v = _storage._profile {
try visitor.visitSingularMessageField(value: v, fieldNumber: 101)
}
if let v = _storage._openGroupInvitation {
try visitor.visitSingularMessageField(value: v, fieldNumber: 102)
}
if let v = _storage._closedGroupControlMessage {
try visitor.visitSingularMessageField(value: v, fieldNumber: 104)
}
@ -1769,6 +1819,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa
if _storage._quote != rhs_storage._quote {return false}
if _storage._preview != rhs_storage._preview {return false}
if _storage._profile != rhs_storage._profile {return false}
if _storage._openGroupInvitation != rhs_storage._openGroupInvitation {return false}
if _storage._closedGroupControlMessage != rhs_storage._closedGroupControlMessage {return false}
if _storage._syncTarget != rhs_storage._syncTarget {return false}
return true
@ -2058,6 +2109,47 @@ extension SessionProtos_DataMessage.LokiProfile: SwiftProtobuf.Message, SwiftPro
}
}
extension SessionProtos_DataMessage.OpenGroupInvitation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_DataMessage.protoMessageName + ".OpenGroupInvitation"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "url"),
3: .same(proto: "name"),
]
public var isInitialized: Bool {
if self._url == nil {return false}
if self._name == nil {return false}
return true
}
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularStringField(value: &self._url)
case 3: try decoder.decodeSingularStringField(value: &self._name)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._url {
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
}
if let v = self._name {
try visitor.visitSingularStringField(value: v, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SessionProtos_DataMessage.OpenGroupInvitation, rhs: SessionProtos_DataMessage.OpenGroupInvitation) -> Bool {
if lhs._url != rhs._url {return false}
if lhs._name != rhs._name {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension SessionProtos_DataMessage.ClosedGroupControlMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = SessionProtos_DataMessage.protoMessageName + ".ClosedGroupControlMessage"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [

View File

@ -101,6 +101,13 @@ message DataMessage {
optional string profilePicture = 2;
}
message OpenGroupInvitation {
// @required
required string url = 1;
// @required
required string name = 3;
}
message ClosedGroupControlMessage {
enum Type {
@ -140,6 +147,7 @@ message DataMessage {
optional Quote quote = 8;
repeated Preview preview = 10;
optional LokiProfile profile = 101;
optional OpenGroupInvitation openGroupInvitation = 102;
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
optional string syncTarget = 105;
}

View File

@ -92,22 +92,22 @@ public enum MessageReceiver {
}
groupPublicKey = envelope.source
try decrypt()
// do {
// try decrypt()
// } catch {
// do {
// let now = Date()
// // Don't spam encryption key pair requests
// let shouldRequestEncryptionKeyPair = given(lastEncryptionKeyPairRequest[groupPublicKey!]) { now.timeIntervalSince($0) > 30 } ?? true
// if shouldRequestEncryptionKeyPair {
// try MessageSender.requestEncryptionKeyPair(for: groupPublicKey!, using: transaction as! YapDatabaseReadWriteTransaction)
// lastEncryptionKeyPairRequest[groupPublicKey!] = now
// }
// }
// throw error // Throw the * decryption * error and not the error generated by requestEncryptionKeyPair (if it generated one)
// }
/*
do {
try decrypt()
} catch {
do {
let now = Date()
// Don't spam encryption key pair requests
let shouldRequestEncryptionKeyPair = given(lastEncryptionKeyPairRequest[groupPublicKey!]) { now.timeIntervalSince($0) > 30 } ?? true
if shouldRequestEncryptionKeyPair {
try MessageSender.requestEncryptionKeyPair(for: groupPublicKey!, using: transaction as! YapDatabaseReadWriteTransaction)
lastEncryptionKeyPairRequest[groupPublicKey!] = now
}
}
throw error // Throw the * decryption * error and not the error generated by requestEncryptionKeyPair (if it generated one)
}
*/
default: throw Error.unknownEnvelopeType
}
}

View File

@ -9,7 +9,6 @@ public final class MessageSender : NSObject {
public enum Error : LocalizedError {
case invalidMessage
case protoConversionFailed
case proofOfWorkCalculationFailed
case noUserX25519KeyPair
case noUserED25519KeyPair
case signingFailed
@ -22,7 +21,7 @@ public final class MessageSender : NSObject {
internal var isRetryable: Bool {
switch self {
case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate, .signingFailed, .encryptionFailed: return false
case .invalidMessage, .protoConversionFailed, .invalidClosedGroupUpdate, .signingFailed, .encryptionFailed: return false
default: return true
}
}
@ -31,7 +30,6 @@ public final class MessageSender : NSObject {
switch self {
case .invalidMessage: return "Invalid message."
case .protoConversionFailed: return "Couldn't convert message to proto."
case .proofOfWorkCalculationFailed: return "Proof of work calculation failed."
case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair."
case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair."
case .signingFailed: return "Couldn't sign message."
@ -48,8 +46,6 @@ public final class MessageSender : NSObject {
// MARK: Initialization
private override init() { }
public static let shared = MessageSender() // FIXME: Remove once requestSenderKey is static
// MARK: Preparation
public static func prep(_ signalAttachments: [SignalAttachment], for message: VisibleMessage, using transaction: YapDatabaseReadWriteTransaction) {
guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else {

View File

@ -7,7 +7,7 @@ public final class ClosedGroupPoller : NSObject {
private var timer: Timer?
// MARK: Settings
private static let pollInterval: TimeInterval = 2
private static let pollInterval: TimeInterval = 3
// MARK: Error
private enum Error : LocalizedError {

View File

@ -18,7 +18,7 @@ public final class OpenGroupPoller : NSObject {
}
// MARK: Settings
private let pollForNewMessagesInterval: TimeInterval = 4
private let pollForNewMessagesInterval: TimeInterval = 8
private let pollForDeletedMessagesInterval: TimeInterval = 30
private let pollForModeratorsInterval: TimeInterval = 10 * 60

View File

@ -9,7 +9,7 @@ public final class Poller : NSObject {
private var pollCount = 0
// MARK: Settings
private static let pollInterval: TimeInterval = 1
private static let pollInterval: TimeInterval = 1.5
private static let retryInterval: TimeInterval = 0.25
/// After polling a given snode this many times we always switch to a new one.
///