Added some more tests and cleaned up some warnings

Fixed the broken LibSessionSpec tests
Added the libSessionSpec tests for GROUP_INFO and GROUP_MEMBERS
This commit is contained in:
Morgan Pretty 2023-09-28 17:59:21 +10:00
parent a48327f6f6
commit 67311f6d25
35 changed files with 1168 additions and 523 deletions

View file

@ -27,7 +27,7 @@ extension ProjectState {
"_SharedTestUtilities/", // Exclude shared test directory
"external/" // External dependencies
]
static let excludedPhrases: Set<String> = [ "", " ", ",", ", ", "null" ]
static let excludedPhrases: Set<String> = [ "", " ", ",", ", ", ".", "/", "\\n", "null" ]
static let excludedUnlocalisedStringLineMatching: Set<MatchType> = [
.contains(ProjectState.lintSuppression),
.prefix("#import"),
@ -38,12 +38,14 @@ extension ProjectState {
.contains("print("),
.contains("NSLog("),
.contains("SNLog("),
.contains("SNLogNotTests("),
.contains("owsFailDebug("),
.contains("#imageLiteral(resourceName:"),
.contains("UIImage(named:"),
.contains("UIImage(systemName:"),
.contains("[UIImage imageNamed:"),
.contains("UIFont(name:"),
.contains(".dateFormat ="),
.contains(".accessibilityLabel ="),
.contains(".accessibilityValue ="),
.contains(".accessibilityIdentifier ="),
@ -51,6 +53,10 @@ extension ProjectState {
.contains("accessibilityLabel:"),
.contains("Accessibility(identifier:"),
.contains("Accessibility(label:"),
.contains("NSAttributedString.Key("),
.contains("Notification.Name("),
.contains("Notification.Key("),
.contains("DispatchQueue("),
.containsAnd("identifier:", .previousLine(numEarlier: 1, .contains("Accessibility("))),
.containsAnd("label:", .previousLine(numEarlier: 1, .contains("Accessibility("))),
.containsAnd("label:", .previousLine(numEarlier: 2, .contains("Accessibility("))),
@ -58,7 +64,10 @@ extension ProjectState {
.regex(".*static var databaseTableName: String"),
.regex("Logger\\..*\\("),
.regex("OWSLogger\\..*\\("),
.regex("case .* = ")
.regex("case .* = "),
.regex("Error.*\\("),
.regex("Crypto.*\\(id:"),
.containsAnd("id:", .previousLine(numEarlier: 1, .regex("Crypto.*\\(")))
]
}

View file

@ -759,6 +759,8 @@
FDA8EB10280F8238002B68E5 /* Codable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */; };
FDAA16762AC28A3B00DDBF77 /* UserDefaultsType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAA16752AC28A3B00DDBF77 /* UserDefaultsType.swift */; };
FDAA167B2AC28E2F00DDBF77 /* SnodeRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAA167A2AC28E2F00DDBF77 /* SnodeRequestSpec.swift */; };
FDAA167D2AC528A200DDBF77 /* Preferences+Sound.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAA167C2AC528A200DDBF77 /* Preferences+Sound.swift */; };
FDAA167F2AC5290000DDBF77 /* Preferences+NotificationPreviewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAA167E2AC5290000DDBF77 /* Preferences+NotificationPreviewType.swift */; };
FDAED05C2A7C6CE600091B25 /* MigrationRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDAED05B2A7C6CE600091B25 /* MigrationRequirement.swift */; };
FDB4BBC72838B91E00B7C95D /* LinkPreviewError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB4BBC62838B91E00B7C95D /* LinkPreviewError.swift */; };
FDB4BBC92839BEF000B7C95D /* ProfileManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB4BBC82839BEF000B7C95D /* ProfileManagerError.swift */; };
@ -1978,6 +1980,8 @@
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Codable+Utilities.swift"; sourceTree = "<group>"; };
FDAA16752AC28A3B00DDBF77 /* UserDefaultsType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsType.swift; sourceTree = "<group>"; };
FDAA167A2AC28E2F00DDBF77 /* SnodeRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeRequestSpec.swift; sourceTree = "<group>"; };
FDAA167C2AC528A200DDBF77 /* Preferences+Sound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Sound.swift"; sourceTree = "<group>"; };
FDAA167E2AC5290000DDBF77 /* Preferences+NotificationPreviewType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+NotificationPreviewType.swift"; sourceTree = "<group>"; };
FDAED05B2A7C6CE600091B25 /* MigrationRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationRequirement.swift; sourceTree = "<group>"; };
FDB4BBC62838B91E00B7C95D /* LinkPreviewError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewError.swift; sourceTree = "<group>"; };
FDB4BBC82839BEF000B7C95D /* ProfileManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileManagerError.swift; sourceTree = "<group>"; };
@ -3393,6 +3397,8 @@
C38EF2F7255B6DBC007E1867 /* OWSAudioPlayer.m */,
C38EF281255B6D84007E1867 /* OWSAudioSession.swift */,
FDF0B75D280AAF35004C14C5 /* Preferences.swift */,
FDAA167E2AC5290000DDBF77 /* Preferences+NotificationPreviewType.swift */,
FDAA167C2AC528A200DDBF77 /* Preferences+Sound.swift */,
C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */,
C38EF306255B6DBE007E1867 /* OWSWindowManager.m */,
FD09797327FAB3E200936362 /* ProfileManager.swift */,
@ -6386,6 +6392,7 @@
FD1D732E2A86114600E3F410 /* _015_BlockCommunityMessageRequests.swift in Sources */,
FD432437299DEA38008A0213 /* TypeConversion+Utilities.swift in Sources */,
FD2B4B042949887A00AB4848 /* QueryInterfaceRequest+Utilities.swift in Sources */,
FDAA167F2AC5290000DDBF77 /* Preferences+NotificationPreviewType.swift in Sources */,
FD09797027FA6FF300936362 /* Profile.swift in Sources */,
FD245C56285065EA00B966DD /* SNProto.swift in Sources */,
FD09798B27FD1CFE00936362 /* Capability.swift in Sources */,
@ -6393,6 +6400,7 @@
FD09798127FCFEE800936362 /* SessionThread.swift in Sources */,
FD09C5EA282A1BB2000CE219 /* ThreadTypingIndicator.swift in Sources */,
FDB5DADA2A95D839002C8721 /* GroupUpdateInfoChangeMessage.swift in Sources */,
FDAA167D2AC528A200DDBF77 /* Preferences+Sound.swift in Sources */,
FDF0B75E280AAF35004C14C5 /* Preferences.swift in Sources */,
FDC4383827B3863200C60D73 /* VersionResponse.swift in Sources */,
FDB5DAD42A9483F3002C8721 /* GroupUpdateInviteMessage.swift in Sources */,

