mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Further work on unit tests (and a couple of bug fixes found when testing)
Removed a couple remaining TODOs Added 'standardUserDefaults' to the 'Dependencies' type Tweaked the OpenGroupAPI to only update the 'lastOpen' timestamp if it successfully polls Refactored a couple of methods in the ConversationViewItem into swift so we can clean up the OpenGroupAPI more Updated the OpenGroupAPI so it no longer has static variables for state (shifted to the OpenGroupManager and made them instance variables) Fixed an encoding issue with the Capabilities.Capability
This commit is contained in:
parent
f9468219d9
commit
17a9e510c5
19 changed files with 1118 additions and 480 deletions
|
@ -753,7 +753,6 @@
|
|||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
||||
C3DB66AC260ACA42001EFC55 /* OpenGroupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */; };
|
||||
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */; };
|
||||
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */; };
|
||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
||||
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECBF7A257056B700EA7FCE /* Threading.swift */; };
|
||||
|
@ -795,6 +794,8 @@
|
|||
FD83B9C927D0487A005E1583 /* SendDirectMessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */; };
|
||||
FD83B9CC27D179BC005E1583 /* FSEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9CB27D179BC005E1583 /* FSEndpoint.swift */; };
|
||||
FD83B9CE27D17A04005E1583 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9CD27D17A04005E1583 /* Request.swift */; };
|
||||
FD83B9D227D59495005E1583 /* TestUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9D127D59495005E1583 /* TestUserDefaults.swift */; };
|
||||
FD83B9D427D5A7D5005E1583 /* ConversationViewItem+Refactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9D327D5A7D5005E1583 /* ConversationViewItem+Refactor.swift */; };
|
||||
FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF127BF6BA200510D0C /* Data+Utilities.swift */; };
|
||||
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* TestSodium.swift */; };
|
||||
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* TestSign.swift */; };
|
||||
|
@ -990,13 +991,6 @@
|
|||
remoteGlobalIDString = C3C2A678255388CC00C340D1;
|
||||
remoteInfo = SessionUtilitiesKit;
|
||||
};
|
||||
FDC438BA27BB276F00C60D73 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D221A080169C9E5E00537ABF /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D221A088169C9E5E00537ABF;
|
||||
remoteInfo = Session;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -1866,7 +1860,6 @@
|
|||
C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = "<group>"; };
|
||||
C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManager.swift; sourceTree = "<group>"; };
|
||||
C3DB66C2260ACCE6001EFC55 /* OpenGroupPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupPoller.swift; sourceTree = "<group>"; };
|
||||
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenGroupAPI+ObjC.swift"; sourceTree = "<group>"; };
|
||||
C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = "<group>"; };
|
||||
C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = "<group>"; };
|
||||
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sodium+Utilities.swift"; sourceTree = "<group>"; };
|
||||
|
@ -1933,6 +1926,8 @@
|
|||
FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendDirectMessageResponse.swift; sourceTree = "<group>"; };
|
||||
FD83B9CB27D179BC005E1583 /* FSEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSEndpoint.swift; sourceTree = "<group>"; };
|
||||
FD83B9CD27D17A04005E1583 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
|
||||
FD83B9D127D59495005E1583 /* TestUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUserDefaults.swift; sourceTree = "<group>"; };
|
||||
FD83B9D327D5A7D5005E1583 /* ConversationViewItem+Refactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationViewItem+Refactor.swift"; sourceTree = "<group>"; };
|
||||
FD859EEF27BF207700510D0C /* SessionProtos.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SessionProtos.proto; sourceTree = "<group>"; };
|
||||
FD859EF027BF207C00510D0C /* WebSocketResources.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = WebSocketResources.proto; sourceTree = "<group>"; };
|
||||
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
|
||||
|
@ -2410,6 +2405,7 @@
|
|||
B8D84E9325DF72AF005A043E /* ConversationViewAction.h */,
|
||||
34D1F06F1F8678AA0066283D /* ConversationViewItem.h */,
|
||||
34D1F0701F8678AA0066283D /* ConversationViewItem.m */,
|
||||
FD83B9D327D5A7D5005E1583 /* ConversationViewItem+Refactor.swift */,
|
||||
341341ED2187467900192D59 /* ConversationViewModel.h */,
|
||||
341341EE2187467900192D59 /* ConversationViewModel.m */,
|
||||
34ABC0E321DD20C500ED9469 /* ConversationMessageMapping.swift */,
|
||||
|
@ -3378,7 +3374,6 @@
|
|||
FDC4381827B34EAD00C60D73 /* Models */,
|
||||
FDC4380727B31D3A00C60D73 /* Types */,
|
||||
B88FA7B726045D100049422F /* OpenGroupAPI.swift */,
|
||||
C3DB66CB260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift */,
|
||||
C3DB66AB260ACA42001EFC55 /* OpenGroupManager.swift */,
|
||||
);
|
||||
path = "Open Groups";
|
||||
|
@ -3992,6 +3987,7 @@
|
|||
FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */,
|
||||
FD859EF927C2F5C500510D0C /* TestGenericHash.swift */,
|
||||
FD859EFB27C2F60700510D0C /* TestEd25519.swift */,
|
||||
FD83B9D127D59495005E1583 /* TestUserDefaults.swift */,
|
||||
);
|
||||
path = _TestUtilities;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4376,7 +4372,6 @@
|
|||
);
|
||||
dependencies = (
|
||||
FDC4389427B9FFC700C60D73 /* PBXTargetDependency */,
|
||||
FDC438BB27BB276F00C60D73 /* PBXTargetDependency */,
|
||||
);
|
||||
name = SessionMessagingKitTests;
|
||||
productName = SessionMessagingKitTests;
|
||||
|
@ -5327,7 +5322,6 @@
|
|||
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */,
|
||||
FDC4380927B31D4E00C60D73 /* SOGSError.swift in Sources */,
|
||||
FDC4382027B36ADC00C60D73 /* SOGSEndpoint.swift in Sources */,
|
||||
C3DB66CC260AF1F3001EFC55 /* OpenGroupAPI+ObjC.swift in Sources */,
|
||||
C32C5BEF256DC8EE003C73A2 /* OWSDisappearingMessagesJob.m in Sources */,
|
||||
C34A977425A3E34A00852C71 /* ClosedGroupControlMessage.swift in Sources */,
|
||||
FDC4384C27B47F7700C60D73 /* OpenGroup.swift in Sources */,
|
||||
|
@ -5547,6 +5541,7 @@
|
|||
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
|
||||
B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */,
|
||||
340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */,
|
||||
FD83B9D427D5A7D5005E1583 /* ConversationViewItem+Refactor.swift in Sources */,
|
||||
B8D0A25025E3678700C1835E /* LinkDeviceVC.swift in Sources */,
|
||||
3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */,
|
||||
B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */,
|
||||
|
@ -5594,6 +5589,7 @@
|
|||
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
|
||||
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */,
|
||||
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
|
||||
FD83B9D227D59495005E1583 /* TestUserDefaults.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -5703,11 +5699,6 @@
|
|||
target = C3C2A678255388CC00C340D1 /* SessionUtilitiesKit */;
|
||||
targetProxy = FDC438A027BA2B8A00C60D73 /* PBXContainerItemProxy */;
|
||||
};
|
||||
FDC438BB27BB276F00C60D73 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D221A088169C9E5E00537ABF /* Session */;
|
||||
targetProxy = FDC438BA27BB276F00C60D73 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
|
126
Session/Conversations/ConversationViewItem+Refactor.swift
Normal file
126
Session/Conversations/ConversationViewItem+Refactor.swift
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionSnodeKit
|
||||
import SessionMessagingKit
|
||||
|
||||
extension ConversationViewItem {
|
||||
func deleteLocallyAction() {
|
||||
guard let message: TSMessage = self.interaction as? TSMessage else { return }
|
||||
|
||||
Storage.write { transaction in
|
||||
MessageInvalidator.invalidate(message, with: transaction)
|
||||
message.remove(with: transaction)
|
||||
|
||||
if message.interactionType() == .outgoingMessage {
|
||||
Storage.shared.cancelPendingMessageSendJobIfNeeded(for: message.timestamp, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteRemotelyAction() {
|
||||
guard let message: TSMessage = self.interaction as? TSMessage else { return }
|
||||
|
||||
if isGroupThread {
|
||||
guard let groupThread: TSGroupThread = message.thread as? TSGroupThread else { return }
|
||||
|
||||
// Only allow deletion on incoming and outgoing messages
|
||||
guard message.interactionType() == .incomingMessage || message.interactionType() == .outgoingMessage else {
|
||||
return
|
||||
}
|
||||
|
||||
if groupThread.isOpenGroup {
|
||||
// Make sure it's an open group message and get the open group
|
||||
guard message.isOpenGroupMessage, let uniqueId: String = groupThread.uniqueId, let openGroup: OpenGroup = Storage.shared.getOpenGroup(for: uniqueId) else {
|
||||
return
|
||||
}
|
||||
|
||||
// If it's an incoming message the user must have moderator status
|
||||
if message.interactionType() == .incomingMessage {
|
||||
guard let userPublicKey: String = Storage.shared.getUserPublicKey() else { return }
|
||||
|
||||
if !OpenGroupManager.isUserModeratorOrAdmin(userPublicKey, for: openGroup.room, on: openGroup.server) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the message
|
||||
OpenGroupAPI.messageDelete(message.openGroupServerMessageID, in: openGroup.room, on: openGroup.server)
|
||||
.catch { _ in
|
||||
// Roll back
|
||||
message.save()
|
||||
}
|
||||
.retainUntilComplete()
|
||||
}
|
||||
else {
|
||||
guard let serverHash: String = message.serverHash else { return }
|
||||
|
||||
let groupPublicKey: String = LKGroupUtilities.getDecodedGroupID(groupThread.groupModel.groupId)
|
||||
|
||||
SnodeAPI.deleteMessage(publicKey: groupPublicKey, serverHashes: [serverHash])
|
||||
.catch { _ in
|
||||
// Roll back
|
||||
message.save()
|
||||
}
|
||||
.retainUntilComplete()
|
||||
}
|
||||
}
|
||||
else {
|
||||
guard let contactThread: TSContactThread = message.thread as? TSContactThread, let serverHash: String = message.serverHash else {
|
||||
return
|
||||
}
|
||||
|
||||
SnodeAPI.deleteMessage(publicKey: contactThread.contactSessionID(), serverHashes: [serverHash])
|
||||
.catch { _ in
|
||||
// Roll back
|
||||
message.save()
|
||||
}
|
||||
.retainUntilComplete()
|
||||
}
|
||||
}
|
||||
|
||||
// Remove this after the unsend request is enabled
|
||||
func deleteAction() {
|
||||
Storage.write { transaction in
|
||||
self.interaction.remove(with: transaction)
|
||||
|
||||
if self.interaction.interactionType() == .outgoingMessage {
|
||||
Storage.shared.cancelPendingMessageSendJobIfNeeded(for: self.interaction.timestamp, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if self.isGroupThread {
|
||||
guard let message: TSMessage = self.interaction as? TSMessage, let groupThread: TSGroupThread = message.thread as? TSGroupThread else {
|
||||
return
|
||||
}
|
||||
|
||||
// Only allow deletion on incoming and outgoing messages
|
||||
guard message.interactionType() == .incomingMessage || message.interactionType() == .outgoingMessage else {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure it's an open group message and get the open group
|
||||
guard message.isOpenGroupMessage, let uniqueId: String = groupThread.uniqueId, let openGroup: OpenGroup = Storage.shared.getOpenGroup(for: uniqueId) else {
|
||||
return
|
||||
}
|
||||
|
||||
// If it's an incoming message the user must have moderator status
|
||||
if message.interactionType() == .incomingMessage {
|
||||
guard let userPublicKey: String = Storage.shared.getUserPublicKey() else { return }
|
||||
|
||||
if !OpenGroupManager.isUserModeratorOrAdmin(userPublicKey, for: openGroup.room, on: openGroup.server) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the message
|
||||
OpenGroupAPI.messageDelete(message.openGroupServerMessageID, in: openGroup.room, on: openGroup.server)
|
||||
.catch { _ in
|
||||
// Roll back
|
||||
message.save()
|
||||
}
|
||||
.retainUntilComplete()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -133,10 +133,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
|||
- (void)copyTextAction;
|
||||
- (void)shareMediaAction;
|
||||
- (void)saveMediaAction;
|
||||
- (void)deleteLocallyAction;
|
||||
- (void)deleteRemotelyAction;
|
||||
|
||||
- (void)deleteAction; // Remove this after the unsend request is enabled
|
||||
|
||||
- (BOOL)canCopyMedia;
|
||||
- (BOOL)canSaveMedia;
|
||||
|
|
|
@ -972,109 +972,6 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|||
return [self saveMediaAlbumItems:mediaAlbumItems];
|
||||
}
|
||||
|
||||
- (void)deleteLocallyAction
|
||||
{
|
||||
TSMessage *message = (TSMessage *)self.interaction;
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[MessageInvalidator invalidate:message with:transaction];
|
||||
[self.interaction removeWithTransaction:transaction];
|
||||
if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
|
||||
[LKStorage.shared cancelPendingMessageSendJobIfNeededForMessage:self.interaction.timestamp using:transaction];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteRemotelyAction
|
||||
{
|
||||
TSMessage *message = (TSMessage *)self.interaction;
|
||||
|
||||
if (self.isGroupThread) {
|
||||
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
||||
|
||||
// Only allow deletion on incoming and outgoing messages
|
||||
OWSInteractionType interationType = self.interaction.interactionType;
|
||||
if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return;
|
||||
|
||||
if (groupThread.isOpenGroup) {
|
||||
// Make sure it's an open group message
|
||||
if (!message.isOpenGroupMessage) return;
|
||||
|
||||
// Get the open group
|
||||
SNOpenGroupV2 *openGroup = [LKStorage.shared getOpenGroupForThreadID:groupThread.uniqueId];
|
||||
if (openGroup == nil) return;
|
||||
|
||||
// If it's an incoming message the user must have moderator status
|
||||
if (self.interaction.interactionType == OWSInteractionType_IncomingMessage) {
|
||||
NSString *userPublicKey = [LKStorage.shared getUserPublicKey];
|
||||
if (![SNOpenGroupManager isUserModeratorOrAdmin:userPublicKey forRoom:openGroup.room onServer:openGroup.server]) { return; }
|
||||
}
|
||||
|
||||
// Delete the message
|
||||
[[SNOpenGroupAPI deleteMessageWithServerID:message.openGroupServerMessageID fromRoom:openGroup.room onServer:openGroup.server].catch(^(NSError *error) {
|
||||
// Roll back
|
||||
[self.interaction save];
|
||||
}) retainUntilComplete];
|
||||
} else {
|
||||
NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:groupThread.groupModel.groupId];
|
||||
[[SNSnodeAPI deleteMessageForPublickKey:groupPublicKey serverHashes:@[message.serverHash]].catch(^(NSError *error) {
|
||||
// Roll back
|
||||
[self.interaction save];
|
||||
}) retainUntilComplete];
|
||||
}
|
||||
} else {
|
||||
TSContactThread *contactThread = (TSContactThread *)self.interaction.thread;
|
||||
[[SNSnodeAPI deleteMessageForPublickKey:contactThread.contactSessionID serverHashes:@[message.serverHash]].catch(^(NSError *error) {
|
||||
// Roll back
|
||||
[self.interaction save];
|
||||
}) retainUntilComplete];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remove this after the unsend request is enabled
|
||||
- (void)deleteAction
|
||||
{
|
||||
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[self.interaction removeWithTransaction:transaction];
|
||||
if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
|
||||
[LKStorage.shared cancelPendingMessageSendJobIfNeededForMessage:self.interaction.timestamp using:transaction];
|
||||
}
|
||||
}];
|
||||
|
||||
if (self.isGroupThread) {
|
||||
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
||||
|
||||
// Only allow deletion on incoming and outgoing messages
|
||||
OWSInteractionType interationType = self.interaction.interactionType;
|
||||
if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return;
|
||||
|
||||
// Make sure it's an open group message
|
||||
TSMessage *message = (TSMessage *)self.interaction;
|
||||
if (!message.isOpenGroupMessage) return;
|
||||
|
||||
// Get the open group
|
||||
SNOpenGroupV2 *openGroup = [LKStorage.shared getOpenGroupForThreadID:groupThread.uniqueId];
|
||||
if (openGroup == nil && openGroup == nil) return;
|
||||
|
||||
// If it's an incoming message the user must have moderator status
|
||||
if (self.interaction.interactionType == OWSInteractionType_IncomingMessage) {
|
||||
NSString *userPublicKey = [LKStorage.shared getUserPublicKey];
|
||||
if (openGroup != nil) {
|
||||
if (![SNOpenGroupManager isUserModeratorOrAdmin:userPublicKey forRoom:openGroup.room onServer:openGroup.server]) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the message
|
||||
BOOL wasSentByUser = (interationType == OWSInteractionType_OutgoingMessage);
|
||||
if (openGroup != nil) {
|
||||
[[SNOpenGroupAPI deleteMessageWithServerID:message.openGroupServerMessageID fromRoom:openGroup.room onServer:openGroup.server].catch(^(NSError *error) {
|
||||
// Roll back
|
||||
[self.interaction save];
|
||||
}) retainUntilComplete];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasBodyTextActionContent
|
||||
{
|
||||
return self.hasBodyText && self.displayableBodyText.fullText.length > 0;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import PromiseKit
|
||||
import NVActivityIndicatorView
|
||||
import SessionMessagingKit
|
||||
|
||||
final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||
private let maxWidth: CGFloat
|
||||
|
@ -64,7 +65,7 @@ final class OpenGroupSuggestionGrid : UIView, UICollectionViewDataSource, UIColl
|
|||
widthAnchor.constraint(greaterThanOrEqualToConstant: OpenGroupSuggestionGrid.cellHeight).isActive = true
|
||||
|
||||
OpenGroupManager.getDefaultRoomsIfNeeded()
|
||||
_ = OpenGroupManager.defaultRoomsPromise?.done { [weak self] rooms in
|
||||
_ = OpenGroupManager.shared.cache.defaultRoomsPromise?.done { [weak self] rooms in
|
||||
self?.rooms = rooms
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPI {
|
||||
public struct Capabilities: Codable {
|
||||
public struct Capabilities: Codable, Equatable {
|
||||
public enum Capability: Equatable, CaseIterable, Codable {
|
||||
public static var allCases: [Capability] {
|
||||
[.sogs, .blind]
|
||||
|
@ -54,4 +54,10 @@ extension OpenGroupAPI.Capabilities.Capability {
|
|||
|
||||
self = OpenGroupAPI.Capabilities.Capability(from: valueString)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container: SingleValueEncodingContainer = encoder.singleValueContainer()
|
||||
|
||||
try container.encode(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPI {
|
||||
public struct PinnedMessage: Codable {
|
||||
public struct PinnedMessage: Codable, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case pinnedAt = "pinned_at"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import Foundation
|
||||
|
||||
extension OpenGroupAPI {
|
||||
public struct Room: Codable {
|
||||
public struct Room: Codable, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case token
|
||||
case name
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import PromiseKit
|
||||
|
||||
extension OpenGroupAPI {
|
||||
@objc(deleteMessageWithServerID:fromRoom:onServer:)
|
||||
public static func objc_deleteMessage(with serverID: Int64, from room: String, on server: String) -> AnyPromise {
|
||||
return AnyPromise.from(messageDelete(serverID, in: room, on: server))
|
||||
}
|
||||
}
|
|
@ -3,34 +3,14 @@ import SessionSnodeKit
|
|||
import Sodium
|
||||
import Curve25519Kit
|
||||
|
||||
@objc(SNOpenGroupAPI)
|
||||
public final class OpenGroupAPI: NSObject {
|
||||
|
||||
public enum OpenGroupAPI {
|
||||
// MARK: - Settings
|
||||
|
||||
public static let defaultServer = "http://116.203.70.33"
|
||||
public static let defaultServerPublicKey = "a03c383cf63c3c4efe67acc52112a6dd734b3a946b9545f488aaa93da7991238"
|
||||
|
||||
public static let workQueue = DispatchQueue(label: "OpenGroupAPI.workQueue", qos: .userInitiated) // It's important that this is a serial queue
|
||||
|
||||
// MARK: - Polling State
|
||||
|
||||
private static var hasPerformedInitialPoll: Atomic<[String: Bool]> = Atomic([:])
|
||||
private static var timeSinceLastPoll: Atomic<[String: TimeInterval]> = Atomic([:])
|
||||
private static var lastPollTime: Atomic<TimeInterval> = Atomic(.greatestFiniteMagnitude)
|
||||
|
||||
private static let timeSinceLastOpen: Atomic<TimeInterval> = {
|
||||
guard let lastOpen = UserDefaults.standard[.lastOpen] else { return Atomic(.greatestFiniteMagnitude) }
|
||||
|
||||
return Atomic(Date().timeIntervalSince(lastOpen))
|
||||
}()
|
||||
|
||||
|
||||
// TODO: Remove these
|
||||
private static var legacyAuthTokenPromises: Atomic<[String: Promise<String>]> = Atomic([:])
|
||||
private static var legacyHasUpdatedLastOpenDate = false
|
||||
private static var legacyGroupImagePromises: [String: Promise<Data>] = [:]
|
||||
|
||||
|
||||
// MARK: - Batching & Polling
|
||||
|
||||
|
@ -42,20 +22,17 @@ public final class OpenGroupAPI: NSObject {
|
|||
/// - Messages (includes additions and deletions)
|
||||
/// - Inbox for the server
|
||||
/// - Outbox for the server
|
||||
public static func poll(_ server: String, using dependencies: Dependencies = Dependencies()) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> {
|
||||
// Store a local copy of the cached state for this server
|
||||
let hadPerformedInitialPoll: Bool = (hasPerformedInitialPoll.wrappedValue[server] == true)
|
||||
let originalTimeSinceLastPoll: TimeInterval = (timeSinceLastPoll.wrappedValue[server] ?? min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue))
|
||||
public static func poll(
|
||||
_ server: String,
|
||||
hasPerformedInitialPoll: Bool,
|
||||
timeSinceLastPoll: TimeInterval,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> {
|
||||
let maybeLastInboxMessageId: Int64? = dependencies.storage.getOpenGroupInboxLatestMessageId(for: server)
|
||||
let maybeLastOutboxMessageId: Int64? = dependencies.storage.getOpenGroupOutboxLatestMessageId(for: server)
|
||||
let lastInboxMessageId: Int64 = (maybeLastInboxMessageId ?? 0)
|
||||
let lastOutboxMessageId: Int64 = (maybeLastOutboxMessageId ?? 0)
|
||||
|
||||
// Update the cached state for this server
|
||||
hasPerformedInitialPoll.mutate { $0[server] = true }
|
||||
lastPollTime.mutate { $0 = min($0, timeSinceLastOpen.wrappedValue)}
|
||||
UserDefaults.standard[.lastOpen] = Date()
|
||||
|
||||
// Generate the requests
|
||||
let requestResponseType: [BatchRequestInfoType] = [
|
||||
BatchRequestInfo(
|
||||
|
@ -78,8 +55,8 @@ public final class OpenGroupAPI: NSObject {
|
|||
// If it's the first poll for this launch and it's been longer than
|
||||
// 'maxInactivityPeriod' then just retrieve recent messages instead
|
||||
// of trying to get all messages since the last one retrieved
|
||||
!hadPerformedInitialPoll &&
|
||||
originalTimeSinceLastPoll > OpenGroupAPI.Poller.maxInactivityPeriod
|
||||
!hasPerformedInitialPoll &&
|
||||
timeSinceLastPoll > OpenGroupAPI.Poller.maxInactivityPeriod
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -180,7 +157,6 @@ public final class OpenGroupAPI: NSObject {
|
|||
body: requestBody
|
||||
)
|
||||
|
||||
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
||||
return send(request, using: dependencies)
|
||||
.decoded(as: responseTypes, on: OpenGroupAPI.workQueue, using: dependencies)
|
||||
.map { result in
|
||||
|
@ -203,11 +179,9 @@ public final class OpenGroupAPI: NSObject {
|
|||
public static func capabilities(on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Capabilities)> {
|
||||
let request: Request = Request<NoBody, Endpoint>(
|
||||
server: server,
|
||||
endpoint: .capabilities,
|
||||
queryParameters: [:] // TODO: Add any requirements '.required'.
|
||||
endpoint: .capabilities
|
||||
)
|
||||
|
||||
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
||||
return send(request, using: dependencies)
|
||||
.decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, using: dependencies)
|
||||
}
|
||||
|
@ -290,31 +264,21 @@ public final class OpenGroupAPI: NSObject {
|
|||
]
|
||||
|
||||
return sequence(server, requests: requestResponseType, using: dependencies)
|
||||
.map { response -> (capabilities: (OnionRequestResponseInfoType, Capabilities?), room: (OnionRequestResponseInfoType, Room?)) in
|
||||
var capabilities: (OnionRequestResponseInfoType, Capabilities?)? = nil
|
||||
var room: (OnionRequestResponseInfoType, Room?)? = nil
|
||||
.map { (response: [Endpoint: (OnionRequestResponseInfoType, Codable?)]) -> (capabilities: (OnionRequestResponseInfoType, Capabilities?), room: (OnionRequestResponseInfoType, Room?)) in
|
||||
let maybeCapabilities: (OnionRequestResponseInfoType, Capabilities?)? = response[.capabilities]
|
||||
.map { info, data in (info, (data as? BatchSubResponse<Capabilities>)?.body) }
|
||||
let maybeRoomResponse: (OnionRequestResponseInfoType, Codable?)? = response
|
||||
.first(where: { key, _ in
|
||||
switch key {
|
||||
case .room: return true
|
||||
default: return false
|
||||
}
|
||||
})
|
||||
.map { _, value in value }
|
||||
let maybeRoom: (OnionRequestResponseInfoType, Room?)? = maybeRoomResponse
|
||||
.map { info, data in (info, (data as? BatchSubResponse<Room>)?.body) }
|
||||
|
||||
try response.forEach { (endpoint: Endpoint, endpointResponse: (info: OnionRequestResponseInfoType, data: Codable?)) in
|
||||
switch endpoint {
|
||||
case .capabilities:
|
||||
guard let responseData: BatchSubResponse<Capabilities> = endpointResponse.data as? BatchSubResponse<Capabilities>, let responseBody: Capabilities = responseData.body else {
|
||||
throw HTTP.Error.parsingFailed
|
||||
}
|
||||
|
||||
capabilities = (endpointResponse.info, responseBody)
|
||||
|
||||
case .room:
|
||||
guard let responseData: OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Room> = endpointResponse.data as? OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Room>, let responseBody: OpenGroupAPI.Room = responseData.body else {
|
||||
throw HTTP.Error.parsingFailed
|
||||
}
|
||||
|
||||
room = (endpointResponse.info, responseBody)
|
||||
|
||||
default: break // No custom handling needed
|
||||
}
|
||||
}
|
||||
|
||||
guard let capabilities: (OnionRequestResponseInfoType, Capabilities?) = capabilities, let room: (OnionRequestResponseInfoType, Room?) = room else {
|
||||
guard let capabilities: (OnionRequestResponseInfoType, Capabilities?) = maybeCapabilities, let room: (OnionRequestResponseInfoType, Room?) = maybeRoom else {
|
||||
throw HTTP.Error.parsingFailed
|
||||
}
|
||||
|
||||
|
@ -367,7 +331,7 @@ public final class OpenGroupAPI: NSObject {
|
|||
}
|
||||
|
||||
/// Returns a single message by ID
|
||||
public static func message(_ id: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
||||
public static func message(_ id: UInt64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
||||
let request: Request = Request<NoBody, Endpoint>(
|
||||
server: server,
|
||||
endpoint: .roomMessageIndividual(roomToken, id: id)
|
||||
|
@ -381,7 +345,7 @@ public final class OpenGroupAPI: NSObject {
|
|||
///
|
||||
/// **Note:** This edit may only be initiated by the creator of the post, and the poster must currently have write permissions in the room
|
||||
public static func messageUpdate(
|
||||
_ id: Int64,
|
||||
_ id: UInt64,
|
||||
plaintext: Data,
|
||||
fileIds: [Int64]?,
|
||||
in roomToken: String,
|
||||
|
@ -405,12 +369,11 @@ public final class OpenGroupAPI: NSObject {
|
|||
body: requestBody
|
||||
)
|
||||
|
||||
// TODO: Handle custom response info?
|
||||
return send(request, using: dependencies)
|
||||
}
|
||||
|
||||
public static func messageDelete(
|
||||
_ id: Int64,
|
||||
_ id: UInt64,
|
||||
in roomToken: String,
|
||||
on server: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
|
@ -432,8 +395,6 @@ public final class OpenGroupAPI: NSObject {
|
|||
let request: Request = Request<NoBody, Endpoint>(
|
||||
server: server,
|
||||
endpoint: .roomMessagesRecent(roomToken)
|
||||
// TODO: Limit?.
|
||||
// queryParameters: [ .limit: 50 ]
|
||||
)
|
||||
|
||||
return send(request, using: dependencies)
|
||||
|
@ -444,13 +405,10 @@ public final class OpenGroupAPI: NSObject {
|
|||
/// remove the `@available` line and make sure to route the response of this method to the `OpenGroupManager.handleMessages`
|
||||
/// method to ensure things are processed correctly
|
||||
@available(*, unavailable, message: "Avoid using this directly, use the pre-built `poll()` method instead")
|
||||
public static func messagesBefore(messageId: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> {
|
||||
// TODO: Do we need to be able to load old messages?
|
||||
public static func messagesBefore(messageId: UInt64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [Message])> {
|
||||
let request: Request = Request<NoBody, Endpoint>(
|
||||
server: server,
|
||||
endpoint: .roomMessagesBefore(roomToken, id: messageId)
|
||||
// TODO: Limit?.
|
||||
// queryParameters: [ .limit: 50 ]
|
||||
)
|
||||
|
||||
return send(request, using: dependencies)
|
||||
|
@ -465,8 +423,6 @@ public final class OpenGroupAPI: NSObject {
|
|||
let request: Request = Request<NoBody, Endpoint>(
|
||||
server: server,
|
||||
endpoint: .roomMessagesSince(roomToken, seqNo: seqNo)
|
||||
// TODO: Limit?.
|
||||
// queryParameters: [ .limit: 50 ]
|
||||
)
|
||||
|
||||
return send(request, using: dependencies)
|
||||
|
@ -485,7 +441,7 @@ public final class OpenGroupAPI: NSObject {
|
|||
/// Pinned messages that are already pinned will be re-pinned (that is, their pin timestamp and pinning admin user will be updated) - because pinned
|
||||
/// messages are returned in pinning-order this allows admins to order multiple pinned messages in a room by re-pinning (via this endpoint) in the
|
||||
/// order in which pinned messages should be displayed
|
||||
public static func pinMessage(id: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<OnionRequestResponseInfoType> {
|
||||
public static func pinMessage(id: UInt64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<OnionRequestResponseInfoType> {
|
||||
let request: Request = Request<NoBody, Endpoint>(
|
||||
method: .post,
|
||||
server: server,
|
||||
|
@ -499,7 +455,7 @@ public final class OpenGroupAPI: NSObject {
|
|||
/// Remove a message from this room's pinned message list
|
||||
///
|
||||
/// The user must have `admin` (not just `moderator`) permissions in the room
|
||||
public static func unpinMessage(id: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<OnionRequestResponseInfoType> {
|
||||
public static func unpinMessage(id: UInt64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<OnionRequestResponseInfoType> {
|
||||
let request: Request = Request<NoBody, Endpoint>(
|
||||
method: .post,
|
||||
server: server,
|
||||
|
|
|
@ -5,22 +5,44 @@ import SessionSnodeKit
|
|||
|
||||
@objc(SNOpenGroupManager)
|
||||
public final class OpenGroupManager: NSObject {
|
||||
@objc public static let shared = OpenGroupManager()
|
||||
public class Cache {
|
||||
public var defaultRoomsPromise: Promise<[OpenGroupAPI.Room]>?
|
||||
fileprivate var groupImagePromises: [String: Promise<Data>] = [:]
|
||||
|
||||
/// Server URL to room ID to set of user IDs
|
||||
fileprivate var moderators: [String: [String: Set<String>]] = [:]
|
||||
fileprivate var admins: [String: [String: Set<String>]] = [:]
|
||||
|
||||
/// Server URL to value
|
||||
public var hasPerformedInitialPoll: [String: Bool] = [:]
|
||||
public var timeSinceLastPoll: [String: TimeInterval] = [:]
|
||||
|
||||
fileprivate var _timeSinceLastOpen: TimeInterval?
|
||||
public func getTimeSinceLastOpen(using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> TimeInterval {
|
||||
if let storedTimeSinceLastOpen: TimeInterval = _timeSinceLastOpen {
|
||||
return storedTimeSinceLastOpen
|
||||
}
|
||||
|
||||
guard let lastOpen: Date = dependencies.standardUserDefaults[.lastOpen] else {
|
||||
_timeSinceLastOpen = .greatestFiniteMagnitude
|
||||
return .greatestFiniteMagnitude
|
||||
}
|
||||
|
||||
_timeSinceLastOpen = dependencies.date.timeIntervalSince(lastOpen)
|
||||
return dependencies.date.timeIntervalSince(lastOpen)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Variables
|
||||
|
||||
@objc public static let shared: OpenGroupManager = OpenGroupManager()
|
||||
|
||||
public let mutableCache: Atomic<Cache> = Atomic(Cache())
|
||||
public var cache: Cache { return mutableCache.wrappedValue }
|
||||
|
||||
private var pollers: [String: OpenGroupAPI.Poller] = [:] // One for each server
|
||||
private var isPolling = false
|
||||
|
||||
// MARK: - Cache
|
||||
|
||||
public static var defaultRoomsPromise: Promise<[OpenGroupAPI.Room]>?
|
||||
private static var groupImagePromises: [String: Promise<Data>] = [:]
|
||||
|
||||
/// Server URL to room ID to set of moderator IDs
|
||||
private static var moderators: Atomic<[String: [String: Set<String>]]> = Atomic([:])
|
||||
|
||||
/// Server URL to room ID to set of admin IDs
|
||||
private static var admins: Atomic<[String: [String: Set<String>]]> = Atomic([:])
|
||||
|
||||
// MARK: - Polling
|
||||
|
||||
@objc public func startPolling() {
|
||||
|
@ -247,15 +269,15 @@ public final class OpenGroupManager: NSObject {
|
|||
|
||||
// - Moderators
|
||||
if let moderators: [String] = (pollInfo.details?.moderators ?? maybeUpdatedModel?.groupModeratorIds) {
|
||||
OpenGroupManager.moderators.mutate {
|
||||
$0[server] = ($0[server] ?? [:]).setting(roomToken, Set(moderators))
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.moderators[server] = (cache.moderators[server] ?? [:]).setting(roomToken, Set(moderators))
|
||||
}
|
||||
}
|
||||
|
||||
// - Admins
|
||||
if let admins: [String] = (pollInfo.details?.admins ?? maybeUpdatedModel?.groupAdminIds) {
|
||||
OpenGroupManager.admins.mutate {
|
||||
$0[server] = ($0[server] ?? [:]).setting(roomToken, Set(admins))
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.admins[server] = (cache.admins[server] ?? [:]).setting(roomToken, Set(admins))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,8 +480,8 @@ public final class OpenGroupManager: NSObject {
|
|||
}
|
||||
|
||||
public static func isUserModeratorOrAdmin(_ publicKey: String, for room: String, on server: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Bool {
|
||||
let modAndAdminKeys: Set<String> = (OpenGroupManager.moderators.wrappedValue[server]?[room] ?? Set())
|
||||
.union(OpenGroupManager.admins.wrappedValue[server]?[room] ?? Set())
|
||||
let modAndAdminKeys: Set<String> = (OpenGroupManager.shared.cache.moderators[server]?[room] ?? Set())
|
||||
.union(OpenGroupManager.shared.cache.admins[server]?[room] ?? Set())
|
||||
|
||||
// If the publicKey is in the set then return immediately, otherwise only continue if it's the
|
||||
// current user
|
||||
|
@ -507,7 +529,7 @@ public final class OpenGroupManager: NSObject {
|
|||
|
||||
public static func getDefaultRoomsIfNeeded(using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) {
|
||||
// Note: If we already have a 'defaultRoomsPromise' then there is no need to get it again
|
||||
guard OpenGroupManager.defaultRoomsPromise == nil else { return }
|
||||
guard OpenGroupManager.shared.cache.defaultRoomsPromise == nil else { return }
|
||||
|
||||
dependencies.storage.write(
|
||||
with: { transaction in
|
||||
|
@ -518,11 +540,13 @@ public final class OpenGroupManager: NSObject {
|
|||
)
|
||||
},
|
||||
completion: {
|
||||
OpenGroupManager.defaultRoomsPromise = attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) {
|
||||
OpenGroupAPI.rooms(for: OpenGroupAPI.defaultServer, using: dependencies)
|
||||
.map { _, data in data }
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.defaultRoomsPromise = attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) {
|
||||
OpenGroupAPI.rooms(for: OpenGroupAPI.defaultServer, using: dependencies)
|
||||
.map { _, data in data }
|
||||
}
|
||||
}
|
||||
OpenGroupManager.defaultRoomsPromise?
|
||||
OpenGroupManager.shared.cache.defaultRoomsPromise?
|
||||
.done(on: OpenGroupAPI.workQueue) { items in
|
||||
items
|
||||
.compactMap { room -> (UInt64, String)? in
|
||||
|
@ -536,7 +560,9 @@ public final class OpenGroupManager: NSObject {
|
|||
}
|
||||
}
|
||||
.catch(on: OpenGroupAPI.workQueue) { _ in
|
||||
OpenGroupManager.defaultRoomsPromise = nil
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.defaultRoomsPromise = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -566,7 +592,7 @@ public final class OpenGroupManager: NSObject {
|
|||
return Promise.value(data)
|
||||
}
|
||||
|
||||
if let promise = OpenGroupManager.groupImagePromises["\(server).\(roomToken)"] {
|
||||
if let promise = OpenGroupManager.shared.cache.groupImagePromises["\(server).\(roomToken)"] {
|
||||
return promise
|
||||
}
|
||||
|
||||
|
@ -581,7 +607,9 @@ public final class OpenGroupManager: NSObject {
|
|||
UserDefaults.standard[.lastOpenGroupImageUpdate] = now
|
||||
}
|
||||
}
|
||||
OpenGroupManager.groupImagePromises["\(server).\(roomToken)"] = promise
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.groupImagePromises["\(server).\(roomToken)"] = promise
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import Foundation
|
||||
import Sodium
|
||||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
// MARK: - Dependencies
|
||||
|
||||
|
@ -62,6 +63,12 @@ extension OpenGroupAPI {
|
|||
set { _nonceGenerator24 = newValue }
|
||||
}
|
||||
|
||||
private var _standardUserDefaults: UserDefaultsType?
|
||||
public var standardUserDefaults: UserDefaultsType {
|
||||
get { getValueSettingIfNull(&_standardUserDefaults) { UserDefaults.standard } }
|
||||
set { _standardUserDefaults = newValue }
|
||||
}
|
||||
|
||||
private var _date: Date?
|
||||
public var date: Date {
|
||||
get { getValueSettingIfNull(&_date) { Date() } }
|
||||
|
@ -80,6 +87,7 @@ extension OpenGroupAPI {
|
|||
ed25519: Ed25519Type.Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
standardUserDefaults: UserDefaultsType? = nil,
|
||||
date: Date? = nil
|
||||
) {
|
||||
_api = api
|
||||
|
@ -91,6 +99,7 @@ extension OpenGroupAPI {
|
|||
_ed25519 = ed25519
|
||||
_nonceGenerator16 = nonceGenerator16
|
||||
_nonceGenerator24 = nonceGenerator24
|
||||
_standardUserDefaults = standardUserDefaults
|
||||
_date = date
|
||||
}
|
||||
|
||||
|
@ -106,6 +115,7 @@ extension OpenGroupAPI {
|
|||
ed25519: Ed25519Type.Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
standardUserDefaults: UserDefaultsType? = nil,
|
||||
date: Date? = nil
|
||||
) -> Dependencies {
|
||||
return Dependencies(
|
||||
|
@ -118,6 +128,7 @@ extension OpenGroupAPI {
|
|||
ed25519: (ed25519 ?? self._ed25519),
|
||||
nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
|
||||
nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
|
||||
standardUserDefaults: (standardUserDefaults ?? self._standardUserDefaults),
|
||||
date: (date ?? self._date)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,15 +20,15 @@ extension OpenGroupAPI {
|
|||
// Messages
|
||||
|
||||
case roomMessage(String)
|
||||
case roomMessageIndividual(String, id: Int64)
|
||||
case roomMessageIndividual(String, id: UInt64)
|
||||
case roomMessagesRecent(String)
|
||||
case roomMessagesBefore(String, id: Int64)
|
||||
case roomMessagesBefore(String, id: UInt64)
|
||||
case roomMessagesSince(String, seqNo: Int64)
|
||||
|
||||
// Pinning
|
||||
|
||||
case roomPinMessage(String, id: Int64)
|
||||
case roomUnpinMessage(String, id: Int64)
|
||||
case roomPinMessage(String, id: UInt64)
|
||||
case roomUnpinMessage(String, id: UInt64)
|
||||
case roomUnpinAll(String)
|
||||
|
||||
// Files
|
||||
|
|
|
@ -240,14 +240,6 @@ extension MessageReceiver {
|
|||
thread.remove(with: transaction)
|
||||
}
|
||||
}
|
||||
else if SessionId.Prefix(from: sessionID) != .blinded {
|
||||
// Otherwise create and save the thread (if the contact isn't a blinded contact - we don't want to
|
||||
// auto-create threads for blinded contacts if they have no messages)
|
||||
// TODO: See what this will do with blinded->unblinded conversations?
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: sessionID, transaction: transaction)
|
||||
thread.shouldBeVisible = true
|
||||
thread.save(with: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: 'OWSBlockingManager' manages it's own dbConnection and transactions so we have to dispatch this to prevent deadlocks
|
||||
|
@ -891,7 +883,6 @@ extension MessageReceiver {
|
|||
// Note: Pending `MessageSendJobs` _shouldn't_ be an issue as even if they are sent after the
|
||||
// un-blinding of a thread, the logic when handling the sent messages should automatically
|
||||
// assign them to the correct thread
|
||||
// TODO: Validate the above note once `/outbox` has been implemented
|
||||
view.enumerateRows(inGroup: blindedThreadId) { _, _, object, _, _, _ in
|
||||
guard let interaction: TSInteraction = object as? TSInteraction else {
|
||||
return
|
||||
|
|
|
@ -52,13 +52,28 @@ extension OpenGroupAPI {
|
|||
guard !self.isPolling else { return Promise.value(()) }
|
||||
|
||||
self.isPolling = true
|
||||
let server: String = self.server
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
promise.retainUntilComplete()
|
||||
|
||||
OpenGroupAPI.poll(server)
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
server,
|
||||
hasPerformedInitialPoll: OpenGroupManager.shared.cache.hasPerformedInitialPoll[server] == true,
|
||||
timeSinceLastPoll: (
|
||||
OpenGroupManager.shared.cache.timeSinceLastPoll[server] ??
|
||||
OpenGroupManager.shared.cache.getTimeSinceLastOpen()
|
||||
)
|
||||
)
|
||||
.done(on: OpenGroupAPI.workQueue) { [weak self] response in
|
||||
self?.isPolling = false
|
||||
self?.handlePollResponse(response, isBackgroundPoll: isBackgroundPoll)
|
||||
|
||||
OpenGroupManager.shared.mutableCache.mutate { cache in
|
||||
cache.hasPerformedInitialPoll[server] = true
|
||||
cache.timeSinceLastPoll[server] = Date().timeIntervalSince1970
|
||||
UserDefaults.standard[.lastOpen] = Date()
|
||||
}
|
||||
seal.fulfill(())
|
||||
}
|
||||
.catch(on: OpenGroupAPI.workQueue) { [weak self] error in
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,27 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class TestUserDefaults: UserDefaultsType {
|
||||
var storage: [String: Any] = [:]
|
||||
|
||||
func object(forKey defaultName: String) -> Any? { return storage[defaultName] }
|
||||
func string(forKey defaultName: String) -> String? { return storage[defaultName] as? String }
|
||||
func array(forKey defaultName: String) -> [Any]? { return storage[defaultName] as? [Any] }
|
||||
func dictionary(forKey defaultName: String) -> [String: Any]? { return storage[defaultName] as? [String: Any] }
|
||||
func data(forKey defaultName: String) -> Data? { return storage[defaultName] as? Data }
|
||||
func stringArray(forKey defaultName: String) -> [String]? { return storage[defaultName] as? [String] }
|
||||
func integer(forKey defaultName: String) -> Int { return ((storage[defaultName] as? Int) ?? 0) }
|
||||
func float(forKey defaultName: String) -> Float { return ((storage[defaultName] as? Float) ?? 0) }
|
||||
func double(forKey defaultName: String) -> Double { return ((storage[defaultName] as? Double) ?? 0) }
|
||||
func bool(forKey defaultName: String) -> Bool { return ((storage[defaultName] as? Bool) ?? false) }
|
||||
func url(forKey defaultName: String) -> URL? { return storage[defaultName] as? URL }
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Int, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Float, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Double, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Bool, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ url: URL?, forKey defaultName: String) { storage[defaultName] = url }
|
||||
}
|
|
@ -308,9 +308,6 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
|
||||
/// Sends an onion request to `server`. Builds new paths as needed.
|
||||
public static func sendOnionRequest(_ request: URLRequest, to server: String, using version: Version = .v4, with x25519PublicKey: String) -> Promise<(OnionRequestResponseInfoType, Data?)> {
|
||||
guard version != .v4 || server == "https://chat.lokinet.dev" else { // TODO: Remove this
|
||||
return sendOnionRequest(request, to: server, using: .v3, with: x25519PublicKey)
|
||||
}
|
||||
guard let url = request.url, let host = request.url?.host else { return Promise(error: Error.invalidURL) }
|
||||
|
||||
let scheme: String? = url.scheme
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
import Foundation
|
||||
|
||||
public protocol UserDefaultsType: AnyObject {
|
||||
func object(forKey defaultName: String) -> Any?
|
||||
func string(forKey defaultName: String) -> String?
|
||||
func array(forKey defaultName: String) -> [Any]?
|
||||
func dictionary(forKey defaultName: String) -> [String : Any]?
|
||||
func data(forKey defaultName: String) -> Data?
|
||||
func stringArray(forKey defaultName: String) -> [String]?
|
||||
func integer(forKey defaultName: String) -> Int
|
||||
func float(forKey defaultName: String) -> Float
|
||||
func double(forKey defaultName: String) -> Double
|
||||
func bool(forKey defaultName: String) -> Bool
|
||||
func url(forKey defaultName: String) -> URL?
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String)
|
||||
func set(_ value: Int, forKey defaultName: String)
|
||||
func set(_ value: Float, forKey defaultName: String)
|
||||
func set(_ value: Double, forKey defaultName: String)
|
||||
func set(_ value: Bool, forKey defaultName: String)
|
||||
func set(_ url: URL?, forKey defaultName: String)
|
||||
}
|
||||
|
||||
extension UserDefaults: UserDefaultsType {}
|
||||
|
||||
public enum SNUserDefaults {
|
||||
|
||||
public enum Bool : Swift.String {
|
||||
|
@ -31,7 +54,7 @@ public enum SNUserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
public extension UserDefaults {
|
||||
public extension UserDefaultsType {
|
||||
|
||||
subscript(bool: SNUserDefaults.Bool) -> Bool {
|
||||
get { return self.bool(forKey: bool.rawValue) }
|
||||
|
|
Loading…
Reference in a new issue