View file

@ -1,5 +1,7 @@
#!/usr/bin/env xcrun swift
// stringlint:disable
import Foundation
// The way this works is:

View file

@ -115,10 +115,7 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
customPadding: SessionCell.Padding(bottom: Values.verySmallSpacing)
),
onTap: { [weak self] in
UserDefaults.standard.set(
!UserDefaults.standard.bool(forKey: "isUsingFullAPNs"),
forKey: "isUsingFullAPNs"
)
dependencies[defaults: .standard, key: .isUsingFullAPNs] = !dependencies[defaults: .standard, key: .isUsingFullAPNs]
// Force sync the push tokens on change
SyncPushTokensJob.run(uploadOnlyIfStale: false)

View file

@ -1,3 +1,5 @@
// stringlint:disable
import WebRTC
extension RTCSignalingState : CustomStringConvertible {

View file

@ -6,7 +6,7 @@ import SessionUtilitiesKit
enum _017_DisappearingMessagesConfiguration: Migration {
static let target: TargetMigrations.Identifier = .messagingKit
static let identifier: String = "DisappearingMessagesWithTypes"
static let identifier: String = "DisappearingMessagesWithTypes" // stringlint:disable
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.1
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]

View file

@ -6,7 +6,7 @@ import SessionUtilitiesKit
enum _018_GroupsRebuildChanges: Migration {
static let target: TargetMigrations.Identifier = .messagingKit
static let identifier: String = "GroupsRebuildChanges"
static let identifier: String = "GroupsRebuildChanges" // stringlint:disable
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.1
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]

View file

@ -66,26 +66,8 @@ extension Capability.Variant {
public init(from decoder: Decoder) throws {
let container: SingleValueDecodingContainer = try decoder.singleValueContainer()
let valueString: String = try container.decode(String.self)
// FIXME: Remove this code
// There was a point where we didn't have custom Codable handling for the Capability.Variant
// which resulted in the data being encoded into the database as a JSON dict - this code catches
// that case and extracts the standard string value so it can be processed the same as the
// "proper" custom Codable logic)
if valueString.starts(with: "{") {
self = Capability.Variant(
from: valueString
.replacingOccurrences(of: "\":{}}", with: "")
.replacingOccurrences(of: "\"}}", with: "")
.replacingOccurrences(of: "{\"unsupported\":{\"_0\":\"", with: "")
.replacingOccurrences(of: "{\"", with: "")
)
return
}
// FIXME: Remove this code ^^^
self = Capability.Variant(from: valueString)
self = Capability.Variant(from: try container.decode(String.self))
}
public func encode(to encoder: Encoder) throws {

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import GRDB

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import GRDB

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import GRDB

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import SessionUtilitiesKit

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -3813,10 +3813,6 @@ extension SNProtoAttachmentPointer.SNProtoAttachmentPointerBuilder {
case userProfile = 1
case contacts = 2
case convoInfoVolatile = 3
case userGroups = 4
case groupInfo = 5
case groupMembers = 6
case groupKeys = 7
}
private class func SNProtoSharedConfigMessageKindWrap(_ value: SessionProtos_SharedConfigMessage.Kind) -> SNProtoSharedConfigMessageKind {
@ -3824,10 +3820,6 @@ extension SNProtoAttachmentPointer.SNProtoAttachmentPointerBuilder {
case .userProfile: return .userProfile
case .contacts: return .contacts
case .convoInfoVolatile: return .convoInfoVolatile
case .userGroups: return .userGroups
case .groupInfo: return .groupInfo
case .groupMembers: return .groupMembers
case .groupKeys: return .groupKeys
}
}
@ -3836,10 +3828,6 @@ extension SNProtoAttachmentPointer.SNProtoAttachmentPointerBuilder {
case .userProfile: return .userProfile
case .contacts: return .contacts
case .convoInfoVolatile: return .convoInfoVolatile
case .userGroups: return .userGroups
case .groupInfo: return .groupInfo
case .groupMembers: return .groupMembers
case .groupKeys: return .groupKeys
}
}
@ -4314,22 +4302,22 @@ extension SNProtoGroupUpdateMessage.SNProtoGroupUpdateMessageBuilder {
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdateInviteMessage) throws -> SNProtoGroupUpdateInviteMessage {
guard proto.hasGroupIdentityPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: groupIdentityPublicKey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: groupIdentityPublicKey")
}
let groupIdentityPublicKey = proto.groupIdentityPublicKey
guard proto.hasName else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: name")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: name")
}
let name = proto.name
guard proto.hasMemberSubkey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: memberSubkey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: memberSubkey")
}
let memberSubkey = proto.memberSubkey
guard proto.hasMemberTag else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: memberTag")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: memberTag")
}
let memberTag = proto.memberTag
@ -4444,12 +4432,12 @@ extension SNProtoGroupUpdateInviteMessage.SNProtoGroupUpdateInviteMessageBuilder
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdateDeleteMessage) throws -> SNProtoGroupUpdateDeleteMessage {
guard proto.hasGroupIdentityPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: groupIdentityPublicKey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: groupIdentityPublicKey")
}
let groupIdentityPublicKey = proto.groupIdentityPublicKey
guard proto.hasEncryptedMemberSubkey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: encryptedMemberSubkey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: encryptedMemberSubkey")
}
let encryptedMemberSubkey = proto.encryptedMemberSubkey
@ -4602,7 +4590,7 @@ extension SNProtoGroupUpdateDeleteMessage.SNProtoGroupUpdateDeleteMessageBuilder
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdateInfoChangeMessage) throws -> SNProtoGroupUpdateInfoChangeMessage {
guard proto.hasType else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: type")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: type")
}
let type = SNProtoGroupUpdateInfoChangeMessageTypeWrap(proto.type)
@ -4738,7 +4726,7 @@ extension SNProtoGroupUpdateInfoChangeMessage.SNProtoGroupUpdateInfoChangeMessag
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdateMemberChangeMessage) throws -> SNProtoGroupUpdateMemberChangeMessage {
guard proto.hasType else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: type")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: type")
}
let type = SNProtoGroupUpdateMemberChangeMessageTypeWrap(proto.type)
@ -4844,12 +4832,12 @@ extension SNProtoGroupUpdateMemberChangeMessage.SNProtoGroupUpdateMemberChangeMe
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdatePromoteMessage) throws -> SNProtoGroupUpdatePromoteMessage {
guard proto.hasMemberPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: memberPublicKey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: memberPublicKey")
}
let memberPublicKey = proto.memberPublicKey
guard proto.hasEncryptedGroupIdentityPrivateKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: encryptedGroupIdentityPrivateKey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: encryptedGroupIdentityPrivateKey")
}
let encryptedGroupIdentityPrivateKey = proto.encryptedGroupIdentityPrivateKey
@ -5052,7 +5040,7 @@ extension SNProtoGroupUpdateMemberLeftMessage.SNProtoGroupUpdateMemberLeftMessag
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdateInviteResponseMessage) throws -> SNProtoGroupUpdateInviteResponseMessage {
guard proto.hasIsApproved else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: isApproved")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: isApproved")
}
let isApproved = proto.isApproved
@ -5155,7 +5143,7 @@ extension SNProtoGroupUpdateInviteResponseMessage.SNProtoGroupUpdateInviteRespon
fileprivate class func parseProto(_ proto: SessionProtos_GroupUpdatePromotionResponseMessage) throws -> SNProtoGroupUpdatePromotionResponseMessage {
guard proto.hasEncryptedMemberPublicKey else {
throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: encryptedMemberPublicKey")
throw SNProtoError.invalidProtobuf(description: "\(String(describing: logTag)) missing required field: encryptedMemberPublicKey")
}
let encryptedMemberPublicKey = proto.encryptedMemberPublicKey

View file

@ -1705,10 +1705,6 @@ struct SessionProtos_SharedConfigMessage {
case userProfile // = 1
case contacts // = 2
case convoInfoVolatile // = 3
case userGroups // = 4
case groupInfo // = 5
case groupMembers // = 6
case groupKeys // = 7
init() {
self = .userProfile
@ -1719,10 +1715,6 @@ struct SessionProtos_SharedConfigMessage {
case 1: self = .userProfile
case 2: self = .contacts
case 3: self = .convoInfoVolatile
case 4: self = .userGroups
case 5: self = .groupInfo
case 6: self = .groupMembers
case 7: self = .groupKeys
default: return nil
}
}
@ -1732,10 +1724,6 @@ struct SessionProtos_SharedConfigMessage {
case .userProfile: return 1
case .contacts: return 2
case .convoInfoVolatile: return 3
case .userGroups: return 4
case .groupInfo: return 5
case .groupMembers: return 6
case .groupKeys: return 7
}
}
@ -3994,10 +3982,6 @@ extension SessionProtos_SharedConfigMessage.Kind: SwiftProtobuf._ProtoNameProvid
1: .same(proto: "USER_PROFILE"),
2: .same(proto: "CONTACTS"),
3: .same(proto: "CONVO_INFO_VOLATILE"),
4: .same(proto: "USER_GROUPS"),
5: .same(proto: "GROUP_INFO"),
6: .same(proto: "GROUP_MEMBERS"),
7: .same(proto: "GROUP_KEYS"),
]
}

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -1,4 +1,6 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import SessionUtilitiesKit

View file

@ -1,4 +1,6 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import SessionUtil
@ -25,6 +27,7 @@ public extension SessionUtil {
// MARK: - Config
enum Config {
case invalid
case object(UnsafeMutablePointer<config_object>)
case groupKeys(
UnsafeMutablePointer<config_group_keys>,
@ -36,6 +39,7 @@ public extension SessionUtil {
var needsPush: Bool {
switch self {
case .invalid: return false
case .object(let conf): return config_needs_push(conf)
case .groupKeys(let conf, _, _):
@ -48,6 +52,7 @@ public extension SessionUtil {
var needsDump: Bool {
switch self {
case .invalid: return false
case .object(let conf): return config_needs_dump(conf)
case .groupKeys(let conf, _, _): return groups_keys_needs_dump(conf)
}
@ -55,6 +60,7 @@ public extension SessionUtil {
var lastError: String {
switch self {
case .invalid: return "Invalid"
case .object(let conf): return String(cString: conf.pointee.last_error)
case .groupKeys(let conf, _, _): return String(cString: conf.pointee.last_error)
}
@ -87,6 +93,7 @@ public extension SessionUtil {
func push(variant: ConfigDump.Variant) throws -> SessionUtil.PushData {
switch self {
case .invalid: throw SessionUtilError.invalidConfigObject
case .object(let conf):
var cPushData: UnsafeMutablePointer<config_push_data>!
@ -142,6 +149,7 @@ public extension SessionUtil {
var cHash: [CChar] = hash.cArray.nullTerminated()
switch self {
case .invalid: return
case .object(let conf): return config_confirm_pushed(conf, seqNo, &cHash)
case .groupKeys: return // No need to do anything here
}
@ -153,6 +161,7 @@ public extension SessionUtil {
try CExceptionHelper.performSafely {
switch self {
case .invalid: return
case .object(let conf): config_dump(conf, &dumpResult, &dumpResultLen)
case .groupKeys(let conf, _, _): groups_keys_dump(conf, &dumpResult, &dumpResultLen)
}
@ -168,6 +177,7 @@ public extension SessionUtil {
func currentHashes() -> [String] {
switch self {
case .invalid: return []
case .object(let conf):
guard let hashList: UnsafeMutablePointer<config_string_list> = config_current_hashes(conf) else {
return []
@ -200,7 +210,8 @@ public extension SessionUtil {
func merge(_ messages: [ConfigMessageReceiveJob.Details.MessageInfo]) throws -> Int64? {
switch self {
case .object(let conf):
case .invalid: throw SessionUtilError.invalidConfigObject
case .object(let conf):
var mergeHashes: [UnsafePointer<CChar>?] = messages
.map { message in message.serverHash.cArray.nullTerminated() }
.unsafeCopy()
@ -266,6 +277,7 @@ public extension SessionUtil {
try? CExceptionHelper.performSafely {
switch self {
case .invalid: return
case .object(let conf): result = funcMap[variant].map { "\($0.size(conf)) \($0.info)" }
case .groupKeys(let conf, _, _): result = "\(groups_keys_size(conf)) group keys"
}
@ -279,7 +291,7 @@ public extension SessionUtil {
// MARK: - PushData
public extension SessionUtil {
public struct PushData {
struct PushData {
let data: Data
let seqNo: Int64
let variant: ConfigDump.Variant

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import GRDB
import SessionUtilitiesKit

View file

@ -0,0 +1,28 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import DifferenceKit
import SessionUtilitiesKit
public extension Preferences {
enum NotificationPreviewType: Int, CaseIterable, EnumIntSetting, Differentiable {
public static var defaultPreviewType: NotificationPreviewType = .nameAndPreview
/// Notifications should include both the sender name and a preview of the message content
case nameAndPreview
/// Notifications should include the sender name but no preview
case nameNoPreview
/// Notifications should be a generic message
case noNameNoPreview
public var name: String {
switch self {
case .nameAndPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_AND_CONTENT".localized()
case .nameNoPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_ONLY".localized()
case .noNameNoPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NO_NAME_OR_CONTENT".localized()
}
}
}
}

View file

@ -0,0 +1,205 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import AudioToolbox
import GRDB
import DifferenceKit
import SessionUtilitiesKit
public extension Preferences {
enum Sound: Int, Codable, DatabaseValueConvertible, EnumIntSetting, Differentiable {
public static var defaultiOSIncomingRingtone: Sound = .opening
public static var defaultNotificationSound: Sound = .note
// Don't store too many sounds in memory (Most users will only use 1 or 2 sounds anyway)
private static let maxCachedSounds: Int = 4
private static var cachedSystemSounds: Atomic<[String: (url: URL?, soundId: SystemSoundID)]> = Atomic([:])
private static var cachedSystemSoundOrder: Atomic<[String]> = Atomic([])
// Values
case `default`
// Notification Sounds
case aurora = 1000
case bamboo
case chord
case circles
case complete
case hello
case input
case keys
case note
case popcorn
case pulse
case synth
case signalClassic
// Ringtone Sounds
case opening = 2000
// Calls
case callConnecting = 3000
case callOutboundRinging
case callBusy
case callFailure
// Other
case messageSent = 4000
case none
public static var notificationSounds: [Sound] {
return [
// None and Note (default) should be first.
.none,
.note,
.aurora,
.bamboo,
.chord,
.circles,
.complete,
.hello,
.input,
.keys,
.popcorn,
.pulse,
.synth
]
}
public var displayName: String {
// TODO: Should we localize these sound names?
switch self {
case .`default`: return ""
// Notification Sounds
case .aurora: return "Aurora"
case .bamboo: return "Bamboo"
case .chord: return "Chord"
case .circles: return "Circles"
case .complete: return "Complete"
case .hello: return "Hello"
case .input: return "Input"
case .keys: return "Keys"
case .note: return "Note"
case .popcorn: return "Popcorn"
case .pulse: return "Pulse"
case .synth: return "Synth"
case .signalClassic: return "Signal Classic"
// Ringtone Sounds
case .opening: return "Opening"
// Calls
case .callConnecting: return "Call Connecting"
case .callOutboundRinging: return "Call Outboung Ringing"
case .callBusy: return "Call Busy"
case .callFailure: return "Call Failure"
// Other
case .messageSent: return "Message Sent"
case .none: return "SOUNDS_NONE".localized()
}
}
// MARK: - Functions
public func filename(quiet: Bool = false) -> String? {
switch self {
case .`default`: return ""
// Notification Sounds
case .aurora: return (quiet ? "aurora-quiet.aifc" : "aurora.aifc")
case .bamboo: return (quiet ? "bamboo-quiet.aifc" : "bamboo.aifc")
case .chord: return (quiet ? "chord-quiet.aifc" : "chord.aifc")
case .circles: return (quiet ? "circles-quiet.aifc" : "circles.aifc")
case .complete: return (quiet ? "complete-quiet.aifc" : "complete.aifc")
case .hello: return (quiet ? "hello-quiet.aifc" : "hello.aifc")
case .input: return (quiet ? "input-quiet.aifc" : "input.aifc")
case .keys: return (quiet ? "keys-quiet.aifc" : "keys.aifc")
case .note: return (quiet ? "note-quiet.aifc" : "note.aifc")
case .popcorn: return (quiet ? "popcorn-quiet.aifc" : "popcorn.aifc")
case .pulse: return (quiet ? "pulse-quiet.aifc" : "pulse.aifc")
case .synth: return (quiet ? "synth-quiet.aifc" : "synth.aifc")
case .signalClassic: return (quiet ? "classic-quiet.aifc" : "classic.aifc")
// Ringtone Sounds
case .opening: return "Opening.m4r"
// Calls
case .callConnecting: return "ringback_tone_ansi.caf"
case .callOutboundRinging: return "ringback_tone_ansi.caf"
case .callBusy: return "busy_tone_ansi.caf"
case .callFailure: return "end_call_tone_cept.caf"
// Other
case .messageSent: return "message_sent.aiff"
case .none: return "silence.aiff"
}
}
public func soundUrl(quiet: Bool = false) -> URL? {
guard let filename: String = filename(quiet: quiet) else { return nil }
let url: URL = URL(fileURLWithPath: filename)
return Bundle.main.url(
forResource: url.deletingPathExtension().path,
withExtension: url.pathExtension
)
}
public func notificationSound(isQuiet: Bool) -> UNNotificationSound {
guard let filename: String = filename(quiet: isQuiet) else {
SNLog("[Preferences.Sound] filename was unexpectedly nil")
return UNNotificationSound.default
}
return UNNotificationSound(named: UNNotificationSoundName(rawValue: filename))
}
public static func systemSoundId(for sound: Sound, quiet: Bool) -> SystemSoundID {
let cacheKey: String = "\(sound.rawValue):\(quiet ? 1 : 0)"
if let cachedSound: SystemSoundID = cachedSystemSounds.wrappedValue[cacheKey]?.soundId {
return cachedSound
}
let systemSound: (url: URL?, soundId: SystemSoundID) = (
url: sound.soundUrl(quiet: quiet),
soundId: SystemSoundID()
)
cachedSystemSounds.mutate { cache in
cachedSystemSoundOrder.mutate { order in
if order.count > Sound.maxCachedSounds {
cache.removeValue(forKey: order[0])
order.remove(at: 0)
}
order.append(cacheKey)
}
cache[cacheKey] = systemSound
}
return systemSound.soundId
}
// MARK: - AudioPlayer
public static func audioPlayer(for sound: Sound, behavior: OWSAudioBehavior) -> OWSAudioPlayer? {
guard let soundUrl: URL = sound.soundUrl(quiet: false) else { return nil }
let player = OWSAudioPlayer(mediaUrl: soundUrl, audioBehavior: behavior)
// These two cases should loop
if sound == .callConnecting || sound == .callOutboundRinging {
player.isLooping = true
}
return player
}
}
}

View file

@ -1,9 +1,8 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import AudioToolbox
import GRDB
import DifferenceKit
import SessionUtilitiesKit
public extension Setting.EnumKey {
@ -99,223 +98,6 @@ public extension Setting.IntKey {
}
public enum Preferences {
public enum NotificationPreviewType: Int, CaseIterable, EnumIntSetting, Differentiable {
public static var defaultPreviewType: NotificationPreviewType = .nameAndPreview
/// Notifications should include both the sender name and a preview of the message content
case nameAndPreview
/// Notifications should include the sender name but no preview
case nameNoPreview
/// Notifications should be a generic message
case noNameNoPreview
public var name: String {
switch self {
case .nameAndPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_AND_CONTENT".localized()
case .nameNoPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_ONLY".localized()
case .noNameNoPreview: return "NOTIFICATIONS_STYLE_CONTENT_OPTION_NO_NAME_OR_CONTENT".localized()
}
}
}
public enum Sound: Int, Codable, DatabaseValueConvertible, EnumIntSetting, Differentiable {
public static var defaultiOSIncomingRingtone: Sound = .opening
public static var defaultNotificationSound: Sound = .note
// Don't store too many sounds in memory (Most users will only use 1 or 2 sounds anyway)
private static let maxCachedSounds: Int = 4
private static var cachedSystemSounds: Atomic<[String: (url: URL?, soundId: SystemSoundID)]> = Atomic([:])
private static var cachedSystemSoundOrder: Atomic<[String]> = Atomic([])
// Values
case `default`
// Notification Sounds
case aurora = 1000
case bamboo
case chord
case circles
case complete
case hello
case input
case keys
case note
case popcorn
case pulse
case synth
case signalClassic
// Ringtone Sounds
case opening = 2000
// Calls
case callConnecting = 3000
case callOutboundRinging
case callBusy
case callFailure
// Other
case messageSent = 4000
case none
public static var notificationSounds: [Sound] {
return [
// None and Note (default) should be first.
.none,
.note,
.aurora,
.bamboo,
.chord,
.circles,
.complete,
.hello,
.input,
.keys,
.popcorn,
.pulse,
.synth
]
}
public var displayName: String {
// TODO: Should we localize these sound names?
switch self {
case .`default`: return ""
// Notification Sounds
case .aurora: return "Aurora"
case .bamboo: return "Bamboo"
case .chord: return "Chord"
case .circles: return "Circles"
case .complete: return "Complete"
case .hello: return "Hello"
case .input: return "Input"
case .keys: return "Keys"
case .note: return "Note"
case .popcorn: return "Popcorn"
case .pulse: return "Pulse"
case .synth: return "Synth"
case .signalClassic: return "Signal Classic"
// Ringtone Sounds
case .opening: return "Opening"
// Calls
case .callConnecting: return "Call Connecting"
case .callOutboundRinging: return "Call Outboung Ringing"
case .callBusy: return "Call Busy"
case .callFailure: return "Call Failure"
// Other
case .messageSent: return "Message Sent"
case .none: return "SOUNDS_NONE".localized()
}
}
// MARK: - Functions
public func filename(quiet: Bool = false) -> String? {
switch self {
case .`default`: return ""
// Notification Sounds
case .aurora: return (quiet ? "aurora-quiet.aifc" : "aurora.aifc")
case .bamboo: return (quiet ? "bamboo-quiet.aifc" : "bamboo.aifc")
case .chord: return (quiet ? "chord-quiet.aifc" : "chord.aifc")
case .circles: return (quiet ? "circles-quiet.aifc" : "circles.aifc")
case .complete: return (quiet ? "complete-quiet.aifc" : "complete.aifc")
case .hello: return (quiet ? "hello-quiet.aifc" : "hello.aifc")
case .input: return (quiet ? "input-quiet.aifc" : "input.aifc")
case .keys: return (quiet ? "keys-quiet.aifc" : "keys.aifc")
case .note: return (quiet ? "note-quiet.aifc" : "note.aifc")
case .popcorn: return (quiet ? "popcorn-quiet.aifc" : "popcorn.aifc")
case .pulse: return (quiet ? "pulse-quiet.aifc" : "pulse.aifc")
case .synth: return (quiet ? "synth-quiet.aifc" : "synth.aifc")
case .signalClassic: return (quiet ? "classic-quiet.aifc" : "classic.aifc")
// Ringtone Sounds
case .opening: return "Opening.m4r"
// Calls
case .callConnecting: return "ringback_tone_ansi.caf"
case .callOutboundRinging: return "ringback_tone_ansi.caf"
case .callBusy: return "busy_tone_ansi.caf"
case .callFailure: return "end_call_tone_cept.caf"
// Other
case .messageSent: return "message_sent.aiff"
case .none: return "silence.aiff"
}
}
public func soundUrl(quiet: Bool = false) -> URL? {
guard let filename: String = filename(quiet: quiet) else { return nil }
let url: URL = URL(fileURLWithPath: filename)
return Bundle.main.url(
forResource: url.deletingPathExtension().path,
withExtension: url.pathExtension
)
}
public func notificationSound(isQuiet: Bool) -> UNNotificationSound {
guard let filename: String = filename(quiet: isQuiet) else {
SNLog("[Preferences.Sound] filename was unexpectedly nil")
return UNNotificationSound.default
}
return UNNotificationSound(named: UNNotificationSoundName(rawValue: filename))
}
public static func systemSoundId(for sound: Sound, quiet: Bool) -> SystemSoundID {
let cacheKey: String = "\(sound.rawValue):\(quiet ? 1 : 0)"
if let cachedSound: SystemSoundID = cachedSystemSounds.wrappedValue[cacheKey]?.soundId {
return cachedSound
}
let systemSound: (url: URL?, soundId: SystemSoundID) = (
url: sound.soundUrl(quiet: quiet),
soundId: SystemSoundID()
)
cachedSystemSounds.mutate { cache in
cachedSystemSoundOrder.mutate { order in
if order.count > Sound.maxCachedSounds {
cache.removeValue(forKey: order[0])
order.remove(at: 0)
}
order.append(cacheKey)
}
cache[cacheKey] = systemSound
}
return systemSound.soundId
}
// MARK: - AudioPlayer
public static func audioPlayer(for sound: Sound, behavior: OWSAudioBehavior) -> OWSAudioPlayer? {
guard let soundUrl: URL = sound.soundUrl(quiet: false) else { return nil }
let player = OWSAudioPlayer(mediaUrl: soundUrl, audioBehavior: behavior)
// These two cases should loop
if sound == .callConnecting || sound == .callOutboundRinging {
player.isLooping = true
}
return player
}
}
public static var isCallKitSupported: Bool {
#if targetEnvironment(simulator)
/// The iOS simulator doesn't support CallKit, when receiving a call on the simulator and routing it via CallKit it

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

File diff suppressed because it is too large Load diff

View file

@ -5,11 +5,7 @@ import SessionUtil
import SessionMessagingKit
extension SessionUtil.Config: Mocked {
static var mockValue: SessionUtil.Config = {
var config: config_object = config_object()
return .object(&config)
}()
static var mockValue: SessionUtil.Config = .invalid
}
extension ConfigDump.Variant: Mocked {

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation
import SessionUtilitiesKit

View file

@ -4,7 +4,7 @@ import Foundation
import GRDB
public class Dependencies {
static let userInfoKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "io.oxen.dependencies.codingOptions")!
static let userInfoKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "io.oxen.dependencies.codingOptions")! // stringlint:disable
private static var singletonInstances: Atomic<[Int: Any]> = Atomic([:])
private static var cacheInstances: Atomic<[Int: MutableCacheType]> = Atomic([:])

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -210,8 +210,8 @@ typedef struct {
// This should usually be 1.
CGFloat depthBytes = (CGFloat)ceil(depthBits / 8.f);
/* The color model of the image such as "RGB", "CMYK", "Gray", or "Lab".
* The value of this key is CFStringRef. */
// The color model of the image such as "RGB", "CMYK", "Gray", or "Lab".
// The value of this key is CFStringRef.
NSString *colorModel = imageProperties[(__bridge NSString *)kCGImagePropertyColorModel];
if (!colorModel) {
return info;

View file

@ -179,7 +179,7 @@ internal extension HTTP.BatchResponse {
case let anyDict as [String: Any]:
guard
let resultsArray: [Data] = (anyDict["results"] as? [Any])?
let resultsArray: [Data] = (anyDict["results"] as? [Any])? // stringlint:disable
.compactMap({ try? JSONSerialization.data(withJSONObject: $0) }),
(
!requireAllResults ||

View file

@ -1,4 +1,6 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -1,4 +1,6 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -1,4 +1,6 @@
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable
import Foundation

View file

@ -1,6 +1,7 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
// stringlint:disable
import Foundation
import UIKit