diff --git a/ConversationsClassic/AppData/Model/Message+OMEMO.swift b/ConversationsClassic/AppData/Model/Message+OMEMO.swift index 3a9c253..538d3e7 100644 --- a/ConversationsClassic/AppData/Model/Message+OMEMO.swift +++ b/ConversationsClassic/AppData/Model/Message+OMEMO.swift @@ -48,6 +48,11 @@ extension Message { } } + // skip for non-visible messages + if martinMessage.body == nil, martinMessage.oob == nil, martinMessage.type == .chat { + return nil + } + // From/To let from = martinMessage.from?.bareJid.stringValue ?? "" let to = martinMessage.to?.bareJid.stringValue diff --git a/old/AppCore/Actions/AccountsActions.swift b/old/AppCore/Actions/AccountsActions.swift deleted file mode 100644 index b2b6890..0000000 --- a/old/AppCore/Actions/AccountsActions.swift +++ /dev/null @@ -1,12 +0,0 @@ -enum AccountsAction: Codable { - case accountsListUpdated(accounts: [Account]) - - case goTo(AccountNavigationState) - - case tryAddAccountWithCredentials(login: String, password: String) - case addAccountError(jid: String, reason: String?) - - case makeAccountPermanent(account: Account) - - case clientServerFeaturesUpdated(jid: String, features: [ServerFeature]) -} diff --git a/old/AppCore/Actions/AppActions.swift b/old/AppCore/Actions/AppActions.swift deleted file mode 100644 index 2cb786d..0000000 --- a/old/AppCore/Actions/AppActions.swift +++ /dev/null @@ -1,15 +0,0 @@ -enum AppAction: Codable { - case info(String) - case flushState - case changeFlow(_ flow: AppFlow) - - case startAction(_ action: StartAction) - case databaseAction(_ action: DatabaseAction) - case accountsAction(_ action: AccountsAction) - case xmppAction(_ action: XMPPAction) - case rostersAction(_ action: RostersAction) - case chatsAction(_ action: ChatsAction) - case conversationAction(_ action: ConversationAction) - case sharingAction(_ action: SharingAction) - case fileAction(_ action: FileAction) -} diff --git a/old/AppCore/Actions/ChatsActions.swift b/old/AppCore/Actions/ChatsActions.swift deleted file mode 100644 index b247cd2..0000000 --- a/old/AppCore/Actions/ChatsActions.swift +++ /dev/null @@ -1,10 +0,0 @@ -enum ChatsAction: Codable { - case chatsListUpdated(chats: [Chat]) - - case startChat(accountJid: String, participantJid: String) - case chatStarted(chat: Chat) - - case createNewChat(accountJid: String, participantJid: String) - case chatCreated(chat: Chat) - case chatCreationFailed(reason: String) -} diff --git a/old/AppCore/Actions/ConversationActions.swift b/old/AppCore/Actions/ConversationActions.swift deleted file mode 100644 index 6bb8b32..0000000 --- a/old/AppCore/Actions/ConversationActions.swift +++ /dev/null @@ -1,10 +0,0 @@ -enum ConversationAction: Codable { - case makeConversationActive(chat: Chat, roster: Roster?) - - case messagesUpdated(messages: [Message]) - - case sendMessage(from: String, to: String, body: String) - case setReplyText(String) - - case sendMediaMessages(from: String, to: String, messagesIds: [String], localFilesNames: [String]) -} diff --git a/old/AppCore/Actions/DatabaseActions.swift b/old/AppCore/Actions/DatabaseActions.swift deleted file mode 100644 index b474cae..0000000 --- a/old/AppCore/Actions/DatabaseActions.swift +++ /dev/null @@ -1,12 +0,0 @@ -enum DatabaseAction: Codable { - case storedAccountsLoaded(accounts: [Account]) - case loadingStoredAccountsFailed - case updateAccountFailed - - case storedRostersLoaded(rosters: [Roster]) - case storedChatsLoaded(chats: [Chat]) - - case storeMessageFailed(reason: String) - - case updateAttachmentFailed(id: String, reason: String) -} diff --git a/old/AppCore/Actions/FileActions.swift b/old/AppCore/Actions/FileActions.swift deleted file mode 100644 index 46187c6..0000000 --- a/old/AppCore/Actions/FileActions.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -enum FileAction: Stateable { - case downloadAttachmentFile(messageId: String, attachmentRemotePath: URL) - case attachmentFileDownloaded(messageId: String, localName: String) - case downloadingAttachmentFileFailed(messageId: String, reason: String) - - case createAttachmentThumbnail(messageId: String, localName: String) - case attachmentThumbnailCreated(messageId: String, thumbnailName: String) - - case fetchItemsFromGallery - case itemsFromGalleryFetched(items: [SharingGalleryItem]) - - case copyGalleryItemsForUploading(items: [SharingGalleryItem]) - case copyCameraCapturedForUploading(media: Data, type: SharingCameraMediaType) - case itemsCopiedForUploading(newMessageIds: [String], localNames: [String]) -} diff --git a/old/AppCore/Actions/RostersActions.swift b/old/AppCore/Actions/RostersActions.swift deleted file mode 100644 index b4ba796..0000000 --- a/old/AppCore/Actions/RostersActions.swift +++ /dev/null @@ -1,12 +0,0 @@ -enum RostersAction: Codable { - case addRoster(ownerJID: String, contactJID: String, name: String?, groups: [String]) - case addRosterDone(jid: String) - case addRosterError(reason: String) - - case rostersListUpdated([Roster]) - - case markRosterAsLocallyDeleted(ownerJID: String, contactJID: String) - case unmarkRosterAsLocallyDeleted(ownerJID: String, contactJID: String) - case deleteRoster(ownerJID: String, contactJID: String) - case rosterDeletingFailed(reason: String) -} diff --git a/old/AppCore/Actions/SharingActions.swift b/old/AppCore/Actions/SharingActions.swift deleted file mode 100644 index 6d8d0ee..0000000 --- a/old/AppCore/Actions/SharingActions.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation - -enum SharingAction: Stateable { - case showSharing(Bool) - - case shareLocation(lat: Double, lon: Double) - case shareContact(jid: String) - case shareDocuments([Data], [String]) - case shareMedia(ids: [String]) - - case checkCameraAccess - case setCameraAccess(Bool) - - case checkGalleryAccess - case setGalleryAccess(Bool) - case galleryItemsUpdated(items: [SharingGalleryItem]) - - case cameraCaptured(media: Data, type: SharingCameraMediaType) - - case retrySharing(messageId: String) -} diff --git a/old/AppCore/Actions/StartActions.swift b/old/AppCore/Actions/StartActions.swift deleted file mode 100644 index 7d41c7c..0000000 --- a/old/AppCore/Actions/StartActions.swift +++ /dev/null @@ -1,5 +0,0 @@ -enum StartAction: Codable { - case loadStoredAccounts - - case goTo(StartNavigationState) -} diff --git a/old/AppCore/Actions/XMPPActions.swift b/old/AppCore/Actions/XMPPActions.swift deleted file mode 100644 index 8876ad3..0000000 --- a/old/AppCore/Actions/XMPPActions.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -enum XMPPAction: Codable { - case clientConnectionChanged(jid: String, state: ConnectionStatus) - case xmppMessageReceived(Message) - - case xmppMessageSent(Message) - case xmppMessageSendFailed(msgId: String) - case xmppMessageSendSuccess(msgId: String) - - case xmppSharingTryUpload(Message) - case xmppSharingUploadFailed(msgId: String, reason: String) - case xmppSharingUploadSuccess(msgId: String, attachmentRemotePath: String) - case serverFeaturesLoaded(jid: String, features: [String]) - - case xmppLoadArchivedMessages(jid: String, to: String?, fromDate: Date) -} diff --git a/old/AppCore/AppStore.swift b/old/AppCore/AppStore.swift deleted file mode 100644 index 11ad860..0000000 --- a/old/AppCore/AppStore.swift +++ /dev/null @@ -1,102 +0,0 @@ -// This file declare global state object for whole app -// and reducers/actions/middleware types. Core of app. -import Combine -import Foundation - -typealias Stateable = Codable & Equatable -typealias AppStore = Store -typealias Reducer = (inout State, Action) -> Void -typealias Middleware = (State, Action) -> AnyPublisher? - -final class Store: ObservableObject { - @Published private(set) var state: State - - // Serial queue for performing any actions sequentially - private let serialQueue = DispatchQueue(label: "im.narayana.conversations.classic.serial.queue", qos: .userInteractive) - private let middlewareQueue = DispatchQueue(label: "im.narayana.conversations.classic.middleware.queue", qos: .default, attributes: .concurrent) - - private let reducer: Reducer - private let middlewares: [Middleware] - private var middlewareCancellables: Set = [] - - // Init - init( - initialState: State, - reducer: @escaping Reducer, - middlewares: [Middleware] = [] - ) { - state = initialState - self.reducer = reducer - self.middlewares = middlewares - } - - // Run reducers/middlewares - func dispatch(_ action: Action) { - if !Thread.isMainThread { - print("❌WARNING!: AppStore.dispatch should be called from the main thread") - } - serialQueue.sync { [weak self] in - guard let wSelf = self else { return } - let newState = wSelf.dispatch(wSelf.state, action) - wSelf.state = newState - } - } - - private func dispatch(_ currentState: State, _ action: Action) -> State { - // Do reducing - var startTime = CFAbsoluteTimeGetCurrent() - var newState = currentState - reducer(&newState, action) - var timeElapsed = CFAbsoluteTimeGetCurrent() - startTime - if timeElapsed > 0.05 { - #if DEBUG - print( - """ - -- - (Ignore this warning ONLY in case, when execution is paused by your breakpoint) - 🕐Execution time: \(timeElapsed) - ❌WARNING! Some reducers work too long! It will lead to issues in production build! - Because of execution each action is synchronous the any stuck will reduce performance dramatically. - Probably you need check which part of reducer/middleware should be async (wrapped with Futures, as example) - -- - """ - ) - #else - #endif - } - - // Dispatch all middleware functions - for middleware in middlewares { - guard let middleware = middleware(newState, action) else { - break - } - startTime = CFAbsoluteTimeGetCurrent() - middleware - .subscribe(on: middlewareQueue) - .receive(on: DispatchQueue.main) - .sink(receiveValue: { [weak self] action in - self?.dispatch(action) - }) - .store(in: &middlewareCancellables) - timeElapsed = CFAbsoluteTimeGetCurrent() - startTime - if timeElapsed > 0.05 { - #if DEBUG - print( - """ - -- - (Ignore this warning ONLY in case, when execution is paused by your breakpoint) - 🕐Execution time: \(timeElapsed) - ❌WARNING! Middleware work too long! It will lead to issues in production build! - Because of execution each action is synchronous the any stuck will reduce performance dramatically. - Probably you need check which part of reducer/middleware should be async (wrapped with Futures, as example) - -- - """ - ) - #else - #endif - } - } - - return newState - } -} diff --git a/old/AppCore/Database/Database+Martin/Database+Martin.swift b/old/AppCore/Database/Database+Martin/Database+Martin.swift deleted file mode 100644 index aec3ef4..0000000 --- a/old/AppCore/Database/Database+Martin/Database+Martin.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation -import GRDB -import Martin - -extension Database: MartinsManager {} - -// Check specific implementation in Database+Martin* files diff --git a/old/AppCore/Database/Database+Martin/Database+MartinChannelManager.swift b/old/AppCore/Database/Database+Martin/Database+MartinChannelManager.swift deleted file mode 100644 index bac242c..0000000 --- a/old/AppCore/Database/Database+Martin/Database+MartinChannelManager.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation -import GRDB -import Martin - -extension Database: Martin.ChannelManager { - func channels(for _: Martin.Context) -> [any Martin.ChannelProtocol] { - [] - } - - func createChannel(for _: Martin.Context, with _: Martin.BareJID, participantId _: String, nick _: String?, state _: Martin.ChannelState) -> Martin.ConversationCreateResult { - .none - } - - func channel(for _: Martin.Context, with _: Martin.BareJID) -> (any Martin.ChannelProtocol)? { - nil - } - - func close(channel _: any Martin.ChannelProtocol) -> Bool { - false - } -} diff --git a/old/AppCore/Database/Database+Martin/Database+MartinChatManager.swift b/old/AppCore/Database/Database+Martin/Database+MartinChatManager.swift deleted file mode 100644 index 6f795f9..0000000 --- a/old/AppCore/Database/Database+Martin/Database+MartinChatManager.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Foundation -import GRDB -import Martin - -extension Database: Martin.ChatManager { - func chats(for context: Martin.Context) -> [any Martin.ChatProtocol] { - do { - let chats: [Chat] = try _db.read { db in - try Chat.filter(Column("account") == context.userBareJid.stringValue).fetchAll(db) - } - return chats.map { chat in - Martin.ChatBase(context: context, jid: BareJID(chat.participant)) - } - } catch { - logIt(.error, "Error fetching chats: \(error.localizedDescription)") - return [] - } - } - - func chat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? { - do { - let chat: Chat? = try _db.read { db in - try Chat - .filter(Column("account") == context.userBareJid.stringValue) - .filter(Column("participant") == with.stringValue) - .fetchOne(db) - } - if chat != nil { - return Martin.ChatBase(context: context, jid: with) - } else { - return nil - } - } catch { - logIt(.error, "Error fetching chat: \(error.localizedDescription)") - return nil - } - } - - func createChat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? { - do { - let chat: Chat? = try _db.read { db in - try Chat - .filter(Column("account") == context.userBareJid.stringValue) - .filter(Column("participant") == with.stringValue) - .fetchOne(db) - } - if chat != nil { - return Martin.ChatBase(context: context, jid: with) - } else { - let chat = Chat( - id: UUID().uuidString, - account: context.userBareJid.stringValue, - participant: with.stringValue, - type: .chat - ) - try _db.write { db in - try chat.save(db) - } - return Martin.ChatBase(context: context, jid: with) - } - } catch { - logIt(.error, "Error fetching chat: \(error.localizedDescription)") - return nil - } - } - - func close(chat: any Martin.ChatProtocol) -> Bool { - // not used in Martin library for now - print("Closing chat: \(chat)") - return false - } -} diff --git a/old/AppCore/Database/Database+Martin/Database+MartinRoomManager.swift b/old/AppCore/Database/Database+Martin/Database+MartinRoomManager.swift deleted file mode 100644 index 6a399c4..0000000 --- a/old/AppCore/Database/Database+Martin/Database+MartinRoomManager.swift +++ /dev/null @@ -1,105 +0,0 @@ -import Foundation -import GRDB -import Martin - -extension Database: Martin.RoomManager { - func rooms(for context: Martin.Context) -> [any Martin.RoomProtocol] { - do { - let rooms: [Room] = try _db.read { db in - try Room.filter(Column("account") == context.userBareJid.stringValue).fetchAll(db) - } - return rooms.map { room in - Martin.RoomBase( - context: context, - jid: context.userBareJid, - nickname: room.nickname, - password: room.password, - dispatcher: QueueDispatcher(label: "room-\(room.id)") - ) - } - } catch { - logIt(.error, "Error fetching channels: \(error.localizedDescription)") - return [] - } - } - - func room(for context: Martin.Context, with roomJid: Martin.BareJID) -> (any Martin.RoomProtocol)? { - do { - let room: Room? = try _db.read { db in - try Room - .filter(Column("account") == context.userBareJid.stringValue) - .filter(Column("id") == roomJid.stringValue) - .fetchOne(db) - } - if let room { - return Martin.RoomBase( - context: context, - jid: context.userBareJid, - nickname: room.nickname, - password: room.password, - dispatcher: QueueDispatcher(label: "room-\(room.id)") - ) - } else { - return nil - } - } catch { - logIt(.error, "Error fetching room: \(error.localizedDescription)") - return nil - } - } - - func createRoom(for context: Martin.Context, with roomJid: Martin.BareJID, nickname: String, password: String?) -> (any Martin.RoomProtocol)? { - do { - let room: Room? = try _db.read { db in - try Room - .filter(Column("account") == context.userBareJid.stringValue) - .filter(Column("id") == roomJid.stringValue) - .fetchOne(db) - } - if let room { - return Martin.RoomBase( - context: context, - jid: context.userBareJid, - nickname: room.nickname, - password: room.password, - dispatcher: QueueDispatcher(label: "room-\(room.id)") - ) - } else { - let room = Room( - id: roomJid.stringValue, - account: context.userBareJid.stringValue, - nickname: nickname, - password: password - ) - try _db.write { db in - try room.save(db) - } - return Martin.RoomBase( - context: context, - jid: context.userBareJid, - nickname: nickname, - password: password, - dispatcher: QueueDispatcher(label: "room-\(room.id)") - ) - } - } catch { - logIt(.error, "Error fetching room: \(error.localizedDescription)") - return nil - } - } - - func close(room: any Martin.RoomProtocol) -> Bool { - do { - try _db.write { db in - try Room - .filter(Column("account") == room.context?.userBareJid.stringValue ?? "") - .filter(Column("id") == room.jid.stringValue) - .deleteAll(db) - } - return true - } catch { - logIt(.error, "Error closing room: \(error.localizedDescription)") - return false - } - } -} diff --git a/old/AppCore/Database/Database+Martin/Database+MartinRosterManager.swift b/old/AppCore/Database/Database+Martin/Database+MartinRosterManager.swift deleted file mode 100644 index 194dc29..0000000 --- a/old/AppCore/Database/Database+Martin/Database+MartinRosterManager.swift +++ /dev/null @@ -1,153 +0,0 @@ -import Foundation -import GRDB -import Martin - -extension Database: Martin.RosterManager { - func clear(for context: Martin.Context) { - print("Clearing roster for context: \(context)") - do { - try _db.write { db in - try Roster - .filter(Column("bareJid") == context.userBareJid.stringValue) - .deleteAll(db) - - try RosterVersion - .filter(Column("bareJid") == context.userBareJid.stringValue) - .deleteAll(db) - } - } catch { - logIt(.error, "Error clearing roster: \(error.localizedDescription)") - } - } - - func items(for context: Martin.Context) -> [any Martin.RosterItemProtocol] { - do { - let rosters: [Roster] = try _db.read { db in - try Roster.filter(Column("bareJid") == context.userBareJid.stringValue).fetchAll(db) - } - return rosters.map { roster in - RosterItemBase( - jid: JID(roster.bareJid), - name: roster.name, - subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, - groups: roster.data.groups, - ask: roster.ask, - annotations: roster.data.annotations - ) - } - } catch { - logIt(.error, "Error fetching roster items: \(error.localizedDescription)") - return [] - } - } - - func item(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? { - do { - let roster: Roster? = try _db.read { db in - try Roster - .filter(Column("bareJid") == context.userBareJid.stringValue) - .filter(Column("contactBareJid") == jid.stringValue) - .fetchOne(db) - } - if let roster { - return RosterItemBase( - jid: JID(roster.bareJid), - name: roster.name, - subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, - groups: roster.data.groups, - ask: roster.ask, - annotations: roster.data.annotations - ) - } else { - return nil - } - } catch { - logIt(.error, "Error fetching roster item: \(error.localizedDescription)") - return nil - } - } - - func updateItem(for context: Martin.Context, jid: Martin.JID, name: String?, subscription: Martin.RosterItemSubscription, groups: [String], ask: Bool, annotations: [Martin.RosterItemAnnotation]) -> (any Martin.RosterItemProtocol)? { - do { - let roster = Roster( - bareJid: context.userBareJid.stringValue, - contactBareJid: jid.stringValue, - name: name, - subscription: subscription.rawValue, - ask: ask, - data: DBRosterData( - groups: groups, - annotations: annotations - ) - ) - try _db.write { db in - try roster.save(db) - } - return RosterItemBase(jid: jid, name: name, subscription: subscription, groups: groups, ask: ask, annotations: annotations) - } catch { - logIt(.error, "Error updating roster item: \(error.localizedDescription)") - return nil - } - } - - func deleteItem(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? { - do { - let roster: Roster? = try _db.read { db in - try Roster - .filter(Column("bareJid") == context.userBareJid.stringValue) - .filter(Column("contactBareJid") == jid.stringValue) - .fetchOne(db) - } - if let roster { - _ = try _db.write { db in - try roster.delete(db) - } - return RosterItemBase( - jid: JID(roster.bareJid), - name: roster.name, - subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, - groups: roster.data.groups, - ask: roster.ask, - annotations: roster.data.annotations - ) - } else { - return nil - } - } catch { - logIt(.error, "Error fetching roster version: \(error.localizedDescription)") - return nil - } - } - - func version(for context: Martin.Context) -> String? { - do { - let version: RosterVersion? = try _db.read { db in - try RosterVersion - .filter(Column("bareJid") == context.userBareJid.stringValue) - .fetchOne(db) - } - return version?.version - } catch { - logIt(.error, "Error fetching roster version: \(error.localizedDescription)") - return nil - } - } - - func set(version: String?, for context: Martin.Context) { - guard let version else { return } - do { - try _db.write { db in - let rosterVersion = RosterVersion( - bareJid: context.userBareJid.stringValue, - version: version - ) - try rosterVersion.save(db) - } - } catch { - logIt(.error, "Error setting roster version: \(error.localizedDescription)") - } - } - - func initialize(context _: Martin.Context) {} - func deinitialize(context _: Martin.Context) {} -} diff --git a/old/AppCore/Database/Database+Migrations.swift b/old/AppCore/Database/Database+Migrations.swift deleted file mode 100644 index c0336d3..0000000 --- a/old/AppCore/Database/Database+Migrations.swift +++ /dev/null @@ -1,90 +0,0 @@ -import Foundation -import GRDB - -extension Database { - static var migrator: DatabaseMigrator = { - var migrator = DatabaseMigrator() - - // flush db on schema change (only in DEV mode) - #if DEBUG - migrator.eraseDatabaseOnSchemaChange = true - #endif - - // 1st migration - basic tables - migrator.registerMigration("Add basic tables") { db in - // accounts - try db.create(table: "accounts", options: [.ifNotExists]) { table in - table.column("bareJid", .text).notNull().primaryKey().unique(onConflict: .replace) - table.column("pass", .text).notNull() - table.column("isActive", .boolean).notNull().defaults(to: true) - table.column("isTemp", .boolean).notNull().defaults(to: false) - } - - // rosters - try db.create(table: "rosterVersions", options: [.ifNotExists]) { table in - table.column("bareJid", .text).notNull().primaryKey().unique(onConflict: .replace) - table.column("version", .text).notNull() - } - try db.create(table: "rosters", options: [.ifNotExists]) { table in - table.column("bareJid", .text).notNull() - table.column("contactBareJid", .text).notNull() - table.column("name", .text) - table.column("subscription", .text).notNull() - table.column("ask", .boolean).notNull().defaults(to: false) - table.column("data", .text).notNull() - table.primaryKey(["bareJid", "contactBareJid"], onConflict: .replace) - table.column("locallyDeleted", .boolean).notNull().defaults(to: false) - } - - // chats - try db.create(table: "chats", options: [.ifNotExists]) { table in - table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace) - table.column("account", .text).notNull() - table.column("participant", .text).notNull() - table.column("type", .integer).notNull() - } - - // messages - try db.create(table: "messages", options: [.ifNotExists]) { table in - table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace) - table.column("type", .text).notNull() - table.column("contentType", .text).notNull() - table.column("from", .text).notNull() - table.column("to", .text) - table.column("body", .text) - table.column("subject", .text) - table.column("thread", .text) - table.column("oobUrl", .text) - table.column("date", .datetime).notNull() - table.column("pending", .boolean).notNull() - table.column("sentError", .boolean).notNull() - table.column("attachmentType", .integer) - table.column("attachmentLocalName", .text) - table.column("attachmentRemotePath", .text) - table.column("attachmentThumbnailName", .text) - table.column("attachmentDownloadFailed", .boolean).notNull().defaults(to: false) - } - } - - // 2nd migration - channels/rooms - migrator.registerMigration("Add channels/rooms") { db in - // rooms - try db.create(table: "rooms", options: [.ifNotExists]) { table in - table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace) - table.column("account", .text).notNull() - table.column("nickname", .text).notNull() - table.column("password", .text) - } - - // channels - // try db.create(table: "channels", options: [.ifNotExists]) { table in - // table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace) - // table.column("account", .text).notNull() - // table.column("channel", .text).notNull() - // } - } - - // return migrator - return migrator - }() -} diff --git a/old/AppCore/Database/Database.swift b/old/AppCore/Database/Database.swift deleted file mode 100644 index dc06f02..0000000 --- a/old/AppCore/Database/Database.swift +++ /dev/null @@ -1,55 +0,0 @@ -import Combine -import Foundation -import GRDB -import SwiftUI - -// MARK: - Models protocol -typealias DBStorable = Codable & FetchableRecord & Identifiable & PersistableRecord & TableRecord - -// MARK: - Database init -final class Database { - static let shared = Database() - let _db: DatabaseQueue - - private init() { - do { - // Create db folder if not exists - let fileManager = FileManager.default - let appSupportURL = try fileManager.url( - for: .applicationSupportDirectory, in: .userDomainMask, - appropriateFor: nil, create: true - ) - let directoryURL = appSupportURL.appendingPathComponent("ConversationsClassic", isDirectory: true) - try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true) - - // Open or create the database - let databaseURL = directoryURL.appendingPathComponent("db.sqlite") - _db = try DatabaseQueue(path: databaseURL.path, configuration: Database.config) - - // Some debug info - #if DEBUG - print("Database path: \(databaseURL.path)") - #endif - - // Apply migrations - try Database.migrator.migrate(_db) - } catch { - fatalError("Database initialization failed: \(error)") - } - } -} - -// MARK: - Config -private extension Database { - static let config: Configuration = { - var config = Configuration() - #if DEBUG - // verbose and debugging in DEBUG builds only. - config.publicStatementArguments = true - config.prepareDatabase { db in - db.trace { print("SQL> \($0)\n") } - } - #endif - return config - }() -} diff --git a/old/AppCore/Files/DownloadManager.swift b/old/AppCore/Files/DownloadManager.swift deleted file mode 100644 index 67bf06e..0000000 --- a/old/AppCore/Files/DownloadManager.swift +++ /dev/null @@ -1,48 +0,0 @@ -import Foundation - -final class DownloadManager { - static let shared = DownloadManager() - - private let urlSession: URLSession - private let downloadQueue = DispatchQueue(label: "com.example.downloadQueue") - private var activeDownloads = Set() - - init() { - let configuration = URLSessionConfiguration.default - urlSession = URLSession(configuration: configuration) - } - - func enqueueDownload(from url: URL, to localUrl: URL, completion: @escaping (Error?) -> Void) { - downloadQueue.async { - if self.activeDownloads.contains(url) { - print("Download for this file is already in queue.") - return - } - - self.activeDownloads.insert(url) - - let task = self.urlSession.downloadTask(with: url) { tempLocalUrl, _, error in - self.downloadQueue.async { - self.activeDownloads.remove(url) - - guard let tempLocalUrl = tempLocalUrl, error == nil else { - completion(error) - return - } - - do { - if FileManager.default.fileExists(atPath: localUrl.path) { - try FileManager.default.removeItem(at: localUrl) - } - let data = try Data(contentsOf: tempLocalUrl) - try data.write(to: localUrl) - completion(nil) - } catch let writeError { - completion(writeError) - } - } - } - task.resume() - } - } -} diff --git a/old/AppCore/Files/FileProcessing.swift b/old/AppCore/Files/FileProcessing.swift deleted file mode 100644 index 14b0048..0000000 --- a/old/AppCore/Files/FileProcessing.swift +++ /dev/null @@ -1,302 +0,0 @@ -import Foundation -import Photos -import UIKit - -final class FileProcessing { - static let shared = FileProcessing() - - static var fileFolder: URL { - // swiftlint:disable:next force_unwrapping - let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - let subdirectoryURL = documentsURL.appendingPathComponent(Const.fileFolder) - if !FileManager.default.fileExists(atPath: subdirectoryURL.path) { - try? FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil) - } - return subdirectoryURL - } - - func createThumbnail(localName: String) -> String? { - let thumbnailFileName = "thumb_\(localName)" - let thumbnailUrl = FileProcessing.fileFolder.appendingPathComponent(thumbnailFileName) - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - - // check if thumbnail already exists - if FileManager.default.fileExists(atPath: thumbnailUrl.path) { - return thumbnailFileName - } - - // create thumbnail if not exists - switch localName.attachmentType { - case .image: - guard let image = UIImage(contentsOfFile: localUrl.path) else { - print("FileProcessing: Error loading image: \(localUrl)") - return nil - } - let targetSize = CGSize(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - guard let thumbnail = scaleAndCropImage(image, targetSize) else { - print("FileProcessing: Error scaling image: \(localUrl)") - return nil - } - guard let data = thumbnail.pngData() else { - print("FileProcessing: Error converting thumbnail of \(localUrl) to data") - return nil - } - do { - try data.write(to: thumbnailUrl) - return thumbnailFileName - } catch { - print("FileProcessing: Error writing thumbnail: \(error)") - return nil - } - - default: - return nil - } - } - - func fetchGallery() -> [SharingGalleryItem] { - let items = syncGalleryEnumerate() - .map { - SharingGalleryItem( - id: $0.localIdentifier, - type: $0.mediaType == .image ? .photo : .video, - duration: $0.mediaType == .video ? $0.duration.minAndSec : nil - ) - } - return items - } - - func fillGalleryItemsThumbnails(items: [SharingGalleryItem]) -> [SharingGalleryItem] { - let ids = items - .filter { $0.thumbnail == nil } - .map { $0.id } - - let assets = syncGalleryEnumerate(ids) - return assets.compactMap { asset in - if asset.mediaType == .image { - return syncGalleryProcessImage(asset) { [weak self] image in - if let thumbnail = self?.scaleAndCropImage(image, CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { - let data = thumbnail.jpegData(compressionQuality: 1.0) ?? Data() - return SharingGalleryItem(id: asset.localIdentifier, type: .photo, thumbnail: data) - } else { - return nil - } - } - } else if asset.mediaType == .video { - return syncGalleryProcessVideo(asset) { [weak self] avAsset in - // swiftlint:disable:next force_cast - let assetURL = avAsset as! AVURLAsset - let url = assetURL.url - if let thumbnail = self?.generateVideoThumbnail(url, CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { - let data = thumbnail.jpegData(compressionQuality: 1.0) ?? Data() - return SharingGalleryItem( - id: asset.localIdentifier, - type: .video, - thumbnail: data, - duration: asset.duration.minAndSec - ) - } else { - return nil - } - } - } else { - return nil - } - } - } - - // This function also creates new ids for messages for each new attachment - func copyGalleryItemsForUploading(items: [SharingGalleryItem]) -> [(String, String)] { - let assets = syncGalleryEnumerate(items.map { $0.id }) - return assets - .compactMap { asset in - let newMessageId = UUID().uuidString - let fileId = UUID().uuidString - if asset.mediaType == .image { - return syncGalleryProcessImage(asset) { image in - let localName = "\(newMessageId)_\(fileId).jpg" - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - if let data = image.jpegData(compressionQuality: 1.0) { - do { - try data.write(to: localUrl) - return (newMessageId, localName) - } catch { - return nil - } - } else { - return nil - } - } - } else if asset.mediaType == .video { - return syncGalleryProcessVideo(asset) { avAsset in - // swiftlint:disable:next force_cast - let assetURL = avAsset as! AVURLAsset - let url = assetURL.url - let localName = "\(newMessageId)_\(fileId).mov" - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - do { - try FileManager.default.copyItem(at: url, to: localUrl) - return (newMessageId, localName) - } catch { - return nil - } - } - } else { - return nil - } - } - } - - // This function also creates new id for file from camera capturing - func copyCameraCapturedForUploading(media: Data, type: SharingCameraMediaType) -> (String, String)? { - let newMessageId = UUID().uuidString - let fileId = UUID().uuidString - let localName: String - switch type { - case .photo: - localName = "\(newMessageId)_\(fileId).jpg" - - case .video: - localName = "\(newMessageId)_\(fileId).mov" - } - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - do { - try media.write(to: localUrl) - return (newMessageId, localName) - } catch { - return nil - } - } - - // This function also creates new id for file from document sharing - func copyDocumentsForUploading(data: [Data], extensions: [String]) -> [(String, String)] { - data.enumerated().compactMap { index, data in - let newMessageId = UUID().uuidString - let fileId = UUID().uuidString - let localName = "\(newMessageId)_\(fileId).\(extensions[index])" - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - do { - try data.write(to: localUrl) - return (newMessageId, localName) - } catch { - print("FileProcessing: Error writing document: \(error)") - return nil - } - } - } -} - -private extension FileProcessing { - func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? { - let aspect = img.size.width / img.size.height - let targetAspect = size.width / size.height - var newWidth: CGFloat - var newHeight: CGFloat - if aspect < targetAspect { - newWidth = size.width - newHeight = size.width / aspect - } else { - newHeight = size.height - newWidth = size.height * aspect - } - - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) - img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight)) - let newImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - return newImage - } - - func syncGalleryEnumerate(_ ids: [String]? = nil) -> [PHAsset] { - var result: [PHAsset] = [] - - let group = DispatchGroup() - DispatchQueue.global(qos: .userInitiated).sync { - let fetchOptions = PHFetchOptions() - fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] - if let ids { - fetchOptions.predicate = NSPredicate(format: "localIdentifier IN %@", ids) - } - let assets = PHAsset.fetchAssets(with: fetchOptions) - assets.enumerateObjects { asset, _, _ in - group.enter() - result.append(asset) - group.leave() - } - } - group.wait() - return result - } - - func syncGalleryProcess(_ assets: [PHAsset], _ block: @escaping (PHAsset) -> T) -> [T] { - var result: [T] = [] - let group = DispatchGroup() - DispatchQueue.global(qos: .userInitiated).sync { - for asset in assets { - group.enter() - let res = block(asset) - result.append(res) - group.leave() - } - } - group.wait() - return result - } - - func syncGalleryProcessImage(_ asset: PHAsset, _ block: @escaping (UIImage) -> T?) -> T? { - var result: T? - let semaphore = DispatchSemaphore(value: 0) - DispatchQueue.global(qos: .userInitiated).sync { - let options = PHImageRequestOptions() - options.version = .original - options.isSynchronous = true - PHImageManager.default().requestImage( - for: asset, - targetSize: PHImageManagerMaximumSize, - contentMode: .aspectFill, - options: options - ) { image, _ in - if let image { - result = block(image) - } else { - result = nil - } - semaphore.signal() - } - } - semaphore.wait() - return result - } - - func syncGalleryProcessVideo(_ asset: PHAsset, _ block: @escaping (AVAsset) -> T?) -> T? { - var result: T? - let semaphore = DispatchSemaphore(value: 0) - _ = DispatchQueue.global(qos: .userInitiated).sync { - PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in - if let avAsset { - result = block(avAsset) - } else { - result = nil - } - semaphore.signal() - } - } - semaphore.wait() - return result - } - - func generateVideoThumbnail(_ url: URL, _ size: CGSize) -> UIImage? { - let asset = AVAsset(url: url) - let assetImgGenerate = AVAssetImageGenerator(asset: asset) - assetImgGenerate.appliesPreferredTrackTransform = true - let time = CMTimeMakeWithSeconds(Float64(1), preferredTimescale: 600) - do { - let cgImage = try assetImgGenerate.copyCGImage(at: time, actualTime: nil) - let image = UIImage(cgImage: cgImage) - return scaleAndCropImage(image, size) - } catch { - return nil - } - } -} diff --git a/old/AppCore/Middlewares/AccountsMiddleware.swift b/old/AppCore/Middlewares/AccountsMiddleware.swift deleted file mode 100644 index 04eb2db..0000000 --- a/old/AppCore/Middlewares/AccountsMiddleware.swift +++ /dev/null @@ -1,69 +0,0 @@ -import Combine -import Foundation - -final class AccountsMiddleware { - static let shared = AccountsMiddleware() - - private lazy var allFeatures: [ServerFeature] = { - guard - let url = Bundle.main.url(forResource: "server_features", withExtension: "plist"), - let data = try? Data(contentsOf: url), - let loaded = try? PropertyListDecoder().decode([ServerFeature].self, from: data) - else { - return [] - } - return loaded - }() - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .databaseAction(.storedAccountsLoaded(let accounts)): - return Just(.accountsAction(.accountsListUpdated(accounts: accounts))) - .eraseToAnyPublisher() - - case .xmppAction(.clientConnectionChanged(let jid, let connectionStatus)): - return Deferred { - Future { promise in - guard let account = state.accountsState.accounts.first(where: { $0.bareJid == jid }) else { - promise(.success(.info("AccountsMiddleware: account not found for jid \(jid)"))) - return - } - if account.isTemp { - switch connectionStatus { - case .connected: - promise(.success(.accountsAction(.makeAccountPermanent(account: account)))) - - case .disconnected(let reason): - if reason != "No error!" { - promise(.success(.accountsAction(.addAccountError(jid: jid, reason: reason)))) - } else { - promise(.success(.info("AccountsMiddleware: account \(jid) disconnected with no error"))) - } - - default: - promise(.success(.info("AccountsMiddleware: account \(jid) connection status changed to \(connectionStatus)"))) - } - } else { - promise(.success(.info("AccountsMiddleware: account \(jid) is not temporary, ignoring"))) - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.serverFeaturesLoaded(let jid, let features)): - return Deferred { - Future { [weak self] promise in - let serverFeatures = features - .compactMap { featureId in - self?.allFeatures.first(where: { $0.xmppId == featureId }) - } - promise(.success(.accountsAction(.clientServerFeaturesUpdated(jid: jid, features: serverFeatures)))) - } - } - .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/ArchivedMessagesMiddleware.swift b/old/AppCore/Middlewares/ArchivedMessagesMiddleware.swift deleted file mode 100644 index bf0aa08..0000000 --- a/old/AppCore/Middlewares/ArchivedMessagesMiddleware.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Combine -import Foundation - -final class ArchivedMessagesMiddleware { - static let shared = ArchivedMessagesMiddleware() - - func middleware(state _: AppState, action: AppAction) -> AnyPublisher { - switch action { - // case .conversationAction(.messagesUpdated(let messages)): - // if state.conversationsState.archivedMessagesRequested { - // return Empty().eraseToAnyPublisher() - // } else { - // guard let chat = state.conversationsState.currentChat else { - // return Empty().eraseToAnyPublisher() - // } - // return Deferred { - // Future { promise in - // if let currentClient = state.accountsState.accounts.first(where: { $0.bareJid == chat.account }) { - // let features = state.accountsState.discoFeatures[currentClient.bareJid] ?? [] - // if features.map({ $0.xep }).contains("XEP-0313") { - // let roster = state.conversationsState.currentRoster - // let date = self.archivesRequestDate(from: messages) - // promise(.success(.xmppAction(.xmppLoadArchivedMessages(jid: currentClient.bareJid, to: roster?.bareJid, fromDate: date)))) - // } else { - // promise(.success(.info("MessageMiddleware: XEP-0313 not supported for client \(currentClient.bareJid)"))) - // } - // } else { - // promise(.success(.info("MessageMiddleware: No client found for account \(chat.account), probably some error here"))) - // } - // } - // } - // .eraseToAnyPublisher() - // } - - default: - return Empty().eraseToAnyPublisher() - } - } -} - -private extension ArchivedMessagesMiddleware { - func archivesRequestDate(from messages: [Message]) -> Date { - if let lastDate = messages.first?.date { - return lastDate - } else { - return Calendar.current.date(byAdding: .day, value: -Const.mamRequestDaysLength, to: Date()) ?? Date() - } - } -} diff --git a/old/AppCore/Middlewares/ChatsMiddleware.swift b/old/AppCore/Middlewares/ChatsMiddleware.swift deleted file mode 100644 index 797f93d..0000000 --- a/old/AppCore/Middlewares/ChatsMiddleware.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Combine - -final class ChatsMiddleware { - static let shared = ChatsMiddleware() - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .databaseAction(.storedChatsLoaded(let chats)): - return Just(.chatsAction(.chatsListUpdated(chats: chats))) - .eraseToAnyPublisher() - - case .chatsAction(.startChat(accountJid: let accountJid, participantJid: let participantJid)): - return Deferred { - Future { promise in - if let exist = state.chatsState.chats.first(where: { $0.account == accountJid && $0.participant == participantJid }) { - // open existing chat - promise(.success(.chatsAction(.chatStarted(chat: exist)))) - } else { - // create new chat - promise(.success(.chatsAction(.createNewChat(accountJid: accountJid, participantJid: participantJid)))) - } - } - } - .eraseToAnyPublisher() - - case .chatsAction(.chatCreated(let chat)): - return Just(.chatsAction(.chatStarted(chat: chat))) - .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/ConversationMiddleware.swift b/old/AppCore/Middlewares/ConversationMiddleware.swift deleted file mode 100644 index e03839d..0000000 --- a/old/AppCore/Middlewares/ConversationMiddleware.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Combine - -final class ConversationMiddleware { - static let shared = ConversationMiddleware() - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .chatsAction(.chatStarted(let chat)): - return Deferred { - Future { promise in - let roster = state.rostersState.rosters - .first { $0.bareJid == chat.account && $0.contactBareJid == chat.participant } - promise(.success(.conversationAction(.makeConversationActive(chat: chat, roster: roster)))) - } - } - .eraseToAnyPublisher() - - case .conversationAction(.makeConversationActive): - return Just(AppAction.changeFlow(.conversation)).eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/DatabaseMiddleware.swift b/old/AppCore/Middlewares/DatabaseMiddleware.swift deleted file mode 100644 index c14dce8..0000000 --- a/old/AppCore/Middlewares/DatabaseMiddleware.swift +++ /dev/null @@ -1,530 +0,0 @@ -import Combine -import Foundation -import GRDB - -// swiftlint:disable:next type_body_length -final class DatabaseMiddleware { - static let shared = DatabaseMiddleware() - private let database = Database.shared - private var cancellables: Set = [] - private var conversationCancellables: Set = [] - - private init() { - // Database changes - ValueObservation - .tracking(Roster.fetchAll) - .publisher(in: database._db, scheduling: .immediate) - .sink { _ in - // Handle completion - } receiveValue: { rosters in - DispatchQueue.main.async { - store.dispatch(.databaseAction(.storedRostersLoaded(rosters: rosters))) - } - } - .store(in: &cancellables) - ValueObservation - .tracking(Chat.fetchAll) - .publisher(in: database._db, scheduling: .immediate) - .sink { _ in - // Handle completion - } receiveValue: { chats in - DispatchQueue.main.async { - store.dispatch(.databaseAction(.storedChatsLoaded(chats: chats))) - } - } - .store(in: &cancellables) - } - - // swiftlint:disable:next function_body_length cyclomatic_complexity - func middleware(state _: AppState, action: AppAction) -> AnyPublisher { - switch action { - // MARK: Accounts - case .startAction(.loadStoredAccounts): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.loadingStoredAccountsFailed))) - return - } - do { - try database._db.read { db in - let accounts = try Account.fetchAll(db) - promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts)))) - } - } catch { - promise(.success(.databaseAction(.loadingStoredAccountsFailed))) - } - } - } - } - .eraseToAnyPublisher() - - case .accountsAction(.makeAccountPermanent(let account)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAccountFailed))) - return - } - do { - try database._db.write { db in - // make permanent and store to database - var acc = account - acc.isTemp = false - try acc.insert(db) - - // Re-Fetch all accounts - let accounts = try Account.fetchAll(db) - - // Use the accounts - promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts)))) - } - } catch { - promise(.success(.databaseAction(.updateAccountFailed))) - } - } - } - } - .eraseToAnyPublisher() - - // MARK: Rosters - case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - _ = try database._db.write { db in - try Roster - .filter(Column("bareJid") == ownerJID) - .filter(Column("contactBareJid") == contactJID) - .updateAll(db, Column("locallyDeleted").set(to: true)) - } - promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) marked as locally deleted"))) - } catch { - promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError)))) - } - } - } - } - .eraseToAnyPublisher() - - case .rostersAction(.unmarkRosterAsLocallyDeleted(let ownerJID, let contactJID)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - _ = try database._db.write { db in - try Roster - .filter(Column("bareJid") == ownerJID) - .filter(Column("contactBareJid") == contactJID) - .updateAll(db, Column("locallyDeleted").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) unmarked as locally deleted"))) - } catch { - promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError)))) - } - } - } - } - .eraseToAnyPublisher() - - // MARK: Chats - case .chatsAction(.createNewChat(let accountJid, let participantJid)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - try database._db.write { db in - let chat = Chat( - id: UUID().uuidString, - account: accountJid, - participant: participantJid, - type: .chat - ) - try chat.insert(db) - promise(.success(.chatsAction(.chatCreated(chat: chat)))) - } - } catch { - promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError)))) - } - } - } - } - .eraseToAnyPublisher() - - // MARK: Conversation and messages - case .conversationAction(.makeConversationActive(let chat, _)): - subscribeToMessages(chat: chat) - return Empty().eraseToAnyPublisher() - - case .xmppAction(.xmppMessageReceived(let message)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - guard message.contentType != .typing, message.body != nil else { - promise(.success(.info("DatabaseMiddleware: message \(message.id) received as 'typing...' or message body is nil"))) - return - } - do { - try database._db.write { db in - try message.insert(db) - } - promise(.success(.info("DatabaseMiddleware: message \(message.id) stored in db"))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))) - } - } - } - } - .eraseToAnyPublisher() - - case .conversationAction(.sendMessage(let from, let to, let body)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - let message = Message( - id: UUID().uuidString, - type: .chat, - contentType: .text, - from: from, - to: to, - body: body, - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), - pending: true, - sentError: false - ) - try database._db.write { db in - try message.insert(db) - } - promise(.success(.xmppAction(.xmppMessageSent(message)))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))) - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppMessageSendSuccess(let msgId)): - // mark message as pending false and sentError false - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == msgId) - .updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as sent"))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppMessageSendFailed(let msgId)): - // mark message as pending false and sentError true - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == msgId) - .updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true)) - } - promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as failed to send"))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))) - } - } - } - } - .eraseToAnyPublisher() - - // MARK: Attachments - case .fileAction(.downloadAttachmentFile(let id, _)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == id) - .updateAll(db, Column("attachmentDownloadFailed").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as starting downloading attachment"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .fileAction(.downloadingAttachmentFileFailed(let id, _)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == id) - .updateAll(db, Column("attachmentDownloadFailed").set(to: true)) - } - promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as failed to download attachment"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .fileAction(.attachmentFileDownloaded(let id, let localName)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == id) - .updateAll(db, Column("attachmentLocalName").set(to: localName), Column("attachmentDownloadFailed").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as downloaded attachment"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .fileAction(.attachmentThumbnailCreated(let id, let thumbnailName)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == id) - .updateAll(db, Column("attachmentThumbnailName").set(to: thumbnailName)) - } - promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as thumbnail created"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - // MARK: Sharing - case .conversationAction(.sendMediaMessages(let from, let to, let messageIds, let localFilesNames)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - for (index, id) in messageIds.enumerated() { - let message = Message( - id: id, - type: .chat, - contentType: .attachment, - from: from, - to: to, - body: nil, - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), - pending: true, - sentError: false, - attachmentType: localFilesNames[index].attachmentType, - attachmentLocalName: localFilesNames[index] - ) - try database._db.write { db in - try message.insert(db) - } - } - promise(.success(.info("DatabaseMiddleware: messages with sharings stored in db"))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .sharingAction(.retrySharing(let id)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == id) - .updateAll(db, Column("pending").set(to: true), Column("sentError").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: message \(id) with shares marked in db as pending to send"))) - } catch { - promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppSharingUploadSuccess(let messageId, let remotePath)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == messageId) - .updateAll(db, Column("attachmentRemotePath").set(to: remotePath), Column("pending").set(to: false), Column("sentError").set(to: false)) - } - promise(.success(.info("DatabaseMiddleware: shared file uploaded and message \(messageId) marked in db as sent"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppSharingUploadFailed(let messageId, _)): - return Deferred { - Future { promise in - Task(priority: .background) { [weak self] in - guard let database = self?.database else { - promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError))) - ) - return - } - do { - _ = try database._db.write { db in - try Message - .filter(Column("id") == messageId) - .updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true)) - } - promise(.success(.info("DatabaseMiddleware: shared file upload failed and message \(messageId) marked in db as failed to send"))) - } catch { - promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription))) - ) - } - } - } - } - .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} - -private extension DatabaseMiddleware { - func subscribeToMessages(chat: Chat) { - conversationCancellables = [] - ValueObservation - .tracking( - Message - .filter( - (Column("to") == chat.account && Column("from") == chat.participant) || - (Column("from") == chat.account && Column("to") == chat.participant) - ) - .order(Column("date").desc) - .fetchAll - ) - .publisher(in: database._db, scheduling: .immediate) - .sink { _ in - } receiveValue: { messages in - // messages - DispatchQueue.main.async { - store.dispatch(.conversationAction(.messagesUpdated(messages: messages))) - } - } - .store(in: &conversationCancellables) - } -} diff --git a/old/AppCore/Middlewares/FileMiddleware.swift b/old/AppCore/Middlewares/FileMiddleware.swift deleted file mode 100644 index 8237ac2..0000000 --- a/old/AppCore/Middlewares/FileMiddleware.swift +++ /dev/null @@ -1,141 +0,0 @@ -import Combine -import Foundation -import UIKit - -final class FileMiddleware { - static let shared = FileMiddleware() - private var downloadingMessageIDs = ThreadSafeSet() - - func middleware(state _: AppState, action: AppAction) -> AnyPublisher { - switch action { - // MARK: - For incomig attachments - case .conversationAction(.messagesUpdated(let messages)): - return Deferred { - Future { [weak self] promise in - guard let wSelf = self else { - promise(.success(.info("FileMiddleware: on checking attachments/shares messages, middleware self is nil"))) - return - } - - // for incoming messages with attachments - for message in messages where message.attachmentRemotePath != nil && message.attachmentLocalPath == nil { - if wSelf.downloadingMessageIDs.contains(message.id) { - continue - } - wSelf.downloadingMessageIDs.insert(message.id) - DispatchQueue.main.async { - // swiftlint:disable:next force_unwrapping - store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: message.attachmentRemotePath!))) - } - } - - // for outgoing messages with shared attachments - for message in messages where message.attachmentLocalPath != nil && message.attachmentRemotePath == nil && message.pending { - DispatchQueue.main.async { - store.dispatch(.xmppAction(.xmppSharingTryUpload(message))) - } - } - - // for outgoing messages with shared attachments which are already uploaded - // but have no thumbnail (only for images) - for message in messages where !message.pending && !message.sentError && message.attachmentType == .image { - if message.attachmentLocalName != nil && message.attachmentRemotePath != nil && message.attachmentThumbnailName == nil { - DispatchQueue.main.async { - // swiftlint:disable:next force_unwrapping - store.dispatch(.fileAction(.createAttachmentThumbnail(messageId: message.id, localName: message.attachmentLocalName!))) - } - } - } - - promise(.success(.info("FileMiddleware: attachments/shares messages processed"))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.downloadAttachmentFile(let id, let attachmentRemotePath)): - return Deferred { - Future { promise in - let localName = "\(id)_\(UUID().uuidString)\(attachmentRemotePath.lastPathComponent)" - let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName) - DownloadManager.shared.enqueueDownload(from: attachmentRemotePath, to: localUrl) { error in - DispatchQueue.main.async { - if let error { - store.dispatch(.fileAction(.downloadingAttachmentFileFailed(messageId: id, reason: error.localizedDescription))) - } else { - store.dispatch(.fileAction(.attachmentFileDownloaded(messageId: id, localName: localName))) - } - } - } - promise(.success(.info("FileMiddleware: started downloading attachment for message \(id)"))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.attachmentFileDownloaded(let id, let localName)): - return Deferred { - Future { [weak self] promise in - self?.downloadingMessageIDs.remove(id) - promise(.success(.fileAction(.createAttachmentThumbnail(messageId: id, localName: localName)))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.createAttachmentThumbnail(let id, let localName)): - return Deferred { - Future { [weak self] promise in - if let thumbnailName = FileProcessing.shared.createThumbnail(localName: localName) { - self?.downloadingMessageIDs.remove(id) - promise(.success(.fileAction(.attachmentThumbnailCreated(messageId: id, thumbnailName: thumbnailName)))) - } else { - self?.downloadingMessageIDs.remove(id) - promise(.success(.info("FileMiddleware: failed to create thumbnail from \(localName) for message \(id)"))) - } - } - } - .eraseToAnyPublisher() - - // MARK: - For outgoing sharing - case .fileAction(.fetchItemsFromGallery): - return Deferred { - Future { promise in - let items = FileProcessing.shared.fetchGallery() - promise(.success(.fileAction(.itemsFromGalleryFetched(items: items)))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.itemsFromGalleryFetched(let items)): - return Deferred { - Future { promise in - let newItems = FileProcessing.shared.fillGalleryItemsThumbnails(items: items) - promise(.success(.sharingAction(.galleryItemsUpdated(items: newItems)))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.copyGalleryItemsForUploading(let items)): - return Deferred { - Future { promise in - let ids = FileProcessing.shared.copyGalleryItemsForUploading(items: items) - promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 })))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.copyCameraCapturedForUploading(let media, let type)): - return Deferred { - Future { promise in - if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) { - promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName])))) - } else { - promise(.success(.info("FileMiddleware: failed to copy camera captured media for uploading"))) - } - } - } - .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/LoggerMiddleware.swift b/old/AppCore/Middlewares/LoggerMiddleware.swift deleted file mode 100644 index f9fec20..0000000 --- a/old/AppCore/Middlewares/LoggerMiddleware.swift +++ /dev/null @@ -1,69 +0,0 @@ -import Combine -import Foundation -import SwiftUI - -let isConsoleLoggingEnabled = false - -#if DEBUG - let prefixLength = 400 - func loggerMiddleware() -> Middleware { - { state, action in - let timeStr = dateFormatter.string(from: Date()) - var actionStr = "\(action)" - actionStr = String(actionStr.prefix(prefixLength)) + " ..." - var stateStr = "\(state)" - stateStr = String(stateStr.prefix(prefixLength)) + " ..." - - let str = "\(timeStr) \u{EA86} \(actionStr)\n\(timeStr) \u{F129} \(stateStr)\n" - - print(str) - if isConsoleLoggingEnabled { - NSLog(str) - } - return Empty().eraseToAnyPublisher() - } - } -#else - func loggerMiddleware() -> Middleware { - { _, _ in - Empty().eraseToAnyPublisher() - } - } -#endif - -enum LogLevels: String { - case info = "\u{F449}" - case warning = "\u{F071}" - case error = "\u{EA76}" -} - -// For database errors logging -func logIt(_ level: LogLevels, _ message: String) { - #if DEBUG - let timeStr = dateFormatter.string(from: Date()) - let str = "\(timeStr) \(level.rawValue) \(message)" - print(str) - if isConsoleLoggingEnabled { - NSLog(str) - } - #endif -} - -private var dateFormatter: DateFormatter { - let formatter = DateFormatter() - formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale - formatter.dateFormat = "MM-dd HH:mm:ss.SSS" - return formatter -} - -// For thread debugging -func ptInfo(_ message: String) { - #if DEBUG - let timeStr = dateFormatter.string(from: Date()) - let str = "\(timeStr) \(message) -> \(Thread.current), \(String(validatingUTF8: __dispatch_queue_get_label(nil)) ?? "no queue label")" - print(str) - if isConsoleLoggingEnabled { - NSLog(str) - } - #endif -} diff --git a/old/AppCore/Middlewares/RostersMiddleware.swift b/old/AppCore/Middlewares/RostersMiddleware.swift deleted file mode 100644 index e253589..0000000 --- a/old/AppCore/Middlewares/RostersMiddleware.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Combine - -final class RostersMiddleware { - static let shared = RostersMiddleware() - - func middleware(state _: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .databaseAction(.storedRostersLoaded(let rosters)): - return Just(.rostersAction(.rostersListUpdated(rosters))) - .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/SharingMiddleware.swift b/old/AppCore/Middlewares/SharingMiddleware.swift deleted file mode 100644 index 7d6e788..0000000 --- a/old/AppCore/Middlewares/SharingMiddleware.swift +++ /dev/null @@ -1,131 +0,0 @@ -import AVFoundation -import Combine -import Foundation -import Photos -import UIKit - -final class SharingMiddleware { - static let shared = SharingMiddleware() - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - // MARK: - Camera and Gallery Access - case .sharingAction(.checkCameraAccess): - return Deferred { - Future { promise in - let status = AVCaptureDevice.authorizationStatus(for: .video) - switch status { - case .authorized: - promise(.success(.sharingAction(.setCameraAccess(true)))) - - case .notDetermined: - AVCaptureDevice.requestAccess(for: .video) { granted in - promise(.success(.sharingAction(.setCameraAccess(granted)))) - } - - case .denied, .restricted: - promise(.success(.sharingAction(.setCameraAccess(false)))) - - @unknown default: - promise(.success(.sharingAction(.setCameraAccess(false)))) - } - } - } - .eraseToAnyPublisher() - - case .sharingAction(.checkGalleryAccess): - return Deferred { - Future { promise in - let status = PHPhotoLibrary.authorizationStatus() - switch status { - case .authorized, .limited: - promise(.success(.sharingAction(.setGalleryAccess(true)))) - - case .notDetermined: - PHPhotoLibrary.requestAuthorization { status in - promise(.success(.sharingAction(.setGalleryAccess(status == .authorized)))) - } - - case .denied, .restricted: - promise(.success(.sharingAction(.setGalleryAccess(false)))) - - @unknown default: - promise(.success(.sharingAction(.setGalleryAccess(false)))) - } - } - } - .eraseToAnyPublisher() - - case .fileAction(.itemsFromGalleryFetched(let items)): - return Just(.sharingAction(.galleryItemsUpdated(items: items))) - .eraseToAnyPublisher() - - // MARK: - Sharing - case .sharingAction(.shareMedia(let ids)): - return Deferred { - Future { promise in - let items = state.sharingState.galleryItems.filter { ids.contains($0.id) } - promise(.success(.fileAction(.copyGalleryItemsForUploading(items: items)))) - } - } - .eraseToAnyPublisher() - - case .fileAction(.itemsCopiedForUploading(let newMessageIds, let localNames)): - if let chat = state.conversationsState.currentChat { - return Just(.conversationAction(.sendMediaMessages( - from: chat.account, - to: chat.participant, - messagesIds: newMessageIds, - localFilesNames: localNames - ))) - .eraseToAnyPublisher() - } else { - return Empty().eraseToAnyPublisher() - } - - case .sharingAction(.cameraCaptured(let media, let type)): - return Deferred { - Future { promise in - if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) { - promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName]))) - ) - } else { - promise(.success(.info("SharingMiddleware: camera's captured file didn't copied"))) - } - } - } - .eraseToAnyPublisher() - - case .sharingAction(.shareLocation(let lat, let lon)): - if let chat = state.conversationsState.currentChat { - let msg = "geo:\(lat),\(lon)" - return Just(.conversationAction(.sendMessage(from: chat.account, to: chat.participant, body: msg))) - .eraseToAnyPublisher() - } else { - return Empty().eraseToAnyPublisher() - } - - case .sharingAction(.shareDocuments(let data, let extensions)): - return Deferred { - Future { promise in - let ids = FileProcessing.shared.copyDocumentsForUploading(data: data, extensions: extensions) - promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 }))) - ) - } - } - .eraseToAnyPublisher() - - case .sharingAction(.shareContact(let jid)): - if let chat = state.conversationsState.currentChat { - let msg = "contact:\(jid)" - return Just(.conversationAction(.sendMessage(from: chat.account, to: chat.participant, body: msg))) - .eraseToAnyPublisher() - } else { - return Empty().eraseToAnyPublisher() - } - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/StartMiddleware.swift b/old/AppCore/Middlewares/StartMiddleware.swift deleted file mode 100644 index 1a084b6..0000000 --- a/old/AppCore/Middlewares/StartMiddleware.swift +++ /dev/null @@ -1,32 +0,0 @@ -import Combine - -final class StartMiddleware { - static let shared = StartMiddleware() - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .accountsAction(.accountsListUpdated(let accounts)): - if accounts.isEmpty { - if state.currentFlow == .start { - return Just(.startAction(.goTo(.welcomeScreen))) - .eraseToAnyPublisher() - } else { - return Empty().eraseToAnyPublisher() - } - } else { - if state.currentFlow == .accounts, state.accountsState.navigation == .addAccount { - return Just(.changeFlow(.chats)) - .eraseToAnyPublisher() - } else if state.currentFlow == .start { - return Just(.changeFlow(.chats)) - .eraseToAnyPublisher() - } else { - return Empty().eraseToAnyPublisher() - } - } - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Middlewares/XMPPMiddleware.swift b/old/AppCore/Middlewares/XMPPMiddleware.swift deleted file mode 100644 index 4f98a0b..0000000 --- a/old/AppCore/Middlewares/XMPPMiddleware.swift +++ /dev/null @@ -1,160 +0,0 @@ -import Combine -import Foundation -import Martin - -final class XMPPMiddleware { - static let shared = XMPPMiddleware() - private let service = XMPPService(manager: Database.shared) - private var cancellables: Set = [] - private var uploadingMessageIDs = ThreadSafeSet() - - private init() { - service.clientState.sink { client, state in - let jid = client.userBareJid.stringValue - let status = ConnectionStatus.from(state) - let action = AppAction.xmppAction(.clientConnectionChanged(jid: jid, state: status)) - DispatchQueue.main.async { - store.dispatch(action) - } - } - .store(in: &cancellables) - - service.clientMessages.sink { _, martinMessage in - guard let message = Message.map(martinMessage) else { return } - DispatchQueue.main.async { - store.dispatch(.xmppAction(.xmppMessageReceived(message))) - } - } - .store(in: &cancellables) - - service.clientFeatures.sink { client, features in - let jid = client.userBareJid.stringValue - DispatchQueue.main.async { - store.dispatch(.xmppAction(.serverFeaturesLoaded(jid: jid, features: features))) - } - } - .store(in: &cancellables) - } - - func middleware(state: AppState, action: AppAction) -> AnyPublisher { - switch action { - case .accountsAction(.tryAddAccountWithCredentials): - return Deferred { - Future { [weak self] promise in - self?.service.updateClients(for: state.accountsState.accounts) - promise(.success(.info("XMPPMiddleware: clients updated in XMPP service"))) - } - } - .eraseToAnyPublisher() - - case .accountsAction(.addAccountError): - return Deferred { - Future { [weak self] promise in - self?.service.updateClients(for: state.accountsState.accounts) - promise(.success(.info("XMPPMiddleware: clients updated in XMPP service"))) - } - } - .eraseToAnyPublisher() - - case .databaseAction(.storedAccountsLoaded(let accounts)): - return Deferred { - Future { [weak self] promise in - self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp }) - promise(.success(.info("XMPPMiddleware: clients updated in XMPP service"))) - } - } - .eraseToAnyPublisher() - - case .rostersAction(.addRoster(let ownerJID, let contactJID, let name, let groups)): - return Deferred { - Future { [weak self] promise in - guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else { - return promise(.success(.rostersAction(.addRosterError(reason: XMPPError.item_not_found.localizedDescription)))) - } - let module = client.modulesManager.module(RosterModule.self) - module.addItem(jid: JID(contactJID), name: name, groups: groups, completionHandler: { result in - switch result { - case .success: - promise(.success(.rostersAction(.addRosterDone(jid: contactJID)))) - - case .failure(let error): - promise(.success(.rostersAction(.addRosterError(reason: error.localizedDescription)))) - } - }) - } - } - .eraseToAnyPublisher() - - case .rostersAction(.deleteRoster(let ownerJID, let contactJID)): - return Deferred { - Future { [weak self] promise in - guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else { - return promise(.success(.rostersAction(.rosterDeletingFailed(reason: XMPPError.item_not_found.localizedDescription)))) - } - let module = client.modulesManager.module(RosterModule.self) - module.removeItem(jid: JID(contactJID), completionHandler: { result in - switch result { - case .success: - promise(.success(.info("XMPPMiddleware: roster \(contactJID) deleted from \(ownerJID)"))) - - case .failure(let error): - promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription)))) - } - }) - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppMessageSent(let message)): - return Deferred { - Future { [weak self] promise in - DispatchQueue.global().async { - self?.service.sendMessage(message: message) { done in - if done { - promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id)))) - } else { - promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id)))) - } - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppSharingTryUpload(let message)): - return Deferred { - Future { [weak self] promise in - if self?.uploadingMessageIDs.contains(message.id) ?? false { - return promise(.success(.info("XMPPMiddleware: attachment in message \(message.id) is already in uploading process"))) - } else { - self?.uploadingMessageIDs.insert(message.id) - DispatchQueue.global().async { - self?.service.uploadAttachment(message: message) { error, remotePath in - self?.uploadingMessageIDs.remove(message.id) - if let error { - promise(.success(.xmppAction(.xmppSharingUploadFailed(msgId: message.id, reason: error.localizedDescription)))) - } else { - promise(.success(.xmppAction(.xmppSharingUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath)))) - } - } - } - } - } - } - .eraseToAnyPublisher() - - case .xmppAction(.xmppLoadArchivedMessages(let jid, let to, let fromDate)): - return Empty().eraseToAnyPublisher() - // return Deferred { - // Future { [weak self] promise in - // self?.service.requestArchivedMessages(jid: jid, to: to, fromDate: fromDate) - // promise(.success(.conversationAction(.setArchivedMessagesRequested))) - // } - // } - // .eraseToAnyPublisher() - - default: - return Empty().eraseToAnyPublisher() - } - } -} diff --git a/old/AppCore/Models/Account.swift b/old/AppCore/Models/Account.swift deleted file mode 100644 index 264d195..0000000 --- a/old/AppCore/Models/Account.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation -import GRDB -import Martin -import SwiftUI - -// MARK: - Account -struct Account: DBStorable { - var bareJid: String - var pass: String - var isActive: Bool - var isTemp: Bool // account which is added by user, but not yet logged in - var id: String { bareJid } -} - -extension Account: UniversalInputSelectionElement { - var text: String? { bareJid } - var icon: Image? { nil } -} - -extension Account { - static let databaseTableName = "accounts" -} diff --git a/old/AppCore/Models/Channel.swift b/old/AppCore/Models/Channel.swift deleted file mode 100644 index 9c8275f..0000000 --- a/old/AppCore/Models/Channel.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import GRDB -import Martin -import SwiftUI - -// MARK: - Account -struct Channel: DBStorable { - var id: String - var account: String - var channel: String -} - -extension Channel { - static let channelTableName = "channels" -} diff --git a/old/AppCore/Models/Chat.swift b/old/AppCore/Models/Chat.swift deleted file mode 100644 index 58fc273..0000000 --- a/old/AppCore/Models/Chat.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Foundation -import GRDB - -enum ConversationType: Int, Codable, DatabaseValueConvertible { - case chat = 0 - case room = 1 - case channel = 2 -} - -struct Chat: DBStorable { - static let databaseTableName = "chats" - - var id: String - var account: String - var participant: String - var type: ConversationType -} - -extension Chat: Equatable {} diff --git a/old/AppCore/Models/ConnectionStatus.swift b/old/AppCore/Models/ConnectionStatus.swift deleted file mode 100644 index fab36aa..0000000 --- a/old/AppCore/Models/ConnectionStatus.swift +++ /dev/null @@ -1,27 +0,0 @@ -// This struct is simpliest variant of Martin's Client State. -// Just for more comfortable using in App State -import Foundation -import Martin - -enum ConnectionStatus: Stateable { - case connecting - case connected(resumed: Bool = false) - case disconnecting - case disconnected(reason: String) - - static func from(_ state: XMPPClient.State) -> ConnectionStatus { - switch state { - case .connecting: - return .connecting - - case .connected(let resumed): - return .connected(resumed: resumed) - - case .disconnecting: - return .disconnecting - - case .disconnected(let reason): - return .disconnected(reason: reason.localizedDescription) - } - } -} diff --git a/old/AppCore/Models/Message.swift b/old/AppCore/Models/Message.swift deleted file mode 100644 index c5fd963..0000000 --- a/old/AppCore/Models/Message.swift +++ /dev/null @@ -1,128 +0,0 @@ -import Foundation -import GRDB -import Martin - -enum MessageType: String, Codable, DatabaseValueConvertible { - case chat - case groupchat - case error -} - -enum MessageAttachmentType: Int, Stateable, DatabaseValueConvertible { - case movie = 0 - case image = 1 - case audio = 2 - case file = 3 -} - -enum MessageContentType: String, Codable, DatabaseValueConvertible { - case text - case typing - case invite - case attachment -} - -struct Message: DBStorable, Equatable { - static let databaseTableName = "messages" - - let id: String - let type: MessageType - let contentType: MessageContentType - - let from: String - let to: String? - - let body: String? - let subject: String? - let thread: String? - let oobUrl: String? - - let date: Date - let pending: Bool - let sentError: Bool - - var attachmentType: MessageAttachmentType? - var attachmentLocalName: String? - var attachmentRemotePath: URL? - var attachmentThumbnailName: String? - var attachmentDownloadFailed: Bool = false -} - -extension Message { - // Universal mapping from Martin's Message to App Message - static func map(_ martinMessage: Martin.Message) -> Message? { - #if DEBUG - print("---") - print("Message received: \(martinMessage)") - print("---") - #endif - - // Check that the message type is supported - let chatTypes: [StanzaType] = [.chat, .groupchat] - guard let mType = martinMessage.type, chatTypes.contains(mType) else { - #if DEBUG - print("Unsupported message type: \(martinMessage.type?.rawValue ?? "nil")") - #endif - return nil - } - - // Type - let type = MessageType(rawValue: martinMessage.type?.rawValue ?? "") ?? .chat - - // Content type - var contentType: MessageContentType = .text - if martinMessage.oob != nil { - contentType = .attachment - } else if martinMessage.hints.contains(.noStore) { - contentType = .typing - } - - // From/To - let from = martinMessage.from?.bareJid.stringValue ?? "" - let to = martinMessage.to?.bareJid.stringValue - - // Extract date or set current - var date = Date() - if let timestampStr = martinMessage.attribute("archived_date"), let timeInterval = TimeInterval(timestampStr) { - date = Date(timeIntervalSince1970: timeInterval) - } - - // Msg - var msg = Message( - id: martinMessage.id ?? UUID().uuidString, - type: type, - contentType: contentType, - from: from, - to: to, - body: martinMessage.body, - subject: martinMessage.subject, - thread: martinMessage.thread, - oobUrl: martinMessage.oob, - date: date, - pending: false, - sentError: false, - attachmentType: nil, - attachmentLocalName: nil, - attachmentRemotePath: nil, - attachmentThumbnailName: nil, - attachmentDownloadFailed: false - ) - if let oob = martinMessage.oob { - msg.attachmentType = oob.attachmentType - msg.attachmentRemotePath = URL(string: oob) - } - return msg - } -} - -extension Message { - var attachmentLocalPath: URL? { - guard let attachmentLocalName = attachmentLocalName else { return nil } - return FileProcessing.fileFolder.appendingPathComponent(attachmentLocalName) - } - - var attachmentThumbnailPath: URL? { - guard let attachmentThumbnailName = attachmentThumbnailName else { return nil } - return FileProcessing.fileFolder.appendingPathComponent(attachmentThumbnailName) - } -} diff --git a/old/AppCore/Models/Room.swift b/old/AppCore/Models/Room.swift deleted file mode 100644 index 583fd8b..0000000 --- a/old/AppCore/Models/Room.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation -import GRDB -import Martin -import SwiftUI - -// MARK: - Account -struct Room: DBStorable { - var id: String - var account: String - var nickname: String - var password: String? -} - -extension Room { - static let roomTableName = "rooms" -} diff --git a/old/AppCore/Models/Roster.swift b/old/AppCore/Models/Roster.swift deleted file mode 100644 index b4653cb..0000000 --- a/old/AppCore/Models/Roster.swift +++ /dev/null @@ -1,62 +0,0 @@ -import Foundation -import GRDB -import Martin - -struct RosterVersion: DBStorable { - static let databaseTableName = "rosterVersions" - - var bareJid: String - var version: String - var id: String { bareJid } -} - -struct Roster: DBStorable { - static let databaseTableName = "rosters" - - var bareJid: String = "" - var contactBareJid: String - var name: String? - var subscription: String - var ask: Bool - var data: DBRosterData - var locallyDeleted: Bool = false - - var id: String { "\(bareJid)-\(contactBareJid)" } -} - -struct DBRosterData: Codable, DatabaseValueConvertible { - let groups: [String] - let annotations: [RosterItemAnnotation] - - public var databaseValue: DatabaseValue { - let encoder = JSONEncoder() - // swiftlint:disable:next force_try - let data = try! encoder.encode(self) - return data.databaseValue - } - - public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { - guard let data = Data.fromDatabaseValue(dbValue) else { - return nil - } - let decoder = JSONDecoder() - // swiftlint:disable:next force_try - return try! decoder.decode(Self.self, from: data) - } - - static func == (lhs: DBRosterData, rhs: DBRosterData) -> Bool { - lhs.groups == rhs.groups && lhs.annotations == rhs.annotations - } -} - -extension RosterItemAnnotation: Equatable { - public static func == (lhs: RosterItemAnnotation, rhs: RosterItemAnnotation) -> Bool { - lhs.type == rhs.type && lhs.values == rhs.values - } -} - -extension Roster: Equatable { - static func == (lhs: Roster, rhs: Roster) -> Bool { - lhs.bareJid == rhs.bareJid && lhs.contactBareJid == rhs.contactBareJid - } -} diff --git a/old/AppCore/Models/ServerFeature.swift b/old/AppCore/Models/ServerFeature.swift deleted file mode 100644 index 8ee3663..0000000 --- a/old/AppCore/Models/ServerFeature.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -struct ServerFeature: Stateable, Identifiable { - let xep: String - let name: String - let xmppId: String? - let description: String? - - var id: String { xep } -} diff --git a/old/AppCore/Reducers/AccountsReducer.swift b/old/AppCore/Reducers/AccountsReducer.swift deleted file mode 100644 index f5a7c56..0000000 --- a/old/AppCore/Reducers/AccountsReducer.swift +++ /dev/null @@ -1,25 +0,0 @@ -extension AccountsState { - static func reducer(state: inout AccountsState, action: AccountsAction) { - switch action { - case .accountsListUpdated(let accounts): - state.accounts = accounts - - case .goTo(let navigation): - state.navigation = navigation - - case .tryAddAccountWithCredentials(let login, let password): - let account = Account(bareJid: login, pass: password, isActive: true, isTemp: true) - state.accounts.append(account) - - case .addAccountError(let jid, let reason): - state.accounts = state.accounts.filter { $0.bareJid != jid } - state.addAccountError = reason - - case .clientServerFeaturesUpdated(let jid, let features): - state.discoFeatures[jid] = features - - default: - break - } - } -} diff --git a/old/AppCore/Reducers/AppReducer.swift b/old/AppCore/Reducers/AppReducer.swift deleted file mode 100644 index ff67556..0000000 --- a/old/AppCore/Reducers/AppReducer.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation - -extension AppState { - static func reducer(state: inout AppState, action: AppAction) { - switch action { - case .flushState: - state = AppState() - - case .changeFlow(let flow): - state.previousFlow = state.currentFlow - state.currentFlow = flow - - case .startAction(let action): - StartState.reducer(state: &state.startState, action: action) - - case .databaseAction, .xmppAction, .fileAction, .info: - break // this actions are processed by other middlewares - - case .accountsAction(let action): - AccountsState.reducer(state: &state.accountsState, action: action) - - case .rostersAction(let action): - RostersState.reducer(state: &state.rostersState, action: action) - - case .chatsAction(let action): - ChatsState.reducer(state: &state.chatsState, action: action) - - case .conversationAction(let action): - ConversationState.reducer(state: &state.conversationsState, action: action) - - case .sharingAction(let action): - SharingState.reducer(state: &state.sharingState, action: action) - } - } -} diff --git a/old/AppCore/Reducers/ChatsReducer.swift b/old/AppCore/Reducers/ChatsReducer.swift deleted file mode 100644 index 393c61c..0000000 --- a/old/AppCore/Reducers/ChatsReducer.swift +++ /dev/null @@ -1,14 +0,0 @@ -extension ChatsState { - static func reducer(state: inout ChatsState, action: ChatsAction) { - switch action { - case .chatsListUpdated(let chats): - state.chats = chats - - case .chatStarted(let chat): - state.currentChat = chat - - default: - break - } - } -} diff --git a/old/AppCore/Reducers/ConversationReducer.swift b/old/AppCore/Reducers/ConversationReducer.swift deleted file mode 100644 index cc76c62..0000000 --- a/old/AppCore/Reducers/ConversationReducer.swift +++ /dev/null @@ -1,24 +0,0 @@ -import SwiftUI - -extension ConversationState { - static func reducer(state: inout ConversationState, action: ConversationAction) { - switch action { - case .makeConversationActive(let chat, let roster): - state.currentChat = chat - state.currentRoster = roster - - case .messagesUpdated(let messages): - state.currentMessages = messages - - case .setReplyText(let text): - if text.isEmpty { - state.replyText = "" - } else { - state.replyText = text.makeReply - } - - default: - break - } - } -} diff --git a/old/AppCore/Reducers/RostersReducer.swift b/old/AppCore/Reducers/RostersReducer.swift deleted file mode 100644 index 076559e..0000000 --- a/old/AppCore/Reducers/RostersReducer.swift +++ /dev/null @@ -1,25 +0,0 @@ -extension RostersState { - static func reducer(state: inout RostersState, action: RostersAction) { - switch action { - case .addRosterDone(let jid): - state.newAddedRosterJid = jid - state.newAddedRosterError = nil - - case .addRosterError(let reason): - state.newAddedRosterJid = nil - state.newAddedRosterError = reason - - case .rostersListUpdated(let rosters): - state.rosters = rosters - - case .markRosterAsLocallyDeleted, .deleteRoster: - state.deleteRosterError = nil - - case .rosterDeletingFailed(let reson): - state.deleteRosterError = reson - - default: - break - } - } -} diff --git a/old/AppCore/Reducers/SharingReducer.swift b/old/AppCore/Reducers/SharingReducer.swift deleted file mode 100644 index 3b66fc9..0000000 --- a/old/AppCore/Reducers/SharingReducer.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - -extension SharingState { - static func reducer(state: inout SharingState, action: SharingAction) { - switch action { - case .showSharing(let shown): - state.sharingShown = shown - - case .setCameraAccess(let granted): - state.isCameraAccessGranted = granted - - case .setGalleryAccess(let granted): - state.isGalleryAccessGranted = granted - - case .galleryItemsUpdated(let items): - state.galleryItems = items - - default: - break - } - } -} diff --git a/old/AppCore/Reducers/StartReducer.swift b/old/AppCore/Reducers/StartReducer.swift deleted file mode 100644 index 76539f6..0000000 --- a/old/AppCore/Reducers/StartReducer.swift +++ /dev/null @@ -1,11 +0,0 @@ -extension StartState { - static func reducer(state: inout StartState, action: StartAction) { - switch action { - case .loadStoredAccounts: - break - - case .goTo(let navigation): - state.navigation = navigation - } - } -} diff --git a/old/AppCore/State/AccountsState.swift b/old/AppCore/State/AccountsState.swift deleted file mode 100644 index 4e6cdb6..0000000 --- a/old/AppCore/State/AccountsState.swift +++ /dev/null @@ -1,20 +0,0 @@ -enum AccountNavigationState: Stateable { - case addAccount -} - -struct AccountsState: Stateable { - var navigation: AccountNavigationState - var accounts: [Account] - var discoFeatures: [String: [ServerFeature]] - - var addAccountError: String? -} - -// MARK: Init -extension AccountsState { - init() { - navigation = .addAccount - accounts = [] - discoFeatures = [:] - } -} diff --git a/old/AppCore/State/AppState.swift b/old/AppCore/State/AppState.swift deleted file mode 100644 index 56f589f..0000000 --- a/old/AppCore/State/AppState.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation - -enum AppFlow: Codable { - case start - case accounts - case chats - case contacts - case settings - case conversation -} - -struct AppState: Stateable { - var appVersion: String - var previousFlow: AppFlow - var currentFlow: AppFlow - - var startState: StartState - var accountsState: AccountsState - var rostersState: RostersState - var chatsState: ChatsState - var conversationsState: ConversationState - var sharingState: SharingState -} - -// MARK: Init -extension AppState { - init() { - appVersion = Const.appVersion - previousFlow = .start - currentFlow = .start - - startState = StartState() - accountsState = AccountsState() - rostersState = RostersState() - chatsState = ChatsState() - conversationsState = ConversationState() - sharingState = SharingState() - } -} diff --git a/old/AppCore/State/ChatsState.swift b/old/AppCore/State/ChatsState.swift deleted file mode 100644 index 469aef8..0000000 --- a/old/AppCore/State/ChatsState.swift +++ /dev/null @@ -1,11 +0,0 @@ -struct ChatsState: Stateable { - var chats: [Chat] - var currentChat: Chat? -} - -// MARK: Init -extension ChatsState { - init() { - chats = [] - } -} diff --git a/old/AppCore/State/ConversationState.swift b/old/AppCore/State/ConversationState.swift deleted file mode 100644 index 9267bbd..0000000 --- a/old/AppCore/State/ConversationState.swift +++ /dev/null @@ -1,15 +0,0 @@ -struct ConversationState: Stateable { - var currentChat: Chat? - var currentRoster: Roster? - var currentMessages: [Message] - - var replyText: String -} - -// MARK: Init -extension ConversationState { - init() { - currentMessages = [] - replyText = "" - } -} diff --git a/old/AppCore/State/RostersState.swift b/old/AppCore/State/RostersState.swift deleted file mode 100644 index bc42a70..0000000 --- a/old/AppCore/State/RostersState.swift +++ /dev/null @@ -1,15 +0,0 @@ -struct RostersState: Stateable { - var rosters: [Roster] - - var newAddedRosterJid: String? - var newAddedRosterError: String? - - var deleteRosterError: String? -} - -// MARK: Init -extension RostersState { - init() { - rosters = [] - } -} diff --git a/old/AppCore/State/SharingState.swift b/old/AppCore/State/SharingState.swift deleted file mode 100644 index d19c489..0000000 --- a/old/AppCore/State/SharingState.swift +++ /dev/null @@ -1,32 +0,0 @@ -import Foundation - -enum SharingCameraMediaType: Stateable { - case video - case photo -} - -struct SharingGalleryItem: Stateable, Identifiable { - var id: String - var type: SharingCameraMediaType - var thumbnail: Data? - var duration: String? -} - -struct SharingState: Stateable { - var sharingShown: Bool - var isCameraAccessGranted: Bool - var isGalleryAccessGranted: Bool - - var galleryItems: [SharingGalleryItem] -} - -// MARK: Init -extension SharingState { - init() { - sharingShown = false - isCameraAccessGranted = false - isGalleryAccessGranted = false - - galleryItems = [] - } -} diff --git a/old/AppCore/State/StartState.swift b/old/AppCore/State/StartState.swift deleted file mode 100644 index 15bb709..0000000 --- a/old/AppCore/State/StartState.swift +++ /dev/null @@ -1,15 +0,0 @@ -enum StartNavigationState: Stateable { - case startScreen - case welcomeScreen -} - -struct StartState: Stateable { - var navigation: StartNavigationState -} - -// MARK: Init -extension StartState { - init() { - navigation = .startScreen - } -} diff --git a/old/AppCore/XMPP/XMPPService.swift b/old/AppCore/XMPP/XMPPService.swift deleted file mode 100644 index f9109ff..0000000 --- a/old/AppCore/XMPP/XMPPService.swift +++ /dev/null @@ -1,266 +0,0 @@ -import Combine -import Foundation -import GRDB -import Martin - -protocol MartinsManager: Martin.RosterManager & Martin.ChatManager & Martin.ChannelManager & Martin.RoomManager {} - -final class XMPPService: ObservableObject { - private let manager: MartinsManager - - private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>() - private var clientStateCancellables: Set = [] - - private let clientMessagesPublisher = PassthroughSubject<(XMPPClient, Martin.Message), Never>() - private var clientMessagesCancellables: Set = [] - - private let clientFeaturesPublisher = PassthroughSubject<(XMPPClient, [String]), Never>() - private var clientFeaturesCancellables: Set = [] - - @Published private(set) var clients: [XMPPClient] = [] - var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> { - clientStatePublisher.eraseToAnyPublisher() - } - - var clientMessages: AnyPublisher<(XMPPClient, Martin.Message), Never> { - clientMessagesPublisher.eraseToAnyPublisher() - } - - var clientFeatures: AnyPublisher<(XMPPClient, [String]), Never> { - clientFeaturesPublisher.eraseToAnyPublisher() - } - - init(manager: MartinsManager) { - self.manager = manager - } - - func updateClients(for accounts: [Account]) { - // get simple diff - let forAdd = accounts - .filter { !self.clients.map { $0.connectionConfiguration.userJid.stringValue }.contains($0.bareJid) } - let forRemove = clients - .map { $0.connectionConfiguration.userJid.stringValue } - .filter { !accounts.map { $0.bareJid }.contains($0) } - - // init and add clients - for account in forAdd { - // add client - let client = makeClient(for: account, with: manager) - clients.append(client) - - // subscribe to client state - client.$state - .sink { [weak self] state in - self?.clientStatePublisher.send((client, state)) - } - .store(in: &clientStateCancellables) - - // subscribe to client server features - client.module(DiscoveryModule.self).$serverDiscoResult - .sink { [weak self] disco in - self?.clientFeaturesPublisher.send((client, disco.features)) - } - .store(in: &clientFeaturesCancellables) - - // subscribe to client messages - client.module(MessageModule.self).messagesPublisher - .sink { [weak self] message in - self?.clientMessagesPublisher.send((client, message.message)) - } - .store(in: &clientMessagesCancellables) - - // subscribe to carbons - client.module(MessageCarbonsModule.self).carbonsPublisher - .sink { [weak self] carbon in - self?.clientMessagesPublisher.send((client, carbon.message)) - } - .store(in: &clientMessagesCancellables) - - // subscribe to archived messages - // client.module(.mam).archivedMessagesPublisher - // .sink(receiveValue: { [weak self] archived in - // let message = archived.message - // message.attribute("archived_date", newValue: "\(archived.timestamp.timeIntervalSince1970)") - // self?.clientMessagesPublisher.send((client, message)) - // }) - // .store(in: &clientMessagesCancellables) - - // enable carbons if available - client.module(.messageCarbons).$isAvailable.filter { $0 } - .sink(receiveValue: { [weak client] _ in - client?.module(.messageCarbons).enable() - }) - .store(in: &clientMessagesCancellables) - - // finally, do login - client.login() - } - - // remove clients - for jid in forRemove { - deinitClient(jid: jid) - } - } - - private func makeClient(for account: Account, with manager: MartinsManager) -> XMPPClient { - let client = XMPPClient() - - // register modules - // core modules RFC 6120 - client.modulesManager.register(StreamFeaturesModule()) - client.modulesManager.register(SaslModule()) - client.modulesManager.register(AuthModule()) - client.modulesManager.register(SessionEstablishmentModule()) - client.modulesManager.register(ResourceBinderModule()) - client.modulesManager.register(DiscoveryModule(identity: .init(category: "client", type: "iOS", name: Const.appName))) - - // messaging modules RFC 6121 - client.modulesManager.register(RosterModule(rosterManager: manager)) - client.modulesManager.register(PresenceModule()) - - client.modulesManager.register(PubSubModule()) - client.modulesManager.register(MessageModule(chatManager: manager)) - client.modulesManager.register(MessageArchiveManagementModule()) - - client.modulesManager.register(MessageCarbonsModule()) - - // file transfer modules - client.modulesManager.register(HttpFileUploadModule()) - - // extensions - client.modulesManager.register(SoftwareVersionModule()) - client.modulesManager.register(PingModule()) - client.connectionConfiguration.userJid = .init(account.bareJid) - client.connectionConfiguration.credentials = .password(password: account.pass) - - // group chats - client.modulesManager.register(MucModule(roomManager: manager)) - - // channels - // client.modulesManager.register(MixModule(channelManager: manager)) - - // add client to clients - return client - } - - func deinitClient(jid: String) { - if let index = clients.firstIndex(where: { $0.connectionConfiguration.userJid.stringValue == jid }) { - let client = clients.remove(at: index) - _ = client.disconnect() - } - } - - func getClient(for jid: String) -> XMPPClient? { - clients.first { $0.connectionConfiguration.userJid.stringValue == jid } - } - - func sendMessage(message: Message, completion: @escaping (Bool) -> Void) { - guard let client = getClient(for: message.from), let to = message.to else { - completion(false) - return - } - guard let chat = client.module(MessageModule.self).chatManager.chat(for: client.context, with: BareJID(to)) else { - completion(false) - return - } - - let msg = chat.createMessage(text: message.body ?? "??", id: message.id) - chat.send(message: msg) { res in - switch res { - case .success: - completion(true) - - case .failure: - completion(false) - } - } - } - - func uploadAttachment(message: Message, completion: @escaping (Error?, String) -> Void) { - guard let client = getClient(for: message.from), let to = message.to else { - completion(XMPPError.bad_request("No such client"), "") - return - } - guard let fileName = message.attachmentLocalName else { - completion(XMPPError.bad_request("No such file"), "") - return - } - let url = FileProcessing.fileFolder.appendingPathComponent(fileName) - guard let data = try? Data(contentsOf: url) else { - completion(XMPPError.bad_request("No such file"), "") - return - } - guard let chat = client.module(MessageModule.self).chatManager.chat(for: client.context, with: BareJID(to)) else { - completion(XMPPError.bad_request("No such chat"), "") - return - } - - let httpModule = client.module(HttpFileUploadModule.self) - httpModule.findHttpUploadComponent { res in - switch res { - case .success(let components): - guard let component = components.first(where: { $0.maxSize > data.count }) else { - completion(XMPPError.bad_request("File too big"), "") - return - } - httpModule.requestUploadSlot(componentJid: component.jid, filename: fileName, size: data.count, contentType: url.mimeType) { res in - switch res { - case .success(let slot): - var request = URLRequest(url: slot.putUri) - for (key, value) in slot.putHeaders { - request.addValue(value, forHTTPHeaderField: key) - } - request.httpMethod = "PUT" - request.httpBody = data - request.addValue(String(data.count), forHTTPHeaderField: "Content-Length") - request.addValue(url.mimeType, forHTTPHeaderField: "Content-Type") - let session = URLSession(configuration: URLSessionConfiguration.default) - session.dataTask(with: request) { _, response, error in - let code = (response as? HTTPURLResponse)?.statusCode ?? 500 - guard error == nil, code == 200 || code == 201 else { - completion(XMPPError.bad_request("Upload failed"), "") - return - } - if code == 200 { - completion(XMPPError.bad_request("Invalid response code"), "") - } else { - let mesg = chat.createMessage(text: slot.getUri.absoluteString, id: message.id) - mesg.oob = slot.getUri.absoluteString - chat.send(message: mesg) { res in - switch res { - case .success: - completion(nil, slot.getUri.absoluteString) - - case .failure: - completion(XMPPError.bad_request("File uploaded, but message sent failed"), slot.getUri.absoluteString) - } - } - } - }.resume() - - case .failure(let error): - completion(error, "") - } - } - - case .failure(let error): - completion(error, "") - } - } - } - - func requestArchivedMessages(jid: String, to: String?, fromDate: Date) { - guard let client = getClient(for: jid) else { - return - } - client.module(.mam).queryItems(componentJid: JID(jid), with: JID(to), start: fromDate, end: Date(), queryId: UUID().uuidString) { result in - switch result { - case .success(let response): - print("MAM response: \(response)") - - case .failure(let error): - print("MAM error: \(error)") - } - } - } -} diff --git a/old/Generated/.gitignore b/old/Generated/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/old/Generated/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/old/Helpers/Bool+Extensions.swift b/old/Helpers/Bool+Extensions.swift deleted file mode 100644 index b26674c..0000000 --- a/old/Helpers/Bool+Extensions.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -extension Bool { - var intValue: Int { - self ? 1 : 0 - } -} diff --git a/old/Helpers/Const.swift b/old/Helpers/Const.swift deleted file mode 100644 index c96edfd..0000000 --- a/old/Helpers/Const.swift +++ /dev/null @@ -1,53 +0,0 @@ -import Foundation -import UIKit - -enum Const { - // // Network - // #if DEBUG - // static let baseUrl = "staging.some.com/api" - // #else - // static let baseUrl = "prod.some.com/api" - // #endif - // static let requestTimeout = 15.0 - // static let networkLogging = true - - // App - static var appVersion: String { - let info = Bundle.main.infoDictionary - let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown" - let appBuild = info?[kCFBundleVersionKey as String] as? String ?? "Unknown" - return "v \(appVersion)(\(appBuild))" - } - - static var appName: String { - Bundle.main.bundleIdentifier ?? "Conversations Classic iOS" - } - - // Trusted servers - enum TrustedServers: String { - case narayana = "narayana.im" - case conversations = "conversations.im" - } - - // Limit for video for sharing - static let videoDurationLimit = 60.0 - - // Upload/download file folder - static let fileFolder = "Downloads" - - // Grid size for gallery preview (3 in a row) - static let galleryGridSize = UIScreen.main.bounds.width / 3 - - // Size for map preview for location messages - static let mapPreviewSize = UIScreen.main.bounds.width * 0.5 - - // Size for attachment preview - static let attachmentPreviewSize = UIScreen.main.bounds.width * 0.5 - - // Lenght in days for MAM request - static let mamRequestDaysLength = 30 - - // Limits for messages pagination - static let messagesPageMin = 20 - static let messagesPageMax = 100 -} diff --git a/old/Helpers/Map+Extensions.swift b/old/Helpers/Map+Extensions.swift deleted file mode 100644 index 4c25921..0000000 --- a/old/Helpers/Map+Extensions.swift +++ /dev/null @@ -1,16 +0,0 @@ -import MapKit - -extension MKCoordinateRegion: Equatable { - public static func == (lhs: MKCoordinateRegion, rhs: MKCoordinateRegion) -> Bool { - lhs.center.latitude == rhs.center.latitude && - lhs.center.longitude == rhs.center.longitude && - lhs.span.latitudeDelta == rhs.span.latitudeDelta && - lhs.span.longitudeDelta == rhs.span.longitudeDelta - } -} - -extension CLLocationCoordinate2D: Identifiable { - public var id: String { - "\(latitude)-\(longitude)" - } -} diff --git a/old/Helpers/Set+Extensions.swift b/old/Helpers/Set+Extensions.swift deleted file mode 100644 index 910f33b..0000000 --- a/old/Helpers/Set+Extensions.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -class ThreadSafeSet { - private var set: Set = [] - private let accessQueue = DispatchQueue(label: "com.example.ThreadSafeSet") - - func insert(_ newElement: T) { - _ = accessQueue.sync { - set.insert(newElement) - } - } - - func remove(_ element: T) { - _ = accessQueue.sync { - set.remove(element) - } - } - - var elements: Set { - accessQueue.sync { set } - } - - func contains(_ element: T) -> Bool { - accessQueue.sync { set.contains(element) } - } -} diff --git a/old/Helpers/String+Extensions.swift b/old/Helpers/String+Extensions.swift deleted file mode 100644 index 1bbdc25..0000000 --- a/old/Helpers/String+Extensions.swift +++ /dev/null @@ -1,106 +0,0 @@ -import CoreLocation -import Foundation -import SwiftUI - -extension String { - var firstLetter: String { - String(prefix(1)).uppercased() - } - - var makeReply: String { - let allLines = components(separatedBy: .newlines) - let nonBlankLines = allLines.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } - var result = nonBlankLines.joined(separator: "\n") - result = "> \(result)" - return result - } - - var isLocation: Bool { - hasPrefix("geo:") - } - - var getLatLon: CLLocationCoordinate2D { - let geo = components(separatedBy: ":")[1] - let parts = geo.components(separatedBy: ",") - let lat = Double(parts[0]) ?? 0.0 - let lon = Double(parts[1]) ?? 0.0 - return CLLocationCoordinate2D(latitude: lat, longitude: lon) - } - - var isContact: Bool { - hasPrefix("contact:") - } - - var getContactJid: String { - components(separatedBy: ":")[1] - } -} - -extension String { - var attachmentType: MessageAttachmentType { - let ext = (self as NSString).pathExtension.lowercased() - - switch ext { - case "mov", "mp4", "avi": - return .movie - - case "jpg", "png", "gif": - return .image - - case "mp3", "wav", "m4a": - return .audio - - case "txt", "doc", "pdf": - return .file - - default: - return .file - } - } -} - -extension String { - var firstLetterColor: Color { - let firstLetter = self.firstLetter - switch firstLetter { - case "A", "M", "Y": - return Color.Rainbow.tortoiseLight500 - - case "B", "N", "Z": - return Color.Rainbow.orangeLight500 - - case "C", "O": - return Color.Rainbow.yellowLight500 - - case "D", "P": - return Color.Rainbow.greenLight500 - - case "E", "Q": - return Color.Rainbow.blueLight500 - - case "F", "R": - return Color.Rainbow.magentaLight500 - - case "G", "S": - return Color.Rainbow.tortoiseDark500 - - case "H", "T": - return Color.Rainbow.orangeDark500 - - case "I", "U": - return Color.Rainbow.yellowDark500 - - case "J", "V": - return Color.Rainbow.greenDark500 - - case "K", "W": - return Color.Rainbow.blueDark500 - - case "L", "X": - return Color.Rainbow.magentaDark500 - - default: - return Color.Rainbow.tortoiseLight500 - } - } -} diff --git a/old/Helpers/TimeInterval+Extensions.swift b/old/Helpers/TimeInterval+Extensions.swift deleted file mode 100644 index 0dde77a..0000000 --- a/old/Helpers/TimeInterval+Extensions.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -extension TimeInterval { - var minAndSec: String { - let minutes = Int(self) / 60 - let seconds = Int(self) % 60 - return String(format: "%02d:%02d", minutes, seconds) - } -} diff --git a/old/Helpers/UIApplication+Extensions.swift b/old/Helpers/UIApplication+Extensions.swift deleted file mode 100644 index 63550f5..0000000 --- a/old/Helpers/UIApplication+Extensions.swift +++ /dev/null @@ -1,10 +0,0 @@ -import UIKit - -func openAppSettings() { - if - let appSettingsUrl = URL(string: UIApplication.openSettingsURLString), - UIApplication.shared.canOpenURL(appSettingsUrl) - { - UIApplication.shared.open(appSettingsUrl, completionHandler: nil) - } -} diff --git a/old/Helpers/URL+Extensions.swift b/old/Helpers/URL+Extensions.swift deleted file mode 100644 index c84c26a..0000000 --- a/old/Helpers/URL+Extensions.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UniformTypeIdentifiers - -extension URL { - var mimeType: String { - let pathExtension = self.pathExtension - - if let uti = UTType(filenameExtension: pathExtension) { - return uti.preferredMIMEType ?? "application/octet-stream" - } else { - return "application/octet-stream" - } - } -} diff --git a/old/Helpers/UserDefaultsWrapper.swift b/old/Helpers/UserDefaultsWrapper.swift deleted file mode 100644 index 535e9e9..0000000 --- a/old/Helpers/UserDefaultsWrapper.swift +++ /dev/null @@ -1,32 +0,0 @@ -import Foundation - -// Wrapper -@propertyWrapper -struct Storage { - private let key: String - private let defaultValue: T - - init(key: String, defaultValue: T) { - self.key = key - self.defaultValue = defaultValue - } - - var wrappedValue: T { - get { - // Read value from UserDefaults - UserDefaults.standard.object(forKey: key) as? T ?? defaultValue - } - set { - // Set value to UserDefaults - UserDefaults.standard.set(newValue, forKey: key) - } - } -} - -// Storage -private let keyLocalizationSelected = "conversations.classic.user.defaults.localizationSelected" - -enum UserSettings { - @Storage(key: keyLocalizationSelected, defaultValue: false) - static var localizationSelectedByUser: Bool -} diff --git a/old/Resources/Assets/Colors.xcassets/Contents.json b/old/Resources/Assets/Colors.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/old/Resources/Assets/Colors.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/Contents.json b/old/Resources/Assets/Colors.xcassets/material/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/background/Contents.json b/old/Resources/Assets/Colors.xcassets/material/background/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/background/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/background/dark.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/background/dark.colorset/Contents.json deleted file mode 100644 index bdb682b..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/background/dark.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xE4", - "green" : "0xE4", - "red" : "0xE4" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/background/light.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/background/light.colorset/Contents.json deleted file mode 100644 index b8c6d9e..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/background/light.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "235", - "green" : "235", - "red" : "235" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/elements/Contents.json b/old/Resources/Assets/Colors.xcassets/material/elements/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/elements/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/elements/active.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/elements/active.colorset/Contents.json deleted file mode 100644 index 5a42e0f..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/elements/active.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x4D", - "green" : "0x46", - "red" : "0x3C" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/elements/inactive.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/elements/inactive.colorset/Contents.json deleted file mode 100644 index 944aec1..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/elements/inactive.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xAC", - "green" : "0xA3", - "red" : "0x95" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/shape/Contents.json b/old/Resources/Assets/Colors.xcassets/material/shape/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/shape/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/shape/alternate.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/shape/alternate.colorset/Contents.json deleted file mode 100644 index 72469b0..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/shape/alternate.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "201", - "green" : "227", - "red" : "199" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/shape/black.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/shape/black.colorset/Contents.json deleted file mode 100644 index 1c18f8d..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/shape/black.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x62", - "green" : "0x59", - "red" : "0x4A" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/shape/separator.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/shape/separator.colorset/Contents.json deleted file mode 100644 index 3d66dc2..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/shape/separator.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "189", - "green" : "189", - "red" : "189" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/shape/white.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/shape/white.colorset/Contents.json deleted file mode 100644 index fafa476..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/shape/white.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xFF", - "green" : "0xFF", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/text/Contents.json b/old/Resources/Assets/Colors.xcassets/material/text/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/text/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/text/main.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/text/main.colorset/Contents.json deleted file mode 100644 index dfe1a2d..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/text/main.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x36", - "green" : "0x31", - "red" : "0x2A" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/text/sub.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/text/sub.colorset/Contents.json deleted file mode 100644 index 4db6d18..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/text/sub.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x78", - "green" : "0x6D", - "red" : "0x5A" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/material/text/white.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/material/text/white.colorset/Contents.json deleted file mode 100644 index 2cedebe..0000000 --- a/old/Resources/Assets/Colors.xcassets/material/text/white.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xEF", - "green" : "0xEF", - "red" : "0xEF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/Contents.json b/old/Resources/Assets/Colors.xcassets/old/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/background/Contents.json b/old/Resources/Assets/Colors.xcassets/old/background/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/background/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/c100.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/c100.colorset/Contents.json deleted file mode 100644 index 2e838f5..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/c100.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xD9", - "green" : "0xD7", - "red" : "0xD3" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/c200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/c200.colorset/Contents.json deleted file mode 100644 index 8bfa21b..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/c200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xC2", - "green" : "0xBD", - "red" : "0xB5" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/c400.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/c400.colorset/Contents.json deleted file mode 100644 index c61781a..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/c400.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x9A", - "green" : "0x8F", - "red" : "0x7D" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/c50.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/c50.colorset/Contents.json deleted file mode 100644 index a61d481..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/c50.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xFF", - "green" : "0xFF", - "red" : "0xFE" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/primary/c500main.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/primary/c500main.colorset/Contents.json deleted file mode 100644 index d4b5ad5..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/primary/c500main.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x89", - "green" : "0x7C", - "red" : "0x66" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c100.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c100.colorset/Contents.json deleted file mode 100644 index 9d735e4..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c100.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xB4", - "green" : "0xEC", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c200.colorset/Contents.json deleted file mode 100644 index aaeed90..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x83", - "green" : "0xF0", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c300.colorset/Contents.json deleted file mode 100644 index a8c8ffc..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x52", - "green" : "0xD5", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c400.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c400.colorset/Contents.json deleted file mode 100644 index c24864c..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c400.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x2D", - "green" : "0xCA", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c50.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c50.colorset/Contents.json deleted file mode 100644 index c345981..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c50.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xE1", - "green" : "0xF8", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c500main.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c500main.colorset/Contents.json deleted file mode 100644 index 11f3048..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c500main.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x14", - "green" : "0xC1", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c600.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c600.colorset/Contents.json deleted file mode 100644 index 2589002..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c600.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x0F", - "green" : "0xB3", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c700.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c700.colorset/Contents.json deleted file mode 100644 index b745fb4..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c700.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x0D", - "green" : "0xA0", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c800.colorset/Contents.json deleted file mode 100644 index 9024695..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x0C", - "green" : "0x8F", - "red" : "0xFE" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/secondary/c900.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/secondary/c900.colorset/Contents.json deleted file mode 100644 index 8b4228e..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/secondary/c900.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x0B", - "green" : "0x70", - "red" : "0xFE" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/text/Contents.json b/old/Resources/Assets/Colors.xcassets/old/text/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/text/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/text/main.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/text/main.colorset/Contents.json deleted file mode 100644 index 9c8eb66..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/text/main.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x2D", - "green" : "0x2D", - "red" : "0x2D" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/old/text/sub.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/old/text/sub.colorset/Contents.json deleted file mode 100644 index 2e42b95..0000000 --- a/old/Resources/Assets/Colors.xcassets/old/text/sub.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x78", - "green" : "0x78", - "red" : "0x78" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/Contents.json deleted file mode 100644 index 6e96565..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/Contents.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "provides-namespace" : true - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blue200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blue200.colorset/Contents.json deleted file mode 100644 index 12b3b32..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blue200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.976", - "green" : "0.792", - "red" : "0.565" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blue300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blue300.colorset/Contents.json deleted file mode 100644 index 0b975d5..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blue300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.965", - "green" : "0.710", - "red" : "0.392" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blue500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blue500.colorset/Contents.json deleted file mode 100644 index 3c05e0a..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blue500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.953", - "green" : "0.588", - "red" : "0.129" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blue800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blue800.colorset/Contents.json deleted file mode 100644 index 865adb5..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blue800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.824", - "green" : "0.463", - "red" : "0.098" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueDark200.colorset/Contents.json deleted file mode 100644 index e29b366..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.855", - "green" : "0.659", - "red" : "0.624" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueDark300.colorset/Contents.json deleted file mode 100644 index 096e5f1..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.796", - "green" : "0.525", - "red" : "0.475" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueDark500.colorset/Contents.json deleted file mode 100644 index 22f8cb4..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.710", - "green" : "0.318", - "red" : "0.247" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueDark800.colorset/Contents.json deleted file mode 100644 index bf615f8..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.624", - "green" : "0.247", - "red" : "0.188" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueLight200.colorset/Contents.json deleted file mode 100644 index 1ded918..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.980", - "green" : "0.831", - "red" : "0.506" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueLight300.colorset/Contents.json deleted file mode 100644 index f17585c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.969", - "green" : "0.765", - "red" : "0.310" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueLight500.colorset/Contents.json deleted file mode 100644 index 9453b24..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.957", - "green" : "0.663", - "red" : "0.012" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/blueLight800.colorset/Contents.json deleted file mode 100644 index 2b85944..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/blueLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.820", - "green" : "0.533", - "red" : "0.008" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/brown200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/brown200.colorset/Contents.json deleted file mode 100644 index ce0b7bb..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/brown200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.643", - "green" : "0.667", - "red" : "0.737" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/brown300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/brown300.colorset/Contents.json deleted file mode 100644 index 957f0da..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/brown300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.498", - "green" : "0.533", - "red" : "0.631" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/brown500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/brown500.colorset/Contents.json deleted file mode 100644 index 2926f1b..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/brown500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.282", - "green" : "0.333", - "red" : "0.475" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/brown800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/brown800.colorset/Contents.json deleted file mode 100644 index 70e2f7c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/brown800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.216", - "green" : "0.251", - "red" : "0.365" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark100.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenDark100.colorset/Contents.json deleted file mode 100644 index 72469b0..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark100.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "201", - "green" : "227", - "red" : "199" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenDark200.colorset/Contents.json deleted file mode 100644 index c7b6100..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "167", - "green" : "214", - "red" : "165" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenDark300.colorset/Contents.json deleted file mode 100644 index 0f3b846..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "132", - "green" : "199", - "red" : "129" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenDark500.colorset/Contents.json deleted file mode 100644 index ddfea69..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.314", - "green" : "0.686", - "red" : "0.298" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenDark800.colorset/Contents.json deleted file mode 100644 index 9dbb7a6..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.235", - "green" : "0.557", - "red" : "0.220" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenLight200.colorset/Contents.json deleted file mode 100644 index 54f6a97..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.647", - "green" : "0.882", - "red" : "0.773" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenLight300.colorset/Contents.json deleted file mode 100644 index 2c4fe10..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.506", - "green" : "0.835", - "red" : "0.682" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenLight500.colorset/Contents.json deleted file mode 100644 index 89db920..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.290", - "green" : "0.765", - "red" : "0.545" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/greenLight800.colorset/Contents.json deleted file mode 100644 index 1acd954..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/greenLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.220", - "green" : "0.624", - "red" : "0.408" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark200.colorset/Contents.json deleted file mode 100644 index ef9328c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.859", - "green" : "0.616", - "red" : "0.702" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark300.colorset/Contents.json deleted file mode 100644 index 3556135..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.804", - "green" : "0.459", - "red" : "0.584" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark500.colorset/Contents.json deleted file mode 100644 index 00e5075..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.718", - "green" : "0.227", - "red" : "0.404" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark800.colorset/Contents.json deleted file mode 100644 index 748a957..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.659", - "green" : "0.176", - "red" : "0.318" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight200.colorset/Contents.json deleted file mode 100644 index 725c54c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.847", - "green" : "0.576", - "red" : "0.808" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight300.colorset/Contents.json deleted file mode 100644 index d9fbdb8..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.784", - "green" : "0.408", - "red" : "0.729" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight500.colorset/Contents.json deleted file mode 100644 index 99500b7..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.690", - "green" : "0.153", - "red" : "0.612" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight800.colorset/Contents.json deleted file mode 100644 index 2921caf..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/magentaLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.635", - "green" : "0.122", - "red" : "0.482" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark200.colorset/Contents.json deleted file mode 100644 index fbe5e38..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.569", - "green" : "0.671", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark300.colorset/Contents.json deleted file mode 100644 index 58e2f9f..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.396", - "green" : "0.541", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark500.colorset/Contents.json deleted file mode 100644 index e219ac8..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.341", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark800.colorset/Contents.json deleted file mode 100644 index a63dd3e..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.098", - "green" : "0.290", - "red" : "0.902" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight200.colorset/Contents.json deleted file mode 100644 index 6e70cd5..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.502", - "green" : "0.800", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight300.colorset/Contents.json deleted file mode 100644 index 799505c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.302", - "green" : "0.718", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight500.colorset/Contents.json deleted file mode 100644 index 4f0878b..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.596", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight800.colorset/Contents.json deleted file mode 100644 index dfc0149..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/orangeLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.486", - "red" : "0.961" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/pink200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/pink200.colorset/Contents.json deleted file mode 100644 index 0becef6..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/pink200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.694", - "green" : "0.561", - "red" : "0.957" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/pink300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/pink300.colorset/Contents.json deleted file mode 100644 index 9e9e4b7..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/pink300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.573", - "green" : "0.384", - "red" : "0.941" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/pink500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/pink500.colorset/Contents.json deleted file mode 100644 index ddc3e1d..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/pink500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.388", - "green" : "0.118", - "red" : "0.914" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/pink800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/pink800.colorset/Contents.json deleted file mode 100644 index 463aa83..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/pink800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.357", - "green" : "0.094", - "red" : "0.761" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/red200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/red200.colorset/Contents.json deleted file mode 100644 index 518a736..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/red200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.604", - "green" : "0.604", - "red" : "0.937" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/red300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/red300.colorset/Contents.json deleted file mode 100644 index 94100b6..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/red300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.451", - "green" : "0.451", - "red" : "0.898" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/red500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/red500.colorset/Contents.json deleted file mode 100644 index 7afafeb..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/red500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.212", - "green" : "0.263", - "red" : "0.957" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/red800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/red800.colorset/Contents.json deleted file mode 100644 index 8972c91..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/red800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.184", - "green" : "0.184", - "red" : "0.827" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark200.colorset/Contents.json deleted file mode 100644 index 89f27af..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.769", - "green" : "0.796", - "red" : "0.502" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark300.colorset/Contents.json deleted file mode 100644 index 39d4eca..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.675", - "green" : "0.714", - "red" : "0.302" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark500.colorset/Contents.json deleted file mode 100644 index 13174d8..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.533", - "green" : "0.588", - "red" : "0.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark800.colorset/Contents.json deleted file mode 100644 index 24f01a5..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.420", - "green" : "0.475", - "red" : "0.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight200.colorset/Contents.json deleted file mode 100644 index 0f87be6..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.918", - "green" : "0.871", - "red" : "0.502" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight300.colorset/Contents.json deleted file mode 100644 index 5d7af98..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.882", - "green" : "0.816", - "red" : "0.302" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight500.colorset/Contents.json deleted file mode 100644 index 20a4a22..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.831", - "green" : "0.737", - "red" : "0.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight800.colorset/Contents.json deleted file mode 100644 index 68b5f8d..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/tortoiseLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.655", - "green" : "0.592", - "red" : "0.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark200.colorset/Contents.json deleted file mode 100644 index 98fa97f..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.510", - "green" : "0.878", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark300.colorset/Contents.json deleted file mode 100644 index 6140117..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.310", - "green" : "0.835", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark500.colorset/Contents.json deleted file mode 100644 index 6ef924c..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.027", - "green" : "0.757", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark800.colorset/Contents.json deleted file mode 100644 index 93e32b7..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowDark800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.627", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight200.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight200.colorset/Contents.json deleted file mode 100644 index f1b174f..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight200.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.612", - "green" : "0.933", - "red" : "0.902" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight300.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight300.colorset/Contents.json deleted file mode 100644 index 74aa8a8..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight300.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.459", - "green" : "0.906", - "red" : "0.863" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight500.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight500.colorset/Contents.json deleted file mode 100644 index ac7f58f..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight500.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.224", - "green" : "0.863", - "red" : "0.804" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight800.colorset/Contents.json b/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight800.colorset/Contents.json deleted file mode 100644 index 8aa0219..0000000 --- a/old/Resources/Assets/Colors.xcassets/rainbow/yellowLight800.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.169", - "green" : "0.706", - "red" : "0.686" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/Contents.json b/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 72c4225..0000000 --- a/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "images" : [ - { - "filename" : "logo2.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/logo2.png b/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/logo2.png deleted file mode 100644 index 0eb8723..0000000 Binary files a/old/Resources/Assets/Images.xcassets/AppIcon.appiconset/logo2.png and /dev/null differ diff --git a/old/Resources/Assets/Images.xcassets/Contents.json b/old/Resources/Assets/Images.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/old/Resources/Assets/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Assets/Images.xcassets/logo.imageset/Contents.json b/old/Resources/Assets/Images.xcassets/logo.imageset/Contents.json deleted file mode 100644 index aed7690..0000000 --- a/old/Resources/Assets/Images.xcassets/logo.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "logo2_wo_bg.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "original" - } -} diff --git a/old/Resources/Assets/Images.xcassets/logo.imageset/logo2_wo_bg.png b/old/Resources/Assets/Images.xcassets/logo.imageset/logo2_wo_bg.png deleted file mode 100644 index e8e80f9..0000000 Binary files a/old/Resources/Assets/Images.xcassets/logo.imageset/logo2_wo_bg.png and /dev/null differ diff --git a/old/Resources/Preview Content/Preview Assets.xcassets/Contents.json b/old/Resources/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/old/Resources/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/old/Resources/Strings/Localizable.strings b/old/Resources/Strings/Localizable.strings deleted file mode 100644 index 8458afc..0000000 --- a/old/Resources/Strings/Localizable.strings +++ /dev/null @@ -1,76 +0,0 @@ -// MARK: General -"Global.name" = "Conversartions Classic"; -"Global.ok" = "Ok"; -"Global.back" = "Back"; -"Global.cancel" = "Cancel"; -"Global.save" = "Save"; -"Global.Error.title" = "Error"; -"Global.Error.genericText" = "Something went wrong"; -"Global.Error.genericDbError" = "Database error"; - -// MARK: Onboar screen -"Start.subtitle" = "Free and secure messaging and calls between any existed messengers"; -"Start.Btn.login" = "Enter with JID"; -"Start.Btn.register" = "New Account"; -"Login.title" = "Let\'s go!"; -"Login.subtitle" = "Enter your JID, it should looks like email address"; -"Login.Hint.jid" = "user@domain.im"; -"Login.Hint.password" = "password"; -"Login.btn" = "Continue"; -"Login.Error.wrongPassword" = "Wrong password or JID"; -"Login.Error.noServer" = "Server not exists"; -"Login.Error.serverError" = "Server error. Check internet connection"; - -// MARK: Contacts screen -"Contacts.title" = "Contacts"; -"Contacts.sendMessage" = "Send message"; -"Contacts.editContact" = "Edit contact"; -"Contacts.selectContact" = "Select contact"; -"Contacts.deleteContact" = "Delete contact"; -"Contacts.Add.title" = "Add Contact"; -"Contacts.Add.explanation" = "Contact or group/channel name are usually JID in format name@domain.ltd (like email)"; -"Contacts.Add.error" = "Contact not added. Server returns error."; -"Contacts.Delete.title" = "Delete contact"; -"Contacts.Delete.message" = "You can delete contact from this device (contact will be available on other devices), or delete it completely"; -"Contacts.Delete.deleteFromDevice" = "Delete from device"; -"Contacts.Delete.deleteCompletely" = "Delete completely"; -"Contacts.Delete.error" = "Contact not deleted. Server returns error."; - - -// MARK: Chats screen -"Chats.title" = "Chats"; - -"Chat.title" = "Chat"; -"Chat.textfieldPrompt" = "Type a message"; - -"Chats.Create.Main.title" = "Create"; -"Chats.Create.Main.createGroup" = "Create public group"; -"Chats.Create.Main.createPrivateGroup" = "Create private group"; -"Chats.Create.Main.findGroup" = "Find public group"; - -// MARK: Accounts add screen -"Accounts.Add.or" = "or"; -"Accounts.Add.Exist.title" = "Add existing\naccount"; -"Accounts.Add.Exist.Prompt.jid" = "Enter your XMPP ID"; -"Accounts.Add.Exist.Prompt.password" = "Enter password"; -"Accounts.Add.Exist.Hint.jid" = "user@domain.im"; -"Accounts.Add.Exist.Hint.password" = "password"; -"Accounts.Add.Exist.Btn.link" = "create a new one"; -"Accounts.Add.Exist.Btn.main" = "Continue"; -"Accounts.Add.Exist.loginError" = "Wrong login or password"; - -// MARK: Server connecting indicator -"ServerConnectingIndicator.State.connecting" = "Connecting to server"; -"ServerConnectingIndicator.State.connected" = "Connected"; -"ServerConnectingIndicator.State.error" = "Server unreachable. Check internet connection and server name"; - -// MARK: Attachments -"Attachment.Prompt.main" = "Select attachment"; -"Attachment.Tab.media" = "Media"; -"Attachment.Tab.files" = "Files"; -"Attachment.Tab.location" = "Location"; -"Attachment.Tab.contacts" = "Contacts"; -"Attachment.Send.media" = "Send media"; -"Attachment.Send.location" = "Send location"; -"Attachment.Send.contact" = "Send contact"; -"Attachment.Downloading.retry" = "Retry"; diff --git a/old/Resources/launchscreen.storyboard b/old/Resources/launchscreen.storyboard deleted file mode 100644 index ed88f70..0000000 --- a/old/Resources/launchscreen.storyboard +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/old/Resources/server_features.plist b/old/Resources/server_features.plist deleted file mode 100644 index 9fa58d6..0000000 --- a/old/Resources/server_features.plist +++ /dev/null @@ -1,6598 +0,0 @@ - - - - - - xep - XEP-0001 - name - XMPP Extension Protocols - type - Procedural - status - Active - date - 2016-11-16 - description - - xmppId - - - - xep - XEP-0002 - name - Special Interest Groups (SIGs) - type - Procedural - status - Active - date - 2002-01-11 - description - - xmppId - - - - xep - XEP-0003 - name - Proxy Accept Socket Service (PASS) - type - Historical - status - Obsolete - date - 2009-06-03 - description - - xmppId - jabber:iq:pass - - - xep - XEP-0004 - name - Data Forms - type - Standards Track - status - Final - date - 2007-08-13 - description - - xmppId - - - - xep - XEP-0005 - name - Jabber Interest Groups - type - Informational - status - Obsolete - date - 2002-05-08 - description - - xmppId - - - - xep - XEP-0006 - name - Profiles - type - SIG Formation - status - Obsolete - date - 2002-05-08 - description - - xmppId - - - - xep - XEP-0007 - name - Conferencing SIG - type - SIG Proposal - status - Obsolete - date - 2002-05-08 - description - - xmppId - - - - xep - XEP-0008 - name - IQ-Based Avatars - type - Historical - status - Deferred - date - 2005-06-16 - description - - xmppId - jabber:x:avatar - - - xep - XEP-0009 - name - Jabber-RPC - type - Standards Track - status - Final - date - 2011-11-10 - description - - xmppId - jabber:iq:rpc - - - xep - XEP-0010 - name - Whiteboarding SIG - type - SIG Formation - status - Obsolete - date - 2002-05-08 - description - - xmppId - - - - xep - XEP-0011 - name - Jabber Browsing - type - Historical - status - Obsolete - date - 2009-06-03 - description - - xmppId - jabber:iq:browse - - - xep - XEP-0012 - name - Last Activity - type - Standards Track - status - Final - date - 2008-11-26 - description - - xmppId - jabber:iq:last - - - xep - XEP-0013 - name - Flexible Offline Message Retrieval - type - Standards Track - status - Draft - date - 2005-07-14 - description - - xmppId - http://jabber.org/protocol/offline - - - xep - XEP-0014 - name - Message Tone - type - Standards Track - status - Rejected - date - 2002-01-16 - description - - xmppId - jabber:x:tone - - - xep - XEP-0015 - name - Account Transfer - type - Standards Track - status - Rejected - date - 2002-04-18 - description - - xmppId - jabber:iq:accountxfer - - - xep - XEP-0016 - name - Privacy Lists - type - Standards Track - status - Deprecated - date - 2017-05-20 - description - - xmppId - jabber:iq:privacy - - - xep - XEP-0017 - name - Naive Packet Framing Protocol - type - Informational - status - Rejected - date - 2002-02-19 - description - - xmppId - - - - xep - XEP-0018 - name - Invisible Presence - type - Informational - status - Rejected - date - 2003-09-26 - description - - xmppId - - - - xep - XEP-0019 - name - Streamlining the SIGs - type - Procedural - status - Active - date - 2002-03-20 - description - - xmppId - - - - xep - XEP-0020 - name - Feature Negotiation - type - Standards Track - status - Deprecated - date - 2018-03-07 - description - - xmppId - http://jabber.org/protocol/feature-neg - - - xep - XEP-0021 - name - Jabber Event Notification Service (ENS) - type - Standards Track - status - Retracted - date - 2003-04-22 - description - - xmppId - http://xml.cataclysm.cx/jabber/ens/ - - - xep - XEP-0022 - name - Message Events - type - Historical - status - Obsolete - date - 2009-05-27 - description - - xmppId - jabber:x:event - - - xep - XEP-0023 - name - Message Expiration - type - Historical - status - Obsolete - date - 2009-06-03 - description - - xmppId - - - - xep - XEP-0024 - name - Publish/Subscribe - type - Standards Track - status - Retracted - date - 2003-04-22 - description - - xmppId - jabber:iq:pubsub - - - xep - XEP-0025 - name - Jabber HTTP Polling - type - Historical - status - Obsolete - date - 2009-06-03 - description - - xmppId - - - - xep - XEP-0026 - name - Internationalization (I18N) - type - Standards Track - status - Retracted - date - 2003-11-05 - description - - xmppId - - - - xep - XEP-0027 - name - Current Jabber OpenPGP Usage - type - Historical - status - Obsolete - date - 2014-03-14 - description - - xmppId - - - - xep - XEP-0028 - name - No Such XEP - type - Informational - status - Retracted - date - 2001-08-20 - description - - xmppId - - - - xep - XEP-0029 - name - Definition of Jabber Identifiers (JIDs) - type - Standards Track - status - Retracted - date - 2003-10-03 - description - - xmppId - - - - xep - XEP-0030 - name - Service Discovery - type - Standards Track - status - Final - date - 2017-10-03 - description - - xmppId - http://jabber.org/protocol/disco* - - - xep - XEP-0031 - name - A Framework For Securing Jabber Conversations - type - Standards Track - status - Deferred - date - 2002-07-09 - description - - xmppId - - - - xep - XEP-0032 - name - Jabber URI Scheme - type - Standards Track - status - Retracted - date - 2003-09-02 - description - - xmppId - - - - xep - XEP-0033 - name - Extended Stanza Addressing - type - Standards Track - status - Draft - date - 2017-01-11 - description - - xmppId - http://jabber.org/protocol/address - - - xep - XEP-0034 - name - SASL Integration - type - Standards Track - status - Retracted - date - 2003-11-05 - description - - xmppId - - - - xep - XEP-0035 - name - SSL/TLS Integration - type - Standards Track - status - Retracted - date - 2003-11-05 - description - - xmppId - - - - xep - XEP-0036 - name - Pub-Sub Subscriptions - type - Standards Track - status - Retracted - date - 2003-04-22 - description - - xmppId - jabber:iq:pubsub - - - xep - XEP-0037 - name - DSPS - Data Stream Proxy Service - type - Standards Track - status - Rejected - date - 2016-10-04 - description - - xmppId - - - - xep - XEP-0038 - name - Icon Styles - type - Standards Track - status - Deferred - date - 2003-06-02 - description - - xmppId - - - - xep - XEP-0039 - name - Statistics Gathering - type - Standards Track - status - Deferred - date - 2002-11-05 - description - - xmppId - http://jabber.org/protocol/stats - - - xep - XEP-0040 - name - Jabber Robust Publish-Subscribe - type - Standards Track - status - Retracted - date - 2004-07-26 - description - - xmppId - - - - xep - XEP-0041 - name - Reliable Entity Link - type - Standards Track - status - Retracted - date - 2003-09-30 - description - - xmppId - http://jabber.org/protocol/rel - - - xep - XEP-0042 - name - Jabber OOB Broadcast Service (JOBS) - type - Standards Track - status - Retracted - date - 2003-04-11 - description - - xmppId - http://jabber.org/protocol/jobs - - - xep - XEP-0043 - name - Jabber Database Access - type - Standards Track - status - Retracted - date - 2003-10-20 - description - - xmppId - http://openaether.org/projects/jabber_database.html - - - xep - XEP-0044 - name - Full Namespace Support for XML Streams - type - Standards Track - status - Deferred - date - 2002-08-26 - description - - xmppId - - - - xep - XEP-0045 - name - Multi-User Chat - type - Standards Track - status - Draft - date - 2018-07-31 - description - - xmppId - http://jabber.org/protocol/muc - - - xep - XEP-0046 - name - DTCP - type - Standards Track - status - Retracted - date - 2003-04-11 - description - - xmppId - http://jabber.org/protocol/dtcp - - - xep - XEP-0047 - name - In-Band Bytestreams - type - Standards Track - status - Final - date - 2012-06-22 - description - - xmppId - http://jabber.org/protocol/ibb - - - xep - XEP-0048 - name - Bookmarks - type - Standards Track - status - Draft - date - 2007-11-07 - description - - xmppId - storage:bookmarks - - - xep - XEP-0049 - name - Private XML Storage - type - Historical - status - Active - date - 2004-03-01 - description - - xmppId - jabber:iq:private - - - xep - XEP-0050 - name - Ad-Hoc Commands - type - Standards Track - status - Draft - date - 2016-12-03 - description - - xmppId - http://jabber.org/protocol/commands - - - xep - XEP-0051 - name - Connection Transfer - type - Standards Track - status - Deferred - date - 2009-07-07 - description - - xmppId - urn:xmpp:cxfr - - - xep - XEP-0052 - name - File Transfer - type - Standards Track - status - Retracted - date - 2003-09-30 - description - - xmppId - http://jabber.org/protocol/feature-neg - - - xep - XEP-0053 - name - XMPP Registrar Function - type - Procedural - status - Active - date - 2016-12-01 - description - - xmppId - - - - xep - XEP-0054 - name - vcard-temp - type - Historical - status - Active - date - 2008-07-16 - description - - xmppId - vcard-temp - - - xep - XEP-0055 - name - Jabber Search - type - Historical - status - Active - date - 2009-09-15 - description - - xmppId - jabber:iq:search - - - xep - XEP-0056 - name - Business Data Interchange - type - Standards Track - status - Deferred - date - 2002-11-18 - description - - xmppId - http://jabber.org/protocol/ebxml - - - xep - XEP-0057 - name - Extended Roster - type - Standards Track - status - Retracted - date - 2003-04-28 - description - - xmppId - - - - xep - XEP-0058 - name - Multi-User Text Editing - type - Standards Track - status - Deferred - date - 2002-11-12 - description - - xmppId - - - - xep - XEP-0059 - name - Result Set Management - type - Standards Track - status - Draft - date - 2006-09-20 - description - - xmppId - http://jabber.org/protocol/rsm - - - xep - XEP-0060 - name - Publish-Subscribe - type - Standards Track - status - Draft - date - 2018-05-14 - description - - xmppId - http://jabber.org/protocol/pubsub - - - xep - XEP-0061 - name - Shared Notes - type - Informational - status - Deferred - date - 2003-09-30 - description - - xmppId - http://www.jabber.org/protocol/sharednote - - - xep - XEP-0062 - name - Packet Filtering - type - Informational - status - Deferred - date - 2003-09-30 - description - - xmppId - http://jabber.org/protocol/filter - - - xep - XEP-0063 - name - Basic Filtering Operations - type - Informational - status - Deferred - date - 2003-09-30 - description - - xmppId - http://jabber.org/protocol/filter/basic - - - xep - XEP-0064 - name - XPath Filtering - type - Informational - status - Deferred - date - 2003-09-30 - description - - xmppId - http://jabber.org/protocol/filter/xpath - - - xep - XEP-0065 - name - SOCKS5 Bytestreams - type - Standards Track - status - Draft - date - 2015-09-17 - description - - xmppId - http://jabber.org/protocol/bytestreams - - - xep - XEP-0065 - name - Out of Band Data - type - Standards Track - status - Draft - date - 2006-08-15 - description - - xmppId - jabber:x:oob - - - xep - XEP-0066 - name - Out of Band Data - type - Standards Track - status - Draft - date - 2006-08-16 - description - - xmppId - jabber:iq:oob - - - xep - XEP-0067 - name - Stock Data Transmission - type - Standards Track - status - Deferred - date - 2003-07-19 - description - - xmppId - - - - xep - XEP-0068 - name - Field Standardization for Data Forms - type - Informational - status - Active - date - 2012-05-28 - description - - xmppId - - - - xep - XEP-0069 - name - Compliance SIG - type - SIG Formation - status - Deferred - date - 2003-01-29 - description - - xmppId - - - - xep - XEP-0070 - name - Verifying HTTP Requests via XMPP - type - Standards Track - status - Draft - date - 2016-12-09 - description - - xmppId - http://jabber.org/protocol/http-auth - - - xep - XEP-0071 - name - XHTML-IM - type - Standards Track - status - Deprecated - date - 2018-03-08 - description - - xmppId - http://jabber.org/protocol/xhtml-im - - - xep - XEP-0072 - name - SOAP Over XMPP - type - Standards Track - status - Draft - date - 2005-12-14 - description - - xmppId - http://jabber.org/protocol/soap - - - xep - XEP-0073 - name - Basic IM Protocol Suite - type - Standards Track - status - Obsolete - date - 2007-10-30 - description - - xmppId - - - - xep - XEP-0074 - name - Simple Access Control - type - Standards Track - status - Retracted - date - 2003-10-20 - description - - xmppId - http://jabber.org/protocol/sac - - - xep - XEP-0075 - name - Jabber Object Access Protocol (JOAP) - type - Standards Track - status - Deferred - date - 2003-05-22 - description - - xmppId - jabber:iq:joap - - - xep - XEP-0076 - name - Malicious Stanzas - type - Humorous - status - Active - date - 2003-04-01 - description - - xmppId - http://jabber.org/protocol/evil - - - xep - XEP-0077 - name - In-Band Registration - type - Standards Track - status - Final - date - 2012-01-25 - description - - xmppId - jabber:iq:register - - - xep - XEP-0078 - name - Non-SASL Authentication - type - Standards Track - status - Obsolete - date - 2008-10-29 - description - - xmppId - jabber:iq:auth - - - xep - XEP-0079 - name - Advanced Message Processing - type - Standards Track - status - Draft - date - 2005-11-30 - description - - xmppId - http://jabber.org/protocol/amp - - - xep - XEP-0080 - name - User Location - type - Standards Track - status - Draft - date - 2015-12-01 - description - - xmppId - http://jabber.org/protocol/geoloc - - - xep - XEP-0081 - name - Jabber MIME Type - type - Standards Track - status - Retracted - date - 2005-07-19 - description - - xmppId - - - - xep - XEP-0082 - name - XMPP Date and Time Profiles - type - Informational - status - Active - date - 2013-09-26 - description - - xmppId - - - - xep - XEP-0083 - name - Nested Roster Groups - type - Informational - status - Active - date - 2004-10-11 - description - - xmppId - roster:delimiter - - - xep - XEP-0084 - name - User Avatar - type - Standards Track - status - Draft - date - 2016-07-09 - description - - xmppId - - - - xep - XEP-0085 - name - Chat State Notifications - type - Standards Track - status - Final - date - 2009-09-23 - description - - xmppId - http://jabber.org/protocol/chatstates - - - xep - XEP-0086 - name - Error Condition Mappings - type - Informational - status - Deprecated - date - 2004-02-17 - description - - xmppId - - - - xep - XEP-0087 - name - Stream Initiation - type - Standards Track - status - Retracted - date - 2003-05-22 - description - - xmppId - http://jabber.org/protocol/si - - - xep - XEP-0088 - name - Client Webtabs - type - Informational - status - Deferred - date - 2004-03-14 - description - - xmppId - http://jabber.org/protocol/webtab - - - xep - XEP-0089 - name - Generic Alerts - type - Standards Track - status - Deferred - date - 2003-05-16 - description - - xmppId - http://jabber.org/protocol/alert - - - xep - XEP-0090 - name - Legacy Entity Time - type - Historical - status - Obsolete - date - 2009-05-27 - description - - xmppId - jabber:iq:time - - - xep - XEP-0091 - name - Legacy Delayed Delivery - type - Historical - status - Obsolete - date - 2009-05-27 - description - - xmppId - jabber:x:delay - - - xep - XEP-0092 - name - Software Version - type - Standards Track - status - Draft - date - 2007-02-15 - description - - xmppId - jabber:iq:version - - - xep - XEP-0093 - name - Roster Item Exchange - type - Historical - status - Deprecated - date - 2005-08-26 - description - - xmppId - - - - xep - XEP-0094 - name - Agent Information - type - Historical - status - Obsolete - date - 2003-10-08 - description - - xmppId - jabber:iq:agents - - - xep - XEP-0095 - name - Stream Initiation - type - Standards Track - status - Deprecated - date - 2017-11-29 - description - - xmppId - http://jabber.org/protocol/si - - - xep - XEP-0096 - name - SI File Transfer - type - Standards Track - status - Deprecated - date - 2017-11-29 - description - - xmppId - http://jabber.org/protocol/si/profile/file-transfer - - - xep - XEP-0097 - name - iCal Envelope - type - Standards Track - status - Deferred - date - 2003-06-10 - description - - xmppId - http://jabber.org/protocol/gw/ical - - - xep - XEP-0098 - name - Enhanced Private XML Storage - type - Standards Track - status - Deferred - date - 2003-06-25 - description - - xmppId - http://jabber.org/protocol/private-xml - - - xep - XEP-0099 - name - IQ Query Action Protocol - type - Standards Track - status - Deferred - date - 2003-06-25 - description - - xmppId - - - - xep - XEP-0100 - name - Gateway Interaction - type - Informational - status - Active - date - 2005-10-05 - description - - xmppId - - - - xep - XEP-0101 - name - HTTP Authentication using Jabber Tickets - type - Standards Track - status - Deferred - date - 2004-01-18 - description - - xmppId - - - - xep - XEP-0102 - name - Security Extensions - type - Standards Track - status - Deferred - date - 2003-06-25 - description - - xmppId - xmpp:sec - - - xep - XEP-0103 - name - URL Address Information - type - Standards Track - status - Deferred - date - 2004-01-20 - description - - xmppId - - - - xep - XEP-0104 - name - HTTP Scheme for URL Data - type - Standards Track - status - Deferred - date - 2004-01-20 - description - - xmppId - - - - xep - XEP-0105 - name - Tree Transfer Stream Initiation Profile - type - Standards Track - status - Deferred - date - 2003-09-22 - description - - xmppId - - - - xep - XEP-0106 - name - JID Escaping - type - Standards Track - status - Draft - date - 2016-07-08 - description - - xmppId - jid\20escaping - - - xep - XEP-0107 - name - User Mood - type - Standards Track - status - Draft - date - 2018-03-13 - description - - xmppId - http://jabber.org/protocol/mood - - - xep - XEP-0108 - name - User Activity - type - Standards Track - status - Draft - date - 2008-10-29 - description - - xmppId - http://jabber.org/protocol/activity - - - xep - XEP-0109 - name - Out-of-Office Messages - type - Standards Track - status - Deferred - date - 2010-05-24 - description - - xmppId - - - - xep - XEP-0110 - name - Generic Maps - type - Standards Track - status - Deferred - date - 2003-07-28 - description - - xmppId - http://jabber.org/protocol/map - - - xep - XEP-0111 - name - A Transport for Initiating and Negotiating Sessions (TINS) - type - Standards Track - status - Retracted - date - 2005-12-21 - description - - xmppId - http://jabber.org/protocol/tins - - - xep - XEP-0112 - name - User Physical Location - type - Standards Track - status - Obsolete - date - 2004-10-12 - description - - xmppId - http://jabber.org/protocol/physloc - - - xep - XEP-0113 - name - Simple Whiteboarding - type - Informational - status - Deferred - date - 2003-09-07 - description - - xmppId - http://jabber.org/protocol/swb - - - xep - XEP-0114 - name - Jabber Component Protocol - type - Historical - status - Active - date - 2012-01-25 - description - - xmppId - - - - xep - XEP-0115 - name - Entity Capabilities - type - Standards Track - status - Draft - date - 2016-10-06 - description - - xmppId - http://jabber.org/protocol/caps - - - xep - XEP-0116 - name - Encrypted Session Negotiation - type - Standards Track - status - Deferred - date - 2007-05-30 - description - - xmppId - http://www.xmpp.org/extensions/xep-0116.html#ns - - - xep - XEP-0117 - name - Intermediate IM Protocol Suite - type - Standards Track - status - Obsolete - date - 2007-10-30 - description - - xmppId - - - - xep - XEP-0118 - name - User Tune - type - Standards Track - status - Draft - date - 2008-01-30 - description - - xmppId - http://jabber.org/protocol/tune - - - xep - XEP-0119 - name - Extended Presence Protocol Suite - type - Standards Track - status - Retracted - date - 2006-08-08 - description - - xmppId - - - - xep - XEP-0120 - name - Infobits - type - Standards Track - status - Retracted - date - 2004-01-22 - description - - xmppId - http://jabber.org/protocol/infobits - - - xep - XEP-0121 - name - Dublin Core Infobits Mapping - type - Informational - status - Retracted - date - 2003-12-15 - description - - xmppId - - - - xep - XEP-0122 - name - Data Forms Validation - type - Standards Track - status - Draft - date - 2018-03-21 - description - - xmppId - http://jabber.org/protocol/xdata-validate - - - xep - XEP-0123 - name - Entity Metadata - type - Standards Track - status - Retracted - date - 2003-12-16 - description - - xmppId - - - - xep - XEP-0124 - name - Bidirectional-streams Over Synchronous HTTP (BOSH) - type - Standards Track - status - Draft - date - 2016-11-16 - description - - xmppId - - - - xep - XEP-0125 - name - vCard Infobits Mapping - type - Informational - status - Retracted - date - 2003-12-15 - description - - xmppId - - - - xep - XEP-0126 - name - Invisibility - type - Informational - status - Deprecated - date - 2005-08-19 - description - - xmppId - - - - xep - XEP-0127 - name - Common Alerting Protocol (CAP) Over XMPP - type - Informational - status - Active - date - 2004-12-09 - description - - xmppId - http://www.incident.com/cap/1.0 - - - xep - XEP-0128 - name - Service Discovery Extensions - type - Informational - status - Active - date - 2004-10-20 - description - - xmppId - - - - xep - XEP-0129 - name - WebDAV File Transfers - type - Standards Track - status - Deferred - date - 2007-04-19 - description - - xmppId - http://www.xmpp.org/extensions/xep-0129.html#ns - - - xep - XEP-0130 - name - Waiting Lists - type - Historical - status - Deprecated - date - 2012-04-18 - description - - xmppId - http://jabber.org/protocol/waitinglist - - - xep - XEP-0131 - name - Stanza Headers and Internet Metadata - type - Standards Track - status - Draft - date - 2006-07-12 - description - - xmppId - http://jabber.org/protocol/shim - - - xep - XEP-0132 - name - Presence Obtained via Kinesthetic Excitation (POKE) - type - Humorous - status - Active - date - 2004-04-01 - description - - xmppId - http://jabber.org/protocol/poke - - - xep - XEP-0133 - name - Service Administration - type - Informational - status - Active - date - 2017-07-15 - description - - xmppId - - - - xep - XEP-0134 - name - XMPP Design Guidelines - type - Informational - status - Active - date - 2004-12-09 - description - - xmppId - - - - xep - XEP-0135 - name - File Sharing - type - Standards Track - status - Deferred - date - 2004-06-04 - description - - xmppId - http://jabber.org/protocol/files - - - xep - XEP-0136 - name - Message Archiving - type - Standards Track - status - Deprecated - date - 2017-11-15 - description - - xmppId - urn:xmpp:archive* - - - xep - XEP-0137 - name - Publishing Stream Initiation Requests - type - Standards Track - status - Deprecated - date - 2018-02-28 - description - - xmppId - http://jabber.org/protocol/sipub - - - xep - XEP-0138 - name - Stream Compression - type - Standards Track - status - Final - date - 2009-05-27 - description - - xmppId - http://jabber.org/features/compress - - - xep - XEP-0139 - name - Security SIG - type - SIG Formation - status - Retracted - date - 2004-09-15 - description - - xmppId - - - - xep - XEP-0140 - name - Shared Groups - type - Informational - status - Retracted - date - 2004-10-27 - description - - xmppId - - - - xep - XEP-0141 - name - Data Forms Layout - type - Standards Track - status - Draft - date - 2005-05-12 - description - - xmppId - - - - xep - XEP-0142 - name - Workgroup Queues - type - Standards Track - status - Deferred - date - 2005-05-09 - description - - xmppId - http://jabber.org/protocol/workgroup - - - xep - XEP-0143 - name - Guidelines for Authors of XMPP Extension Protocols - type - Procedural - status - Active - date - 2016-12-02 - description - - xmppId - - - - xep - XEP-0144 - name - Roster Item Exchange - type - Standards Track - status - Draft - date - 2017-11-28 - description - - xmppId - http://jabber.org/protocol/rosterx - - - xep - XEP-0145 - name - Annotations - type - Historical - status - Active - date - 2006-03-23 - description - - xmppId - - - - xep - XEP-0146 - name - Remote Controlling Clients - type - Informational - status - Obsolete - date - 2017-11-07 - description - - xmppId - http://jabber.org/protocol/rc* - - - xep - XEP-0147 - name - XMPP URI Scheme Query Components - type - Informational - status - Active - date - 2006-09-13 - description - - xmppId - - - - xep - XEP-0148 - name - Instant Messaging Intelligence Quotient (IM IQ) - type - Humorous - status - Active - date - 2005-04-01 - description - - xmppId - jabber:iq:iq - - - xep - XEP-0149 - name - Time Periods - type - Informational - status - Active - date - 2006-01-24 - description - - xmppId - - - - xep - XEP-0150 - name - Use of Entity Tags in XMPP Extensions - type - Informational - status - Deferred - date - 2005-08-09 - description - - xmppId - - - - xep - XEP-0151 - name - Virtual Presence - type - Standards Track - status - Deferred - date - 2005-07-05 - description - - xmppId - - - - xep - XEP-0152 - name - Reachability Addresses - type - Standards Track - status - Draft - date - 2014-02-25 - description - - xmppId - urn:xmpp:reach:0 - - - xep - XEP-0153 - name - vCard-Based Avatars - type - Historical - status - Active - date - 2018-02-26 - description - - xmppId - - - - xep - XEP-0154 - name - User Profile - type - Standards Track - status - Deferred - date - 2008-04-18 - description - - xmppId - urn:xmpp:tmp:profile - - - xep - XEP-0155 - name - Stanza Session Negotiation - type - Standards Track - status - Draft - date - 2016-01-20 - description - - xmppId - http://jabber.org/protocol/feature-neg - - - xep - XEP-0156 - name - Discovering Alternative XMPP Connection Methods - type - Standards Track - status - Draft - date - 2018-07-21 - description - - xmppId - - - - xep - XEP-0157 - name - Contact Addresses for XMPP Services - type - Informational - status - Active - date - 2018-07-21 - description - - xmppId - - - - xep - XEP-0158 - name - CAPTCHA Forms - type - Standards Track - status - Draft - date - 2008-09-03 - description - - xmppId - - - - xep - XEP-0159 - name - Spim-Blocking Control - type - Standards Track - status - Deferred - date - 2006-07-11 - description - - xmppId - http://www.xmpp.org/extensions/xep-0159.html* - - - xep - XEP-0160 - name - Best Practices for Handling Offline Messages - type - Informational - status - Active - date - 2016-10-07 - description - - xmppId - msgoffline - - - xep - XEP-0161 - name - Abuse Reporting - type - Standards Track - status - Deferred - date - 2007-05-06 - description - - xmppId - urn:xmpp:tmp:abuse - - - xep - XEP-0162 - name - Best Practices for Roster and Subscription Management - type - Informational - status - Deferred - date - 2005-12-06 - description - - xmppId - - - - xep - XEP-0163 - name - Personal Eventing Protocol - type - Standards Track - status - Draft - date - 2018-03-18 - description - - xmppId - - - - xep - XEP-0164 - name - vCard Filtering - type - Standards Track - status - Deferred - date - 2005-11-16 - description - - xmppId - - - - xep - XEP-0165 - name - Best Practices to Discourage JID Mimicking - type - Informational - status - Deferred - date - 2007-12-13 - description - - xmppId - - - - xep - XEP-0166 - name - Jingle - type - Standards Track - status - Draft - date - 2016-05-17 - description - - xmppId - urn:xmpp:jingle:1 - - - xep - XEP-0167 - name - Jingle RTP Sessions - type - Standards Track - status - Draft - date - 2016-07-08 - description - - xmppId - - - - xep - XEP-0168 - name - Resource Application Priority - type - Standards Track - status - Deferred - date - 2008-09-26 - description - - xmppId - urn:xmpp:rap:0 - - - xep - XEP-0169 - name - Twas The Night Before Christmas (Jabber Version) - type - Humorous - status - Active - date - 2009-12-24 - description - - xmppId - - - - xep - XEP-0170 - name - Recommended Order of Stream Feature Negotiation - type - Informational - status - Active - date - 2007-01-04 - description - - xmppId - - - - xep - XEP-0171 - name - Language Translation - type - Standards Track - status - Draft - date - 2015-10-15 - description - - xmppId - urn:xmpp:langtrans - - - xep - XEP-0172 - name - User Nickname - type - Standards Track - status - Draft - date - 2012-03-21 - description - - xmppId - http://jabber.org/protocol/nick - - - xep - XEP-0173 - name - Pubsub Subscription Storage - type - Historical - status - Deferred - date - 2006-02-09 - description - - xmppId - storage:pubsubs - - - xep - XEP-0174 - name - Serverless Messaging - type - Standards Track - status - Final - date - 2018-02-08 - description - - xmppId - - - - xep - XEP-0175 - name - Best Practices for Use of SASL ANONYMOUS - type - Informational - status - Active - date - 2009-09-30 - description - - xmppId - - - - xep - XEP-0176 - name - Jingle ICE-UDP Transport Method - type - Standards Track - status - Draft - date - 2009-06-10 - description - - xmppId - urn:xmpp:jingle:transports:ice-udp* - - - xep - XEP-0177 - name - Jingle Raw UDP Transport Method - type - Standards Track - status - Draft - date - 2009-12-23 - description - - xmppId - urn:xmpp:jingle:transports:raw-udp* - - - xep - XEP-0178 - name - Best Practices for Use of SASL EXTERNAL with Certificates - type - Informational - status - Active - date - 2011-05-25 - description - - xmppId - - - - xep - XEP-0179 - name - Jingle IAX Transport Method - type - Standards Track - status - Deferred - date - 2006-03-23 - description - - xmppId - - - - xep - XEP-0180 - name - Jingle Video via RTP - type - Standards Track - status - Retracted - date - 2008-06-04 - description - - xmppId - urn:xmpp:tmp:jingle:apps:video-rtp - - - xep - XEP-0181 - name - Jingle DTMF - type - Standards Track - status - Deferred - date - 2009-10-02 - description - - xmppId - - - - xep - XEP-0182 - name - Application-Specific Error Conditions - type - Procedural - status - Active - date - 2008-03-05 - description - - xmppId - - - - xep - XEP-0183 - name - Jingle Telepathy Transport - type - Humorous - status - Active - date - 2006-04-01 - description - - xmppId - - - - xep - XEP-0184 - name - Message Delivery Receipts - type - Standards Track - status - Draft - date - 2011-03-01 - description - - xmppId - urn:xmpp:receipts - - - xep - XEP-0185 - name - Dialback Key Generation and Validation - type - Informational - status - Active - date - 2007-02-15 - description - - xmppId - - - - xep - XEP-0186 - name - Invisible Command - type - Standards Track - status - Proposed - date - 2017-11-29 - description - - xmppId - urn:xmpp:invisible:1 - - - xep - XEP-0187 - name - Offline Encrypted Sessions - type - Standards Track - status - Deferred - date - 2007-05-30 - description - - xmppId - http://www.xmpp.org/extensions/xep-0187.html#ns - - - xep - XEP-0188 - name - Cryptographic Design of Encrypted Sessions - type - Informational - status - Deferred - date - 2007-05-30 - description - - xmppId - - - - xep - XEP-0189 - name - Public Key Publishing - type - Standards Track - status - Deferred - date - 2010-07-15 - description - - xmppId - urn:xmpp:pubkey:2 - - - xep - XEP-0190 - name - Best Practice for Closing Idle Streams - type - Informational - status - Obsolete - date - 2012-03-06 - description - - xmppId - - - - xep - XEP-0191 - name - Blocking Command - type - Standards Track - status - Draft - date - 2015-03-12 - description - - xmppId - urn:xmpp:blocking - - - xep - XEP-0192 - name - Proposed Stream Feature Improvements - type - Standards Track - status - Obsolete - date - 2012-02-08 - description - - xmppId - - - - xep - XEP-0193 - name - Proposed Resource Binding Improvements - type - Standards Track - status - Obsolete - date - 2012-02-08 - description - - xmppId - - - - xep - XEP-0194 - name - User Chatting - type - Standards Track - status - Deferred - date - 2008-09-25 - description - - xmppId - urn:xmpp:chatting:0 - - - xep - XEP-0195 - name - User Browsing - type - Standards Track - status - Deferred - date - 2008-09-25 - description - - xmppId - urn:xmpp:browsing:0 - - - xep - XEP-0196 - name - User Gaming - type - Standards Track - status - Deferred - date - 2008-09-25 - description - - xmppId - urn:xmpp:gaming:0 - - - xep - XEP-0197 - name - User Viewing - type - Standards Track - status - Deferred - date - 2008-09-25 - description - - xmppId - urn:xmpp:viewing:0 - - - xep - XEP-0198 - name - Stream Management - type - Standards Track - status - Draft - date - 2018-07-19 - description - - xmppId - urn:xmpp:sm:3 - - - xep - XEP-0199 - name - XMPP Ping - type - Standards Track - status - Final - date - 2009-06-03 - description - - xmppId - urn:xmpp:ping - - - xep - XEP-0200 - name - Stanza Encryption - type - Standards Track - status - Deferred - date - 2007-05-30 - description - - xmppId - http://www.xmpp.org/extensions/xep-0200.html* - - - xep - XEP-0201 - name - Best Practices for Message Threads - type - Informational - status - Active - date - 2010-11-29 - description - - xmppId - - - - xep - XEP-0202 - name - Entity Time - type - Standards Track - status - Final - date - 2009-09-11 - description - - xmppId - urn:xmpp:time - - - xep - XEP-0203 - name - Delayed Delivery - type - Standards Track - status - Final - date - 2009-09-15 - description - - xmppId - urn:xmpp:delay - - - xep - XEP-0204 - name - Collaborative Data Objects - type - Standards Track - status - Deferred - date - 2007-01-17 - description - - xmppId - http://www.xmpp.org/extensions/xep-0204.html* - - - xep - XEP-0205 - name - Best Practices to Discourage Denial of Service Attacks - type - Informational - status - Active - date - 2009-01-07 - description - - xmppId - - - - xep - XEP-0206 - name - XMPP Over BOSH - type - Standards Track - status - Draft - date - 2014-04-09 - description - - xmppId - - - - xep - XEP-0207 - name - XMPP Eventing via Pubsub - type - Humorous - status - Active - date - 2007-04-01 - description - - xmppId - - - - xep - XEP-0208 - name - Bootstrapping Implementation of Jingle - type - Informational - status - Retracted - date - 2009-01-06 - description - - xmppId - - - - xep - XEP-0209 - name - Metacontacts - type - Standards Track - status - Deferred - date - 2007-04-10 - description - - xmppId - - - - xep - XEP-0210 - name - Requirements for Encrypted Sessions - type - Standards Track - status - Deferred - date - 2007-05-30 - description - - xmppId - - - - xep - XEP-0211 - name - XMPP Basic Client 2008 - type - Standards Track - status - Obsolete - date - 2007-07-11 - description - - xmppId - - - - xep - XEP-0212 - name - XMPP Basic Server 2008 - type - Standards Track - status - Obsolete - date - 2007-07-11 - description - - xmppId - - - - xep - XEP-0213 - name - XMPP Intermediate IM Client 2008 - type - Standards Track - status - Obsolete - date - 2007-07-11 - description - - xmppId - - - - xep - XEP-0214 - name - File Repository and Sharing - type - Standards Track - status - Deferred - date - 2009-01-05 - description - - xmppId - - - - xep - XEP-0215 - name - External Service Discovery - type - Standards Track - status - Deferred - date - 2015-10-20 - description - - xmppId - urn:xmpp:extdisco:2 - - - xep - XEP-0216 - name - XMPP Intermediate IM Server 2008 - type - Standards Track - status - Obsolete - date - 2007-07-11 - description - - xmppId - - - - xep - XEP-0217 - name - Simplified Encrypted Session Negotiation - type - Standards Track - status - Deferred - date - 2007-05-30 - description - - xmppId - - - - xep - XEP-0218 - name - Bootstrapping Implementation of Encrypted Sessions - type - Informational - status - Deferred - date - 2007-05-30 - description - - xmppId - - - - xep - XEP-0219 - name - Hop Check - type - Standards Track - status - Retracted - date - 2008-06-12 - description - - xmppId - http://www.xmpp.org/extensions/xep-0219.html#ns - - - xep - XEP-0220 - name - Server Dialback - type - Standards Track - status - Draft - date - 2015-03-12 - description - - xmppId - - - - xep - XEP-0221 - name - Data Forms Media Element - type - Standards Track - status - Draft - date - 2008-09-03 - description - - xmppId - urn:xmpp:media-element - - - xep - XEP-0222 - name - Persistent Storage of Public Data via PubSub - type - Informational - status - Active - date - 2008-09-08 - description - - xmppId - - - - xep - XEP-0223 - name - Persistent Storage of Private Data via PubSub - type - Informational - status - Active - date - 2018-03-28 - description - - xmppId - - - - xep - XEP-0224 - name - Attention - type - Standards Track - status - Draft - date - 2008-11-13 - description - - xmppId - urn:xmpp:attention:0 - - - xep - XEP-0225 - name - Component Connections - type - Standards Track - status - Deferred - date - 2008-10-06 - description - - xmppId - urn:xmpp:component:0 - - - xep - XEP-0226 - name - Message Stanza Profiles - type - Informational - status - Deferred - date - 2008-11-05 - description - - xmppId - - - - xep - XEP-0227 - name - Portable Import/Export Format for XMPP-IM Servers - type - Standards Track - status - Draft - date - 2010-03-12 - description - - xmppId - - - - xep - XEP-0228 - name - Requirements for Shared Editing - type - Standards Track - status - Deferred - date - 2007-08-22 - description - - xmppId - - - - xep - XEP-0229 - name - Stream Compression with LZW - type - Standards Track - status - Draft - date - 2007-09-26 - description - - xmppId - - - - xep - XEP-0230 - name - Service Discovery Notifications - type - Standards Track - status - Deferred - date - 2016-10-04 - description - - xmppId - - - - xep - XEP-0231 - name - Bits of Binary - type - Standards Track - status - Draft - date - 2008-09-03 - description - - xmppId - urn:xmpp:bob - - - xep - XEP-0232 - name - Software Information - type - Standards Track - status - Deferred - date - 2009-02-26 - description - - xmppId - - - - xep - XEP-0233 - name - XMPP Server Registration for use with Kerberos V5 - type - Standards Track - status - Draft - date - 2017-03-16 - description - - xmppId - - - - xep - XEP-0234 - name - Jingle File Transfer - type - Standards Track - status - Proposed - date - 2017-08-24 - description - - xmppId - urn:xmpp:jingle:apps:file-transfer:5 - - - xep - XEP-0235 - name - OAuth Over XMPP - type - Standards Track - status - Deferred - date - 2009-03-24 - description - - xmppId - urn:xmpp:oauth:0 - - - xep - XEP-0236 - name - Abuse Reporting - type - Standards Track - status - Retracted - date - 2008-05-09 - description - - xmppId - urn:xmpp:tmp:abuse - - - xep - XEP-0237 - name - Roster Versioning - type - Standards Track - status - Obsolete - date - 2012-02-08 - description - - xmppId - urn:xmpp:features:rosterver - - - xep - XEP-0238 - name - XMPP Protocol Flows for Inter-Domain Federation - type - Informational - status - Deferred - date - 2008-03-31 - description - - xmppId - - - - xep - XEP-0239 - name - Binary XMPP - type - Humorous - status - Active - date - 2008-04-01 - description - - xmppId - - - - xep - XEP-0240 - name - Auto-Discovery of JabberIDs - type - Standards Track - status - Deferred - date - 2008-04-30 - description - - xmppId - - - - xep - XEP-0241 - name - Encryption of Archived Messages - type - Standards Track - status - Deferred - date - 2008-04-30 - description - - xmppId - urn:xmpp:tmp:archive:encrypt - - - xep - XEP-0242 - name - XMPP Client Compliance 2009 - type - Standards Track - status - Obsolete - date - 2008-09-08 - description - - xmppId - - - - xep - XEP-0243 - name - XMPP Server Compliance 2009 - type - Standards Track - status - Obsolete - date - 2008-09-08 - description - - xmppId - - - - xep - XEP-0244 - name - IO Data - type - Standards Track - status - Deferred - date - 2008-06-18 - description - - xmppId - urn:xmpp:tmp:io-data - - - xep - XEP-0245 - name - The /me Command - type - Informational - status - Active - date - 2009-01-21 - description - - xmppId - - - - xep - XEP-0246 - name - End-to-End XML Streams - type - Standards Track - status - Deferred - date - 2016-01-20 - description - - xmppId - - - - xep - XEP-0247 - name - Jingle XML Streams - type - Standards Track - status - Deferred - date - 2009-02-20 - description - - xmppId - - - - xep - XEP-0248 - name - PubSub Collection Nodes - type - Standards Track - status - Deferred - date - 2010-09-28 - description - - xmppId - http://jabber.org/protocol/pubsub#collections - - - xep - XEP-0249 - name - Direct MUC Invitations - type - Standards Track - status - Draft - date - 2011-09-22 - description - - xmppId - - - - xep - XEP-0250 - name - C2C Authentication Using TLS - type - Standards Track - status - Deferred - date - 2008-09-08 - description - - xmppId - - - - xep - XEP-0251 - name - Jingle Session Transfer - type - Standards Track - status - Deferred - date - 2009-10-05 - description - - xmppId - - - - xep - XEP-0252 - name - BOSH Script Syntax - type - Historical - status - Deferred - date - 2008-10-31 - description - - xmppId - - - - xep - XEP-0253 - name - PubSub Chaining - type - Standards Track - status - Deferred - date - 2009-11-18 - description - - xmppId - http://jabber.org/protocol/pubsub#chaining - - - xep - XEP-0254 - name - PubSub Queueing - type - Standards Track - status - Deferred - date - 2008-11-13 - description - - xmppId - urn:xmpp:pubsub:queueing:0 - - - xep - XEP-0255 - name - Location Query - type - Standards Track - status - Deferred - date - 2009-04-09 - description - - xmppId - urn:xmpp:locationquery:0 - - - xep - XEP-0256 - name - Last Activity in Presence - type - Standards Track - status - Draft - date - 2009-09-15 - description - - xmppId - jabber:iq:last - - - xep - XEP-0257 - name - Client Certificate Management for SASL EXTERNAL - type - Standards Track - status - Deferred - date - 2012-07-18 - description - - xmppId - urn:xmpp:saslcert:1 - - - xep - XEP-0258 - name - Security Labels in XMPP - type - Standards Track - status - Draft - date - 2013-04-08 - description - - xmppId - urn:xmpp:sec-label:0 - - - xep - XEP-0259 - name - Message Mine-ing - type - Standards Track - status - Deferred - date - 2009-01-21 - description - - xmppId - urn:xmpp:tmp:mine:0 - - - xep - XEP-0260 - name - Jingle SOCKS5 Bytestreams Transport Method - type - Standards Track - status - Draft - date - 2018-05-04 - description - - xmppId - urn:xmpp:jingle:transports:s5b:1 - - - xep - XEP-0261 - name - Jingle In-Band Bytestreams Transport Method - type - Standards Track - status - Draft - date - 2011-09-23 - description - - xmppId - urn:xmpp:jingle:transports:ibb:* - - - xep - XEP-0262 - name - Use of ZRTP in Jingle RTP Sessions - type - Standards Track - status - Draft - date - 2011-06-15 - description - - xmppId - urn:xmpp:jingle:apps:rtp:zrtp:* - - - xep - XEP-0263 - name - ECO-XMPP - type - Humorous - status - Active - date - 2009-04-01 - description - - xmppId - - - - xep - XEP-0264 - name - Jingle Content Thumbnails - type - Standards Track - status - Deferred - date - 2015-08-26 - description - - xmppId - urn:xmpp:thumbs:1 - - - xep - XEP-0265 - name - Out-of-Band Stream Data - type - Standards Track - status - Deferred - date - 2009-04-02 - description - - xmppId - urn:xmpp:jingle:apps:out-of-band:0 - - - xep - XEP-0266 - name - Codecs for Jingle Audio - type - Standards Track - status - Draft - date - 2013-03-01 - description - - xmppId - - - - xep - XEP-0267 - name - Server Buddies - type - Standards Track - status - Deferred - date - 2012-05-29 - description - - xmppId - urn:xmpp:server-presence - - - xep - XEP-0268 - name - Incident Handling - type - Standards Track - status - Deferred - date - 2012-05-29 - description - - xmppId - urn:xmpp:incident:2 - - - xep - XEP-0269 - name - Jingle Early Media - type - Standards Track - status - Deferred - date - 2009-05-19 - description - - xmppId - - - - xep - XEP-0270 - name - XMPP Compliance Suites 2010 - type - Standards Track - status - Obsolete - date - 2017-01-28 - description - - xmppId - - - - xep - XEP-0271 - name - XMPP Nodes - type - Informational - status - Deferred - date - 2009-06-26 - description - - xmppId - - - - xep - XEP-0272 - name - Multiparty Jingle (Muji) - type - Standards Track - status - Deferred - date - 2009-09-11 - description - - xmppId - http://telepathy.freedesktop.org/muji - - - xep - XEP-0273 - name - Stanza Interception and Filtering Technology (SIFT) - type - Standards Track - status - Deferred - date - 2011-06-27 - description - - xmppId - urn:xmpp:sift:* - - - xep - XEP-0274 - name - Design Considerations for Digital Signatures in XMPP - type - Informational - status - Deferred - date - 2011-01-28 - description - - xmppId - - - - xep - XEP-0275 - name - Entity Reputation - type - Standards Track - status - Deferred - date - 2012-06-06 - description - - xmppId - urn:xmpp:reputation:0 - - - xep - XEP-0276 - name - Presence Decloaking - type - Standards Track - status - Deferred - date - 2012-07-13 - description - - xmppId - urn:xmpp:decloak:0 - - - xep - XEP-0277 - name - Microblogging over XMPP - type - Standards Track - status - Deferred - date - 2017-11-28 - description - - xmppId - - - - xep - XEP-0278 - name - Jingle Relay Nodes - type - Standards Track - status - Experimental - date - 2017-09-14 - description - - xmppId - http://jabber.org/protocol/jinglenodes* - - - xep - XEP-0279 - name - Server IP Check - type - Standards Track - status - Deferred - date - 2013-04-17 - description - - xmppId - urn:xmpp:sic:1 - - - xep - XEP-0280 - name - Message Carbons - type - Standards Track - status - Proposed - date - 2017-02-16 - description - - xmppId - urn:xmpp:carbons:2 - - - xep - XEP-0281 - name - DMUC1: Distributed Multi-User Chat - type - Standards Track - status - Retracted - date - 2010-07-20 - description - - xmppId - - - - xep - XEP-0282 - name - DMUC2: Distributed MUC - type - Standards Track - status - Deferred - date - 2010-06-11 - description - - xmppId - - - - xep - XEP-0283 - name - Moved - type - Standards Track - status - Experimental - date - 2018-08-06 - description - - xmppId - urn:xmpp:moved:0 - - - xep - XEP-0284 - name - Shared XML Editing - type - Standards Track - status - Deferred - date - 2010-07-02 - description - - xmppId - urn:xmpp:sxe:0 - - - xep - XEP-0285 - name - Encapsulating Digital Signatures in XMPP - type - Standards Track - status - Deferred - date - 2011-01-12 - description - - xmppId - urn:xmpp:signed:0 - - - xep - XEP-0286 - name - Mobile Considerations on LTE Networks - type - Informational - status - Active - date - 2018-01-25 - description - - xmppId - - - - xep - XEP-0287 - name - Spim Markers and Reports - type - Standards Track - status - Deferred - date - 2010-10-03 - description - - xmppId - urn:xmpp:spim-report:0 - - - xep - XEP-0287 - name - Spim Markers and Reports - type - Standards Track - status - Deferred - date - 2010-10-04 - description - - xmppId - urn:xmpp:spim-marker:0 - - - xep - XEP-0288 - name - Bidirectional Server-to-Server Connections - type - Standards Track - status - Draft - date - 2016-10-17 - description - - xmppId - urn:xmpp:bidi - - - xep - XEP-0289 - name - Federated MUC for Constrained Environments - type - Standards Track - status - Deferred - date - 2012-05-29 - description - - xmppId - http://isode.com/protocol/fmuc - - - xep - XEP-0290 - name - Encapsulated Digital Signatures in XMPP - type - Standards Track - status - Deferred - date - 2011-01-28 - description - - xmppId - urn:xmpp:dsig:0 - - - xep - XEP-0291 - name - Service Delegation - type - Standards Track - status - Deferred - date - 2011-01-26 - description - - xmppId - urn:xmpp:tmp:delegate - - - xep - XEP-0292 - name - vCard4 Over XMPP - type - Standards Track - status - Deferred - date - 2013-09-12 - description - - xmppId - urn:ietf:params:xml:ns:vcard-4.0 - - - xep - XEP-0293 - name - Jingle RTP Feedback Negotiation - type - Standards Track - status - Draft - date - 2015-08-11 - description - - xmppId - urn:xmpp:jingle:apps:rtp:rtcp-fb:0 - - - xep - XEP-0294 - name - Jingle RTP Header Extensions Negotiation - type - Standards Track - status - Draft - date - 2015-08-11 - description - - xmppId - urn:xmpp:jingle:apps:rtp:rtp-hdrext:0 - - - xep - XEP-0295 - name - JSON Encodings for XMPP - type - Humorous - status - Active - date - 2011-04-01 - description - - xmppId - - - - xep - XEP-0296 - name - Best Practices for Resource Locking - type - Informational - status - Deferred - date - 2011-08-18 - description - - xmppId - - - - xep - XEP-0297 - name - Stanza Forwarding - type - Standards Track - status - Draft - date - 2013-10-02 - description - - xmppId - urn:xmpp:forward:0 - - - xep - XEP-0298 - name - Delivering Conference Information to Jingle Participants (Coin) - type - Standards Track - status - Deferred - date - 2015-07-02 - description - - xmppId - urn:xmpp:coin:1 - - - xep - XEP-0299 - name - Codecs for Jingle Video - type - Standards Track - status - Deferred - date - 2011-06-12 - description - - xmppId - - - - xep - XEP-0300 - name - Use of Cryptographic Hash Functions in XMPP - type - Standards Track - status - Experimental - date - 2018-02-14 - description - - xmppId - urn:xmpp:hashes:2 - - - xep - XEP-0301 - name - In-Band Real Time Text - type - Standards Track - status - Draft - date - 2013-10-08 - description - - xmppId - urn:xmpp:rtt:0 - - - xep - XEP-0302 - name - XMPP Compliance Suites 2012 - type - Standards Track - status - Obsolete - date - 2011-07-21 - description - - xmppId - - - - xep - XEP-0303 - name - Commenting - type - Standards Track - status - Deferred - date - 2011-07-28 - description - - xmppId - urn:xmpp:tmp:comments:0 - - - xep - XEP-0304 - name - Whitespace Keepalive Negotiation - type - Standards Track - status - Deferred - date - 2011-08-18 - description - - xmppId - urn:xmpp:keepalive:0 - - - xep - XEP-0305 - name - XMPP Quickstart - type - Standards Track - status - Deferred - date - 2013-03-01 - description - - xmppId - - - - xep - XEP-0306 - name - Extensible Status Conditions for Multi-User Chat - type - Standards Track - status - Deferred - date - 2016-06-07 - description - - xmppId - urn:xmpp:muc:conditions:1 - - - xep - XEP-0307 - name - Unique Room Names for Multi-User Chat - type - Standards Track - status - Deferred - date - 2011-11-10 - description - - xmppId - http://jabber.org/protocol/muc#unique - - - xep - XEP-0308 - name - Last Message Correction - type - Standards Track - status - Draft - date - 2013-04-08 - description - - xmppId - urn:xmpp:message-correct:0 - - - xep - XEP-0309 - name - Service Directories - type - Standards Track - status - Deferred - date - 2012-05-29 - description - - xmppId - urn:xmpp:public-server - - - xep - XEP-0310 - name - Presence State Annotations - type - Standards Track - status - Deferred - date - 2012-01-10 - description - - xmppId - urn:xmpp:psa - - - xep - XEP-0311 - name - MUC Fast Reconnect - type - Standards Track - status - Deferred - date - 2012-01-25 - description - - xmppId - urn:xmpp:presence-session:0 - - - xep - XEP-0312 - name - PubSub Since - type - Standards Track - status - Deferred - date - 2012-05-29 - description - - xmppId - http://jabber.org/protocol/pubsub#since - - - xep - XEP-0313 - name - Message Archive Management - type - Standards Track - status - Experimental - date - 2018-07-16 - description - - xmppId - urn:xmpp:mam:2 - - - xep - XEP-0314 - name - Security Labels in PubSub - type - Standards Track - status - Deferred - date - 2012-07-27 - description - - xmppId - - - - xep - XEP-0315 - name - Data Forms XML Element - type - Standards Track - status - Deferred - date - 2012-10-15 - description - - xmppId - - - - xep - XEP-0316 - name - MUC Eventing Protocol - type - Standards Track - status - Deferred - date - 2013-01-03 - description - - xmppId - - - - xep - XEP-0317 - name - Hats - type - Standards Track - status - Deferred - date - 2013-01-03 - description - - xmppId - - - - xep - XEP-0318 - name - Best Practices for Client Initiated Presence Probes - type - Informational - status - Deferred - date - 2013-08-06 - description - - xmppId - - - - xep - XEP-0319 - name - Last User Interaction in Presence - type - Standards Track - status - Draft - date - 2017-07-17 - description - - xmppId - urn:xmpp:idle:1 - - - xep - XEP-0320 - name - Use of DTLS-SRTP in Jingle Sessions - type - Standards Track - status - Deferred - date - 2015-10-15 - description - - xmppId - urn:xmpp:jingle:apps:dtls:0 - - - xep - XEP-0321 - name - Remote Roster Management - type - Standards Track - status - Deferred - date - 2013-04-16 - description - - xmppId - urn:xmpp:tmp:roster-management:0 - - - xep - XEP-0322 - name - Efficient XML Interchange (EXI) Format - type - Standards Track - status - Deferred - date - 2018-01-25 - description - - xmppId - - - - xep - XEP-0323 - name - Internet of Things - Sensor Data - type - Standards Track - status - Retracted - date - 2017-05-20 - description - - xmppId - urn:xmpp:iot:sensordata - - - xep - XEP-0324 - name - Internet of Things - Provisioning - type - Standards Track - status - Retracted - date - 2017-05-20 - description - - xmppId - urn:xmpp:iot:provisioning - - - xep - XEP-0325 - name - Internet of Things - Control - type - Standards Track - status - Retracted - date - 2017-05-20 - description - - xmppId - urn:xmpp:iot:control - - - xep - XEP-0326 - name - Internet of Things - Concentrators - type - Standards Track - status - Retracted - date - 2017-05-20 - description - - xmppId - urn:xmpp:iot:concentrators - - - xep - XEP-0327 - name - Rayo - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:rayo:1 - - - xep - XEP-0328 - name - JID Prep - type - Standards Track - status - Deferred - date - 2013-05-28 - description - - xmppId - urn:xmpp:jidprep:0 - - - xep - XEP-0329 - name - File Information Sharing - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:fis:0 - - - xep - XEP-0330 - name - Pubsub Subscription - type - Standards Track - status - Deferred - date - 2013-06-11 - description - - xmppId - - - - xep - XEP-0331 - name - Data Forms - Color Field Types - type - Standards Track - status - Deferred - date - 2015-11-09 - description - - xmppId - - - - xep - XEP-0332 - name - HTTP over XMPP transport - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:http - - - xep - XEP-0333 - name - Chat Markers - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:chat-markers:0 - - - xep - XEP-0334 - name - Message Processing Hints - type - Standards Track - status - Deferred - date - 2018-01-25 - description - - xmppId - urn:xmpp:hints - - - xep - XEP-0335 - name - JSON Containers - type - Standards Track - status - Deferred - date - 2013-10-25 - description - - xmppId - urn:xmpp:json:0 - - - xep - XEP-0336 - name - Data Forms - Dynamic Forms - type - Standards Track - status - Deferred - date - 2015-11-09 - description - - xmppId - urn:xmpp:xdata:dynamic - - - xep - XEP-0337 - name - Event Logging over XMPP - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:eventlog - - - xep - XEP-0338 - name - Jingle Grouping Framework - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:ietf:rfc:5888 - - - xep - XEP-0339 - name - Source-Specific Media Attributes in Jingle - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:ietf:rfc:5576 - - - xep - XEP-0340 - name - COnferences with LIghtweight BRIdging (COLIBRI) - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - http://jitsi.org/protocol/colibri - - - xep - XEP-0341 - name - Rayo CPA - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:rayo:cpa:0 - - - xep - XEP-0342 - name - Rayo Fax - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:rayo:fax:1 - - - xep - XEP-0343 - name - Signaling WebRTC datachannels in Jingle - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0344 - name - Impact of TLS and DNSSEC on Dialback - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0345 - name - Form of Membership Applications - type - Procedural - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0346 - name - Form Discovery and Publishing - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0347 - name - Internet of Things - Discovery - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:iot:discovery - - - xep - XEP-0348 - name - Signing Forms - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:xdata:signature:oauth1 - - - xep - XEP-0349 - name - Rayo Clustering - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0350 - name - Data Forms Geolocation Element - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0351 - name - Recipient Server Side Notifications Filtering - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0352 - name - Client State Indication - type - Standards Track - status - Proposed - date - 2017-02-18 - description - - xmppId - urn:xmpp:csi:0 - - - xep - XEP-0353 - name - Jingle Message Initiation - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0354 - name - Customizable Message Routing - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:cmr:0 - - - xep - XEP-0355 - name - Namespace Delegation - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:delegation:1 - - - xep - XEP-0356 - name - Privileged Entity - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:privilege:1 - - - xep - XEP-0357 - name - Push Notifications - type - Standards Track - status - Experimental - date - 2017-08-24 - description - - xmppId - urn:xmpp:push:0 - - - xep - XEP-0358 - name - Publishing Available Jingle Sessions - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0359 - name - Unique and Stable Stanza IDs - type - Standards Track - status - Experimental - date - 2017-08-23 - description - - xmppId - urn:xmpp:sid:0 - - - xep - XEP-0360 - name - Nonzas (are not Stanzas) - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0361 - name - Zero Handshake Server to Server Protocol - type - Informational - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0362 - name - Raft over XMPP - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:raft - - - xep - XEP-0363 - name - HTTP File Upload - type - Standards Track - status - Proposed - date - 2018-05-30 - description - - xmppId - urn:xmpp:http:upload:0 - - - xep - XEP-0364 - name - Current Off-the-Record Messaging Usage - type - Informational - status - Deferred - date - 2017-01-28 - description - - xmppId - - - - xep - XEP-0365 - name - Server to Server communication over STANAG 5066 ARQ - type - Standards Track - status - Deferred - date - 2018-07-21 - description - - xmppId - - - - xep - XEP-0366 - name - Entity Versioning - type - Standards Track - status - Deferred - date - 2016-12-21 - description - - xmppId - urn:xmpp:entityver:0 - - - xep - XEP-0367 - name - Message Attaching - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:message-attaching:0 - - - xep - XEP-0368 - name - SRV records for XMPP over TLS - type - Standards Track - status - Draft - date - 2017-03-09 - description - - xmppId - - - - xep - XEP-0369 - name - Mediated Information eXchange (MIX) - type - Standards Track - status - Experimental - date - 2018-06-06 - description - - xmppId - urn:xmpp:mix:core:* - - - xep - XEP-0370 - name - Jingle HTTP Transport Method - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0371 - name - Jingle ICE Transport Method - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0372 - name - References - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:reference:0 - - - xep - XEP-0373 - name - OpenPGP for XMPP - type - Standards Track - status - Experimental - date - 2018-07-30 - description - - xmppId - - - - xep - XEP-0374 - name - OpenPGP for XMPP Instant Messaging - type - Standards Track - status - Deferred - date - 2018-01-25 - description - - xmppId - urn:xmpp:openpgp:im:0 - - - xep - XEP-0375 - name - XMPP Compliance Suites 2016 - type - Standards Track - status - Retracted - date - 2016-07-20 - description - - xmppId - - - - xep - XEP-0376 - name - Pubsub Account Management - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - - - - xep - XEP-0377 - name - Spam Reporting - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:reporting:* - - - xep - XEP-0378 - name - OTR Discovery - type - Standards Track - status - Deferred - date - 2017-09-11 - description - - xmppId - urn:xmpp:otr:0 - - - xep - XEP-0379 - name - Pre-Authenticated Roster Subscription - type - Standards Track - status - Experimental - date - 2017-03-06 - description - - xmppId - - - - xep - XEP-0380 - name - Explicit Message Encryption - type - Standards Track - status - Deferred - date - 2018-01-25 - description - - xmppId - urn:xmpp:eme:0 - - - xep - XEP-0381 - name - Internet of Things Special Interest Group (IoT SIG) - type - Procedural - status - Proposed - date - 2016-11-23 - description - - xmppId - - - - xep - XEP-0382 - name - Spoiler messages - type - Standards Track - status - Deferred - date - 2018-01-25 - description - - xmppId - urn:xmpp:spoiler:0 - - - xep - XEP-0383 - name - Burner JIDs - type - Standards Track - status - Deferred - date - 2017-01-28 - description - - xmppId - urn:xmpp:burner:0 - - - xep - XEP-0384 - name - OMEMO Encryption - type - Standards Track - status - Experimental - date - 2018-05-21 - description - - xmppId - eu.siacs.conversations.axolotl - - - xep - XEP-0385 - name - Stateless Inline Media Sharing (SIMS) - type - Standards Track - status - Experimental - date - 2018-01-25 - description - - xmppId - urn:xmpp:sims:1 - - - xep - XEP-0386 - name - Bind 2.0 - type - Standards Track - status - Deferred - date - 2018-02-08 - description - - xmppId - urn:xmpp:bind2:0 - - - xep - XEP-0387 - name - XMPP Compliance Suites 2018 - type - Standards Track - status - Draft - date - 2018-01-25 - description - - xmppId - - - - xep - XEP-0388 - name - Extensible SASL Profile - type - Standards Track - status - Experimental - date - 2017-08-24 - description - - xmppId - - - - xep - XEP-0389 - name - Extensible In-Band Registration - type - Standards Track - status - Experimental - date - 2017-03-16 - description - - xmppId - - - - xep - XEP-0390 - name - Entity Capabilities 2.0 - type - Standards Track - status - Experimental - date - 2017-06-14 - description - - xmppId - urn:xmpp:caps - - - xep - XEP-0391 - name - Jingle Encrypted Transports - type - Standards Track - status - Experimental - date - 2018-07-31 - description - - xmppId - urn:xmpp:jingle:jet:0 - - - xep - XEP-0392 - name - Consistent Color Generation - type - Standards Track - status - Experimental - date - 2018-07-28 - description - - xmppId - - - - xep - XEP-0393 - name - Message Styling - type - Standards Track - status - Experimental - date - 2018-05-01 - description - - xmppId - - - - xep - XEP-0394 - name - Message Markup - type - Standards Track - status - Experimental - date - 2017-11-22 - description - - xmppId - urn:xmpp:markup:0 - - - xep - XEP-0395 - name - Atomically Compare-And-Publish PubSub Items - type - Standards Track - status - Experimental - date - 2017-11-29 - description - - xmppId - - - - xep - XEP-0396 - name - Jingle Encrypted Transports - OMEMO - type - Standards Track - status - Experimental - date - 2017-11-29 - description - - xmppId - urn:xmpp:jingle:jet-omemo:0 - - - xep - XEP-0397 - name - Instant Stream Resumption - type - Standards Track - status - Experimental - date - 2018-01-22 - description - - xmppId - https://xmpp.org/extensions/isr/0 - - - xep - XEP-0398 - name - User Avatar to vCard-Based Avatars Conversion - type - Standards Track - status - Experimental - date - 2018-08-27 - description - - xmppId - urn:xmpp:pep-vcard-conversion:0 - - - xep - XEP-0399 - name - Client Key Support - type - Standards Track - status - Experimental - date - 2018-01-25 - description - - xmppId - urn:xmpp:client-key:0 - - - xep - XEP-0400 - name - Multi-Factor Authentication with TOTP - type - Standards Track - status - Experimental - date - 2018-01-25 - description - - xmppId - urn:xmpp:mfa:0 - - - xep - XEP-0401 - name - Easy User Onboarding - type - Standards Track - status - Experimental - date - 2018-02-11 - description - - xmppId - - - - xep - XEP-0402 - name - Bookmarks 2 (This Time it's Serious) - type - Standards Track - status - Experimental - date - 2018-07-22 - description - - xmppId - urn:xmpp:bookmarks:0 - - - xep - XEP-0403 - name - Mediated Information eXchange (MIX): Presence Support. - type - Standards Track - status - Experimental - date - 2018-06-06 - description - - xmppId - urn:xmpp:mix:nodes:presence - - - xep - XEP-0404 - name - Mediated Information eXchange (MIX): JID Hidden Channels. - type - Standards Track - status - Experimental - date - 2018-06-06 - description - - xmppId - - - - xep - XEP-0405 - name - Mediated Information eXchange (MIX): Participant Server Requirements - type - Standards Track - status - Experimental - date - 2018-06-06 - description - - xmppId - urn:xmpp:mix:pam:0 - - - xep - XEP-0406 - name - Mediated Information eXchange (MIX): MIX Administration - type - Standards Track - status - Experimental - date - 2018-06-06 - description - - xmppId - - - - xep - XEP-0407 - name - Mediated Information eXchange (MIX): Miscellaneous Capabilities - type - Standards Track - status - Experimental - date - 2018-05-14 - description - - xmppId - urn:xmpp:mix:misc:* - - - xep - XEP-0408 - name - Mediated Information eXchange (MIX): Co-existence with MUC - type - Standards Track - status - Experimental - date - 2018-05-21 - description - - xmppId - - - - xep - XEP-0409 - name - IM Routing-NG - type - Standards Track - status - Experimental - date - 2018-06-05 - description - - xmppId - urn:xmpp:im-ng:0 - - - xep - XEP-0410 - name - MUC Self-Ping (Schrödinger's Chat) - type - Standards Track - status - Experimental - date - 2018-08-31 - description - - xmppId - - - - diff --git a/old/View/BaseNavigationView.swift b/old/View/BaseNavigationView.swift deleted file mode 100644 index 558b471..0000000 --- a/old/View/BaseNavigationView.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Martin -import SwiftUI - -struct BaseNavigationView: View { - @EnvironmentObject var store: AppStore - - public var body: some View { - Group { - switch store.state.currentFlow { - case .start: - switch store.state.startState.navigation { - case .startScreen: - StartScreen() - - case .welcomeScreen: - WelcomeScreen() - } - - case .accounts: - switch store.state.accountsState.navigation { - case .addAccount: - AddAccountScreen() - } - - case .chats: - ChatsListScreen() - - case .contacts: - ContactsScreen() - - case .settings: - SettingsScreen() - - case .conversation: - ConversationScreen() - } - } - } -} diff --git a/old/View/Screens/AddAccountScreen.swift b/old/View/Screens/AddAccountScreen.swift deleted file mode 100644 index 1648429..0000000 --- a/old/View/Screens/AddAccountScreen.swift +++ /dev/null @@ -1,122 +0,0 @@ -import Combine -import Martin -import SwiftUI - -struct AddAccountScreen: View { - @EnvironmentObject var store: AppStore - - enum Field { - case userJid - case password - } - - @FocusState private var focus: Field? - @State private var errorMsg: String = "" - @State private var isShowingAlert = false - @State private var isShowingLoader = false - - #if DEBUG - @State private var jidStr: String = "nartest1@conversations.im" - @State private var pass: String = "nartest12345" - // @State private var jidStr: String = "test1@test.anal.company" - // @State private var pass: String = "12345" - #else - @State private var jidStr: String = "" - @State private var pass: String = "" - #endif - - public var body: some View { - ZStack { - // background - Color.Material.Background.light - .ignoresSafeArea() - - // content - VStack(spacing: 32) { - // icon - Image.logo - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 120, height: 120) - - // texts - VStack(spacing: 10) { - Text(L10n.Login.title) - .font(.head1l) - .foregroundColor(.Material.Text.main) - .fixedSize(horizontal: true, vertical: false) - Text(L10n.Login.subtitle) - .font(.body2) - .foregroundColor(.Material.Text.sub) - .multilineTextAlignment(.center) - .fixedSize(horizontal: false, vertical: true) - } - - VStack(spacing: 16) { - UniversalInputCollection.TextField( - prompt: L10n.Login.Hint.jid, - text: $jidStr, - focus: $focus, - fieldType: .userJid, - contentType: .emailAddress, - keyboardType: .emailAddress, - submitLabel: .next, - action: { - focus = .password - } - ) - UniversalInputCollection.SecureField( - prompt: L10n.Login.Hint.password, - text: $pass, - focus: $focus, - fieldType: .password, - submitLabel: .go, - action: { - focus = nil - } - ) - - Button { - isShowingLoader = true - store.dispatch(.accountsAction(.tryAddAccountWithCredentials(login: jidStr, password: pass))) - } label: { - Text(L10n.Login.btn) - } - .buttonStyle(PrimaryButtonStyle()) - .disabled(!loginInputValid) - - Button { - store.dispatch(.startAction(.goTo(.welcomeScreen))) - store.dispatch(.changeFlow(.start)) - } label: { - Text("\(Image(systemName: "chevron.left")) \(L10n.Global.back)") - .foregroundColor(.Material.Elements.active) - .font(.body2) - } - } - } - .padding(.horizontal, 32) - } - .loadingIndicator(isShowingLoader) - .alert(isPresented: $isShowingAlert) { - Alert( - title: Text(L10n.Global.Error.title), - message: Text(errorMsg), - dismissButton: .default(Text(L10n.Global.ok)) { - store.dispatch(.accountsAction(.addAccountError(jid: jidStr, reason: nil))) - } - ) - } - .onChange(of: store.state.accountsState.addAccountError) { err in - if let err { - isShowingLoader = false - isShowingAlert = true - errorMsg = err - } - } - } - - private var loginInputValid: Bool { - !jidStr.isEmpty && !pass.isEmpty && UniversalInputCollection.Validators.isEmail(jidStr) - } -} diff --git a/old/View/Screens/Chats/ChatsCreateMainScreen.swift b/old/View/Screens/Chats/ChatsCreateMainScreen.swift deleted file mode 100644 index c25b8f8..0000000 --- a/old/View/Screens/Chats/ChatsCreateMainScreen.swift +++ /dev/null @@ -1,182 +0,0 @@ -import SwiftUI - -struct ChatsCreateMainScreen: View { - @Binding var isPresented: Bool - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - SharedNavigationBar( - leftButton: .init( - image: Image(systemName: "xmark"), - action: { - isPresented = false - } - ), - centerText: .init(text: L10n.Chats.Create.Main.title) - ) - - // List - List { - // ChatsCreateRowButton( - // title: L10n.Chats.Create.Main.createGroup, - // image: "person.2.fill", - // action: {} - // ) - // ChatsCreateRowButton( - // title: L10n.Chats.Create.Main.createPrivateGroup, - // image: "person.2.fill", - // action: {} - // ) - // ChatsCreateRowButton( - // title: L10n.Chats.Create.Main.findGroup, - // image: "magnifyingglass", - // action: {} - // ) - - // for contacts list - let rosters = store.state.rostersState.rosters.filter { !$0.locallyDeleted } - if rosters.isEmpty { - ChatsCreateRowSeparator() - } - } - .listStyle(.plain) - - Spacer() - } - } - } -} - -// private struct ChatsCreateRowButton: View { -// var title: String -// var image: String -// var action: () -> Void -// -// var body: some View { -// VStack(alignment: .center, spacing: 0) { -// Spacer() -// HStack(alignment: .center, spacing: 16) { -// Image(systemName: image) -// .font(.head2) -// .foregroundColor(.Material.Elements.active) -// .padding(.leading, 16) -// Text(title) -// .font(.body1) -// .foregroundColor(.Material.Text.main) -// Spacer() -// } -// Spacer() -// Rectangle() -// .frame(maxWidth: .infinity) -// .frame(height: 1) -// .foregroundColor(.Material.Background.dark) -// } -// .sharedListRow() -// .frame(height: 48) -// .onTapGesture { -// action() -// } -// } -// } - -private struct ChatsCreateRowSeparator: View { - var body: some View { - Text("aa") - } -} - -// import SwiftUI -// -// struct CreateConversationMainScreen: View { -// @EnvironmentObject var store: AppStore -// -// var body: some View { -// ZStack { -// // Background color -// Color.Material.Background.light -// .ignoresSafeArea() -// -// // Content -// VStack(spacing: 0) { -// // Header -// CreateConversationHeader() -// -// // Chats list -// // if !store.state.chatsState.chats.isEmpty { -// // List { -// // ForEach(store.state.chatsState.chats) { chat in -// // ChatsRow(chat: chat) -// // } -// // } -// // .listStyle(.plain) -// // .background(Color.Material.Background.light) -// // } else { -// // Spacer() -// // } -// // -// // // Tab bar -// // SharedTabBar() -// } -// } -// } -// } -// -// private struct CreateConversationHeader: View { -// @EnvironmentObject var store: AppStore -// -// var body: some View { -// ZStack { -// // bg -// Color.Material.Background.dark -// .ignoresSafeArea() -// -// HStack(spacing: 0) { -// Image(systemName: "arrow.left") -// .foregroundColor(.Material.Elements.active) -// .padding(.leading, 16) -// .tappablePadding(.symmetric(12)) { -// store.dispatch(.changeFlow(store.state.previousFlow)) -// } -// Spacer() -// } -// -// // title -// Text("New conversation") -// .font(.head2) -// .foregroundColor(Color.Material.Text.main) -// } -// } -// } -// - -// Preview -#if DEBUG - struct ChatsCreateMainScreen_Previews: PreviewProvider { - static var previews: some View { - ChatsCreateMainScreen(isPresented: .constant(true)) - .environmentObject(pStore) - } - - static var pStore: AppStore { - let state = pState - return AppStore(initialState: state, reducer: AppState.reducer, middlewares: []) - } - - static var pState: AppState { - var state = AppState() - - state.rostersState.rosters = [ - .init(contactBareJid: "test@me.com", subscription: "both", ask: true, data: .init(groups: [], annotations: [])) - ] - - return state - } - } -#endif diff --git a/old/View/Screens/Chats/ChatsListScreen.swift b/old/View/Screens/Chats/ChatsListScreen.swift deleted file mode 100644 index 572c23c..0000000 --- a/old/View/Screens/Chats/ChatsListScreen.swift +++ /dev/null @@ -1,61 +0,0 @@ -import SwiftUI - -struct ChatsListScreen: View { - @EnvironmentObject var store: AppStore - - @State private var isCretePanelPresented = false - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - SharedNavigationBar( - centerText: .init(text: L10n.Chats.title), - rightButton: .init( - image: Image(systemName: "square.and.pencil"), - action: { - isCretePanelPresented = true - } - ) - ) - - // Chats list - if !store.state.chatsState.chats.isEmpty { - List { - ForEach(store.state.chatsState.chats) { chat in - ChatsRow(chat: chat) - } - } - .listStyle(.plain) - .background(Color.Material.Background.light) - } else { - Spacer() - } - - // Tab bar - SharedTabBar() - } - } - .fullScreenCover(isPresented: $isCretePanelPresented) { - ChatsCreateMainScreen(isPresented: $isCretePanelPresented) - } - } -} - -private struct ChatsRow: View { - @EnvironmentObject var store: AppStore - - var chat: Chat - - var body: some View { - SharedListRow(iconType: .charCircle(chat.participant), text: chat.participant) - .onTapGesture { - store.dispatch(.chatsAction(.startChat(accountJid: chat.account, participantJid: chat.participant))) - } - } -} diff --git a/old/View/Screens/Contacts/AddContactOrChannelScreen.swift b/old/View/Screens/Contacts/AddContactOrChannelScreen.swift deleted file mode 100644 index 56897f7..0000000 --- a/old/View/Screens/Contacts/AddContactOrChannelScreen.swift +++ /dev/null @@ -1,148 +0,0 @@ -import SwiftUI - -struct AddContactOrChannelScreen: View { - @EnvironmentObject var store: AppStore - - enum Field { - case account - case contact - } - - @FocusState private var focus: Field? - - @Binding var isPresented: Bool - @State private var contactJID: String = "" - @State private var ownerAccount: Account? - - @State private var isShowingLoader = false - @State private var isShowingAlert = false - @State private var errorMsg = "" - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - SharedNavigationBar( - leftButton: .init( - image: Image(systemName: "xmark"), - action: { - isPresented = false - } - ), - centerText: .init(text: L10n.Contacts.Add.title), - rightButton: .init( - image: Image(systemName: "plus.viewfinder"), - action: { - print("Scan QR-code") - } - ) - ) - - VStack(spacing: 16) { - // Explanation text - - Text(L10n.Contacts.Add.explanation) - .font(.body3) - .foregroundColor(.Material.Shape.separator) - .multilineTextAlignment(.center) - .padding(.top, 16) - - // Account selector - HStack(spacing: 0) { - Text("Use account:") - .font(.body2) - .foregroundColor(.Material.Text.main) - .frame(alignment: .leading) - Spacer() - } - UniversalInputCollection.DropDownMenu( - prompt: "Use account", - elements: store.state.accountsState.accounts, - selected: $ownerAccount, - focus: $focus, - fieldType: .account - ) - - // Contact text input - HStack(spacing: 0) { - Text("Contact JID:") - .font(.body2) - .foregroundColor(.Material.Text.main) - .frame(alignment: .leading) - Spacer() - } - UniversalInputCollection.TextField( - prompt: "Contact or channel JID", - text: $contactJID, - focus: $focus, - fieldType: .contact, - contentType: .emailAddress, - keyboardType: .emailAddress, - submitLabel: .done, - action: { - focus = .account - } - ) - - // Save button - Button { - save() - } label: { - Text(L10n.Global.save) - } - .buttonStyle(PrimaryButtonStyle()) - .disabled(!inputValid) - .padding(.top) - Spacer() - } - .padding(.horizontal, 32) - } - } - .onAppear { - if let exists = store.state.accountsState.accounts.first, exists.isActive { - ownerAccount = exists - } - } - .loadingIndicator(isShowingLoader) - .alert(isPresented: $isShowingAlert) { - Alert( - title: Text(L10n.Global.Error.title), - message: Text(errorMsg), - dismissButton: .default(Text(L10n.Global.ok)) - ) - } - .onChange(of: store.state.rostersState.newAddedRosterJid) { jid in - if jid != nil, isShowingLoader { - isShowingLoader = false - isPresented = false - } - } - .onChange(of: store.state.rostersState.newAddedRosterError) { error in - if let error = error, isShowingLoader { - isShowingLoader = false - errorMsg = error - isShowingAlert = true - } - } - } - - private var inputValid: Bool { - ownerAccount != nil && !contactJID.isEmpty && UniversalInputCollection.Validators.isEmail(contactJID) - } - - private func save() { - guard let ownerAccount else { return } - if let exists = store.state.rostersState.rosters.first(where: { $0.bareJid == ownerAccount.bareJid && $0.contactBareJid == contactJID }), exists.locallyDeleted { - store.dispatch(.rostersAction(.unmarkRosterAsLocallyDeleted(ownerJID: ownerAccount.bareJid, contactJID: contactJID))) - isPresented = false - } else { - isShowingLoader = true - store.dispatch(.rostersAction(.addRoster(ownerJID: ownerAccount.bareJid, contactJID: contactJID, name: nil, groups: []))) - } - } -} diff --git a/old/View/Screens/Contacts/ContactsScreen.swift b/old/View/Screens/Contacts/ContactsScreen.swift deleted file mode 100644 index d47c736..0000000 --- a/old/View/Screens/Contacts/ContactsScreen.swift +++ /dev/null @@ -1,172 +0,0 @@ -import SwiftUI - -struct ContactsScreen: View { - @EnvironmentObject var store: AppStore - - @State private var addPanelPresented = false - @State private var isErrorAlertPresented = false - @State private var errorAlertMessage = "" - @State private var isShowingLoader = false - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - SharedNavigationBar( - centerText: .init(text: L10n.Contacts.title), - rightButton: .init( - image: Image(systemName: "plus"), - action: { - addPanelPresented = true - } - ) - ) - - // Contacts list - let rosters = store.state.rostersState.rosters.filter { !$0.locallyDeleted } - if !rosters.isEmpty { - List { - ForEach(rosters) { roster in - ContactsScreenRow( - roster: roster, - isErrorAlertPresented: $isErrorAlertPresented, - errorAlertMessage: $errorAlertMessage, - isShowingLoader: $isShowingLoader - ) - } - } - .listStyle(.plain) - .background(Color.Material.Background.light) - } else { - Spacer() - } - - // Tab bar - SharedTabBar() - } - } - .loadingIndicator(isShowingLoader) - .fullScreenCover(isPresented: $addPanelPresented) { - AddContactOrChannelScreen(isPresented: $addPanelPresented) - } - .alert(isPresented: $isErrorAlertPresented) { - Alert( - title: Text(L10n.Global.Error.title), - message: Text(errorAlertMessage), - dismissButton: .default(Text(L10n.Global.ok)) - ) - } - } -} - -private struct ContactsScreenRow: View { - @EnvironmentObject var store: AppStore - - var roster: Roster - @State private var isShowingMenu = false - @State private var isDeleteAlertPresented = false - - @Binding var isErrorAlertPresented: Bool - @Binding var errorAlertMessage: String - @Binding var isShowingLoader: Bool - - var body: some View { - SharedListRow( - iconType: .charCircle(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter), - text: roster.contactBareJid - ) - // VStack(spacing: 0) { - // HStack(spacing: 8) { - // ZStack { - // Circle() - // .frame(width: 44, height: 44) - // .foregroundColor(.red) - // Text(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter) - // .foregroundColor(.white) - // .font(.body1) - // } - // Text(roster.contactBareJid) - // .foregroundColor(Color.Material.Text.main) - // .font(.body2) - // Spacer() - // } - // .padding(.horizontal, 16) - // .padding(.vertical, 4) - // Rectangle() - // .frame(maxWidth: .infinity) - // .frame(height: 1) - // .foregroundColor(.Material.Background.dark) - // } - // .sharedListRow() - .onTapGesture { - store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid))) - } - .onLongPressGesture { - isShowingMenu.toggle() - } - .swipeActions(edge: .trailing, allowsFullSwipe: false) { - Button { - isDeleteAlertPresented = true - } label: { - Label(L10n.Contacts.sendMessage, systemImage: "trash") - } - .tint(Color.red) - } - .contextMenu { - Button(L10n.Contacts.sendMessage, systemImage: "message") { - store.dispatch(.chatsAction(.startChat(accountJid: roster.bareJid, participantJid: roster.contactBareJid))) - } - Divider() - - Button(L10n.Contacts.editContact) { - print("Edit contact") - } - - Button(L10n.Contacts.selectContact) { - print("Select contact") - } - - Divider() - Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) { - isDeleteAlertPresented = true - } - } - .actionSheet(isPresented: $isDeleteAlertPresented) { - ActionSheet( - title: Text(L10n.Contacts.Delete.title), - message: Text(L10n.Contacts.Delete.message), - buttons: [ - .destructive(Text(L10n.Contacts.Delete.deleteFromDevice)) { - store.dispatch(.rostersAction(.markRosterAsLocallyDeleted(ownerJID: roster.bareJid, contactJID: roster.contactBareJid))) - }, - .destructive(Text(L10n.Contacts.Delete.deleteCompletely)) { - isShowingLoader = true - store.dispatch(.rostersAction(.deleteRoster(ownerJID: roster.bareJid, contactJID: roster.contactBareJid))) - }, - .cancel(Text(L10n.Global.cancel)) - ] - ) - } - .onChange(of: store.state.rostersState.rosters) { _ in - endOfDeleting() - } - .onChange(of: store.state.rostersState.deleteRosterError) { _ in - endOfDeleting() - } - } - - private func endOfDeleting() { - if isShowingLoader { - isShowingLoader = false - if let error = store.state.rostersState.deleteRosterError { - errorAlertMessage = error - isErrorAlertPresented = true - } - } - } -} diff --git a/old/View/Screens/Conversation/ConversationMessageContainer.swift b/old/View/Screens/Conversation/ConversationMessageContainer.swift deleted file mode 100644 index 0de1616..0000000 --- a/old/View/Screens/Conversation/ConversationMessageContainer.swift +++ /dev/null @@ -1,254 +0,0 @@ -import AVKit -import MapKit -import QuickLook -import SwiftUI - -struct ConversationMessageContainer: View { - let message: Message - let isOutgoing: Bool - - var body: some View { - if let msgText = message.body, msgText.isLocation { - EmbededMapView(location: msgText.getLatLon) - } else if let msgText = message.body, msgText.isContact { - ContactView(message: message) - } else if message.attachmentType != nil { - AttachmentView(message: message) - } else { - Text(message.body ?? "...") - .font(.body2) - .foregroundColor(.Material.Text.main) - .multilineTextAlignment(.leading) - .padding(10) - } - } -} - -struct MessageAttr: View { - let message: Message - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - Text(message.date, style: .time) - .font(.sub2) - .foregroundColor(.Material.Shape.separator) - Spacer() - if message.sentError { - Image(systemName: "exclamationmark.circle") - .font(.body3) - .foregroundColor(.Rainbow.red500) - } else if message.pending { - Image(systemName: "clock") - .font(.body3) - .foregroundColor(.Material.Shape.separator) - } - } - } -} - -private struct EmbededMapView: View { - let location: CLLocationCoordinate2D - - var body: some View { - Map( - coordinateRegion: .constant(MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))), - interactionModes: [], - showsUserLocation: false, - userTrackingMode: .none, - annotationItems: [location], - annotationContent: { _ in - MapMarker(coordinate: location, tint: .blue) - } - ) - .frame(width: Const.mapPreviewSize, height: Const.mapPreviewSize) - .onTapGesture { - let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: location)) - mapItem.name = "Location" - mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]) - } - } -} - -private struct ContactView: View { - let message: Message - - var body: some View { - VStack { - ZStack { - Circle() - .frame(width: 44, height: 44) - .foregroundColor(contactName.firstLetterColor) - Text(contactName.firstLetter) - .foregroundColor(.white) - .font(.body1) - } - Text(message.body?.getContactJid ?? "...") - .font(.body2) - .foregroundColor(.Material.Text.main) - .multilineTextAlignment(.leading) - } - .padding() - .onTapGesture { - // TODO: Jump to add roster from here - } - } - - private var contactName: String { - message.body?.getContactJid ?? "?" - } -} - -private struct AttachmentView: View { - let message: Message - - var body: some View { - if message.attachmentDownloadFailed || (message.attachmentLocalName != nil && message.sentError) { - failed - } else { - switch message.attachmentType { - case .image: - if let thumbnail = thumbnail() { - thumbnail - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - } else { - placeholder - } - - case .movie: - if let file = message.attachmentLocalPath { - VideoPlayerView(url: file) - .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - } else { - placeholder - } - - case .file: - if let file = message.attachmentLocalPath { - DocumentPreview(url: file) - .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - } else { - placeholder - } - - default: - placeholder - } - } - } - - @ViewBuilder private var placeholder: some View { - Rectangle() - .foregroundColor(.Material.Background.dark) - .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - .overlay { - ZStack { - ProgressView() - .scaleEffect(1.5) - .progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active)) - let imageName = progressImageName(message.attachmentType ?? .file) - Image(systemName: imageName) - .font(.body1) - .foregroundColor(.Material.Elements.active) - } - } - } - - @ViewBuilder private var failed: some View { - Rectangle() - .foregroundColor(.Material.Background.dark) - .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize) - .overlay { - ZStack { - VStack { - Text(L10n.Attachment.Downloading.retry) - .font(.body3) - .foregroundColor(.Rainbow.red500) - Image(systemName: "exclamationmark.arrow.triangle.2.circlepath") - .font(.body1) - .foregroundColor(.Rainbow.red500) - } - } - } - .onTapGesture { - if let url = message.attachmentRemotePath { - store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: url))) - } else if message.attachmentLocalName != nil && message.sentError { - store.dispatch(.sharingAction(.retrySharing(messageId: message.id))) - } - } - } - - private func progressImageName(_ type: MessageAttachmentType) -> String { - switch type { - case .image: - return "photo" - - case .audio: - return "music.note" - - case .movie: - return "film" - - case .file: - return "doc" - } - } - - private func thumbnail() -> Image? { - guard let thumbnailPath = message.attachmentThumbnailPath else { return nil } - guard let uiImage = UIImage(contentsOfFile: thumbnailPath.path()) else { return nil } - return Image(uiImage: uiImage) - } -} - -// TODO: Make video player better! -private struct VideoPlayerView: UIViewControllerRepresentable { - let url: URL - - func makeUIViewController(context _: Context) -> AVPlayerViewController { - let controller = AVPlayerViewController() - controller.player = AVPlayer(url: url) - controller.allowsPictureInPicturePlayback = true - return controller - } - - func updateUIViewController(_: AVPlayerViewController, context _: Context) { - // Update the controller if needed. - } -} - -struct DocumentPreview: UIViewControllerRepresentable { - var url: URL - - func makeUIViewController(context: Context) -> QLPreviewController { - let controller = QLPreviewController() - controller.dataSource = context.coordinator - return controller - } - - func updateUIViewController(_: QLPreviewController, context _: Context) { - // Update the controller if needed. - } - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject, QLPreviewControllerDataSource { - var parent: DocumentPreview - - init(_ parent: DocumentPreview) { - self.parent = parent - } - - func numberOfPreviewItems(in _: QLPreviewController) -> Int { - 1 - } - - func previewController(_: QLPreviewController, previewItemAt _: Int) -> QLPreviewItem { - parent.url as QLPreviewItem - } - } -} diff --git a/old/View/Screens/Conversation/ConversationMessageRow.swift b/old/View/Screens/Conversation/ConversationMessageRow.swift deleted file mode 100644 index b3fa2df..0000000 --- a/old/View/Screens/Conversation/ConversationMessageRow.swift +++ /dev/null @@ -1,82 +0,0 @@ -import Foundation -import SwiftUI - -struct ConversationMessageRow: View { - @EnvironmentObject var store: AppStore - - let message: Message - @State private var offset: CGSize = .zero - - var body: some View { - VStack(spacing: 0) { - HStack(spacing: 0) { - if isOutgoing() { - Spacer() - MessageAttr(message: message) - .padding(.trailing, 4) - } - ConversationMessageContainer(message: message, isOutgoing: isOutgoing()) - .background(isOutgoing() ? Color.Material.Shape.alternate : Color.Material.Shape.white) - .clipShape(ConversationMessageBubble(isOutgoing: isOutgoing())) - if !isOutgoing() { - MessageAttr(message: message) - .padding(.leading, 4) - Spacer() - } - } - .padding(.vertical, 10) - .padding(.horizontal, 16) - .background(Color.clearTappable) - .offset(offset) - .gesture( - DragGesture(minimumDistance: 30, coordinateSpace: .local) - .onChanged { value in - var width = value.translation.width - width = width > 0 ? 0 : width - offset = CGSize(width: width, height: 0) - } - .onEnded { value in - let targetWidth: CGFloat = -90 - withAnimation(.easeOut(duration: 0.1)) { - if value.translation.width <= targetWidth { - Vibration.success.vibrate() - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - withAnimation(.easeOut(duration: 0.1)) { - offset = .zero - } - } - } else { - offset = .zero - } - } - if value.translation.width <= targetWidth { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { - store.dispatch(.conversationAction(.setReplyText(message.body ?? ""))) - } - } - } - ) - } - .listRowInsets(.zero) - .listRowSeparator(.hidden) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.Material.Background.light) - } - - private func isOutgoing() -> Bool { - message.from == store.state.conversationsState.currentChat?.account - } -} - -struct ConversationMessageBubble: Shape { - let isOutgoing: Bool - - func path(in rect: CGRect) -> Path { - let path = UIBezierPath( - roundedRect: rect, - byRoundingCorners: isOutgoing ? [.topLeft, .bottomLeft, .bottomRight] : [.topRight, .bottomLeft, .bottomRight], - cornerRadii: CGSize(width: 8, height: 10) - ) - return Path(path.cgPath) - } -} diff --git a/old/View/Screens/Conversation/ConversationScreen.swift b/old/View/Screens/Conversation/ConversationScreen.swift deleted file mode 100644 index 73657bf..0000000 --- a/old/View/Screens/Conversation/ConversationScreen.swift +++ /dev/null @@ -1,198 +0,0 @@ -import Combine -import Foundation -import Martin -import SwiftUI - -struct ConversationScreen: View { - @EnvironmentObject var store: AppStore - - @State private var autoScroll = true - @State private var firstIsVisible = true - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - let name = ( - store.state.conversationsState.currentRoster?.name ?? - store.state.conversationsState.currentRoster?.contactBareJid - ) ?? L10n.Chat.title - SharedNavigationBar( - leftButton: .init( - image: Image(systemName: "chevron.left"), - action: { - store.dispatch(.changeFlow(store.state.previousFlow)) - } - ), - centerText: .init(text: name) - ) - - // Msg list - let messages = store.state.conversationsState.currentMessages - if !messages.isEmpty { - ScrollViewReader { proxy in - List { - ForEach(messages) { message in - ConversationMessageRow(message: message) - .id(message.id) - .onAppear { - if message.id == messages.first?.id { - firstIsVisible = true - autoScroll = true - } - } - .onDisappear { - if message.id == messages.first?.id { - firstIsVisible = false - autoScroll = false - } - } - } - .rotationEffect(.degrees(180)) - } - .rotationEffect(.degrees(180)) - .listStyle(.plain) - .background(Color.Material.Background.light) - .scrollDismissesKeyboard(.immediately) - .scrollIndicators(.hidden) - .onChange(of: autoScroll) { new in - if new, !firstIsVisible { - withAnimation { - proxy.scrollTo(messages.first?.id, anchor: .top) - } - } - } - } - } else { - Spacer() - } - } - .onTapGesture { - UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - } - - // Jump to last button - if !autoScroll { - VStack { - Spacer() - HStack { - Spacer() - Button { - autoScroll = true - } label: { - ZStack { - Circle() - .fill(Color.Material.Shape.white) - Image(systemName: "arrow.down") - .foregroundColor(.Material.Elements.active) - } - .frame(width: 40, height: 40) - .shadow(color: .black.opacity(0.2), radius: 4) - .padding(.trailing, 8) - .padding(.bottom, 8) - } - } - } - } - } - .safeAreaInset(edge: .bottom, spacing: 0) { - ConversationTextInput(autoScroll: $autoScroll) - } - } -} - -// Preview -#if DEBUG - struct ConversationScreen_Previews: PreviewProvider { - static var previews: some View { - ConversationScreen() - .environmentObject(pStore) - } - - static var pStore: AppStore { - let state = pState - return AppStore(initialState: state, reducer: AppState.reducer, middlewares: []) - } - - static var pState: AppState { - var state = AppState() - - let acc = "user@test.com" - let contact = "some@test.com" - - state.conversationsState.currentChat = Chat(id: "1", account: acc, participant: contact, type: .chat) - state.conversationsState.currentMessages = [ - Message( - id: "1", - type: .chat, - contentType: .text, - from: contact, - to: acc, - body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ", - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), - pending: true, sentError: false - ), - Message( - id: "2", - type: .chat, - contentType: .text, - from: contact, - to: acc, - body: "this is for testsdfsdf sdfsdf sdfs sdf sdffsdf sdf sdf sdf sdf sdf sdff sdfffwwe ", - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), - pending: false, - sentError: false - ), - Message(id: "3", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: true), - Message( - id: "4", - type: .chat, - contentType: .text, - from: acc, - to: contact, - body: "this is for test sdfkjwek jwkjfh jwerf jdfhskjdhf jsdhfjhwefh sjdhfh fsdjhfh sd ", - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), - pending: false, - sentError: false - ), - Message(id: "5", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test sdfjkkeke kekkddjw;; w;edkdjfj l kjwekrjfk wef", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), - Message(id: "6", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for testsdf dsdkkekkddn wejkjfj ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), - Message( - id: "7", - type: .chat, - contentType: .text, - from: acc, - to: contact, - - body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ", - subject: nil, - thread: nil, - oobUrl: nil, - date: Date(), pending: false, sentError: false - ), - Message(id: "8", type: .chat, contentType: .text, from: acc, to: contact, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), - Message(id: "9", type: .chat, contentType: .text, from: contact, to: acc, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), - Message(id: "10", type: .chat, contentType: .text, from: acc, to: contact, body: "so test so test so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), - Message(id: "11", type: .chat, contentType: .text, from: contact, to: acc, body: "xD", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false) - ] - - state.conversationsState.replyText = "> Some Text here! And if it a long and very long text sdfsadfsadfsafsadfsadfsadfsadfassadfsadfsafsafdsadfsafdsadfsadfas sdf sdf asdf sdfasdfsd sdfasdf sdfsdfdsasdfsdfa dsafsaf" - - return state - } - } -#endif diff --git a/old/View/Screens/Conversation/ConversationTextInput.swift b/old/View/Screens/Conversation/ConversationTextInput.swift deleted file mode 100644 index a0b089d..0000000 --- a/old/View/Screens/Conversation/ConversationTextInput.swift +++ /dev/null @@ -1,113 +0,0 @@ -import SwiftUI -import UIKit - -struct ConversationTextInput: View { - @EnvironmentObject var store: AppStore - - @State private var messageStr = "" - @FocusState private var isFocused: Bool - @Binding var autoScroll: Bool - - var body: some View { - VStack(spacing: 0) { - Rectangle() - .foregroundColor(.Material.Shape.separator) - .frame(height: 0.5) - .padding(.bottom, 8) - if !replyText.isEmpty { - VStack(spacing: 0) { - HStack(alignment: .top) { - Text(replyText) - .font(.body3) - .foregroundColor(Color.Material.Text.main) - .multilineTextAlignment(.leading) - .lineLimit(3) - .padding(8) - Spacer() - Image(systemName: "xmark") - .font(.title2) - .foregroundColor(.Material.Elements.active) - .padding(.leading, 8) - .tappablePadding(.symmetric(8)) { - store.dispatch(.conversationAction(.setReplyText(""))) - } - .padding(8) - } - .frame(maxWidth: .infinity) - .background(RoundedRectangle(cornerRadius: 4) - .foregroundColor(.Material.Background.light) - .shadow(radius: 0.5) - ) - .padding(.bottom, 8) - .padding(.horizontal, 8) - } - .padding(.horizontal, 8) - } - HStack { - Image(systemName: "paperclip") - .font(.title2) - .foregroundColor(.Material.Elements.active) - .padding(.leading, 8) - .tappablePadding(.symmetric(8)) { - store.dispatch(.sharingAction(.showSharing(true))) - } - TextField("", text: $messageStr, prompt: Text(L10n.Chat.textfieldPrompt).foregroundColor(.Material.Shape.separator)) - .font(.body1) - .foregroundColor(Color.Material.Text.main) - .accentColor(.Material.Shape.black) - .focused($isFocused) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background(Color.Material.Shape.white) - .clipShape(RoundedRectangle(cornerRadius: 8)) - .padding(.vertical, 4) - let img = messageStr.isEmpty ? "paperplane" : "paperplane.fill" - Image(systemName: img) - .font(.title2) - .foregroundColor(messageStr.isEmpty ? .Material.Elements.inactive : .Material.Elements.active) - .padding(.trailing, 8) - .tappablePadding(.symmetric(8)) { - if !messageStr.isEmpty { - guard let acc = store.state.conversationsState.currentChat?.account else { return } - guard let contact = store.state.conversationsState.currentChat?.participant else { return } - store.dispatch(.conversationAction(.sendMessage( - from: acc, - to: contact, - body: composedMessage - ))) - messageStr = "" - // UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) - store.dispatch(.conversationAction(.setReplyText(""))) - autoScroll = true - } - } - } - } - .padding(.bottom, 8) - .background(Color.Material.Background.dark) - .onChange(of: store.state.conversationsState.replyText) { new in - if !new.isEmpty { - isFocused = true - } - } - .fullScreenCover(isPresented: Binding( - get: { store.state.sharingState.sharingShown }, - set: { _ in } - )) { - AttachmentPickerScreen() - } - } - - private var replyText: String { - store.state.conversationsState.replyText - } - - private var composedMessage: String { - var result = "" - if !replyText.isEmpty { - result += replyText + "\n\n" - } - result += messageStr - return result - } -} diff --git a/old/View/Screens/RegistrationScreen.swift b/old/View/Screens/RegistrationScreen.swift deleted file mode 100644 index ccd4d7f..0000000 --- a/old/View/Screens/RegistrationScreen.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SwiftUI - -struct RegistrationScreen: View { - // @EnvironmentObject var state: AppState - - public var body: some View { - ZStack { - Color.Material.Background.light - Button { - // state.flow = .welcome - } label: { - VStack { - Text("Not yet implemented") - Text(L10n.Global.back) - } - } - } - .ignoresSafeArea() - } -} diff --git a/old/View/Screens/Settings/SettingsScreen.swift b/old/View/Screens/Settings/SettingsScreen.swift deleted file mode 100644 index 98ee46c..0000000 --- a/old/View/Screens/Settings/SettingsScreen.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SwiftUI - -struct SettingsScreen: View { - var body: some View { - ZStack { - // bg - Color.Material.Background.light - .ignoresSafeArea() - - // content - Text("under construction...") - - // tab bar - VStack { - Spacer() - SharedTabBar() - } - } - } -} diff --git a/old/View/Screens/Sharing/SharingContactsPickerView.swift b/old/View/Screens/Sharing/SharingContactsPickerView.swift deleted file mode 100644 index 8c7588e..0000000 --- a/old/View/Screens/Sharing/SharingContactsPickerView.swift +++ /dev/null @@ -1,64 +0,0 @@ -import SwiftUI - -struct SharingContactsPickerView: View { - @EnvironmentObject var store: AppStore - @State private var selectedContact: Roster? - - var body: some View { - VStack(spacing: 0) { - // Contacts list - let rosters = store.state.rostersState.rosters.filter { !$0.locallyDeleted } - if !rosters.isEmpty { - List { - ForEach(rosters) { roster in - ContactRow(roster: roster, selectedContact: $selectedContact) - } - } - .listStyle(.plain) - .background(Color.Material.Background.light) - } else { - Spacer() - } - - // Send panel - Rectangle() - .foregroundColor(.Material.Shape.black) - .frame(maxWidth: .infinity) - .frame(height: selectedContact == nil ? 0 : 50) - .overlay { - HStack { - Text(L10n.Attachment.Send.contact) - .foregroundColor(.Material.Text.white) - .font(.body1) - Image(systemName: "arrow.up.circle") - .foregroundColor(.Material.Text.white) - .font(.body1) - .padding(.leading, 8) - } - .padding() - } - .clipped() - .onTapGesture { - if let selectedContact = selectedContact { - store.dispatch(.sharingAction(.shareContact(jid: selectedContact.contactBareJid))) - store.dispatch(.sharingAction(.showSharing(false))) - } - } - } - } -} - -private struct ContactRow: View { - var roster: Roster - @Binding var selectedContact: Roster? - - var body: some View { - SharedListRow( - iconType: .charCircle(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter), - text: roster.contactBareJid - ) - .onTapGesture { - selectedContact = roster - } - } -} diff --git a/old/View/Screens/Sharing/SharingFilesPickerView.swift b/old/View/Screens/Sharing/SharingFilesPickerView.swift deleted file mode 100644 index 5535b02..0000000 --- a/old/View/Screens/Sharing/SharingFilesPickerView.swift +++ /dev/null @@ -1,64 +0,0 @@ -import SwiftUI -import UIKit - -struct SharingFilesPickerView: View { - @EnvironmentObject var store: AppStore - - var body: some View { - DocumentPicker( - completion: { dataArray, extensionsArray in - store.dispatch(.sharingAction(.showSharing(false))) - store.dispatch(.sharingAction(.shareDocuments(dataArray, extensionsArray))) - }, - cancel: { - store.dispatch(.sharingAction(.showSharing(false))) - } - ) - } -} - -struct DocumentPicker: UIViewControllerRepresentable { - let completion: ([Data], [String]) -> Void - let cancel: () -> Void - - func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIDocumentPickerViewController { - let picker: UIDocumentPickerViewController - picker = UIDocumentPickerViewController(forOpeningContentTypes: [.item], asCopy: true) - picker.delegate = context.coordinator - picker.allowsMultipleSelection = true - return picker - } - - func updateUIViewController(_: UIDocumentPickerViewController, context _: UIViewControllerRepresentableContext) {} - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject, UIDocumentPickerDelegate { - var parent: DocumentPicker - - init(_ parent: DocumentPicker) { - self.parent = parent - } - - func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt: [URL]) { - var dataArray = [Data]() - var extensionArray = [String]() - for url in didPickDocumentsAt { - do { - let data = try Data(contentsOf: url) - dataArray.append(data) - extensionArray.append(url.pathExtension) - } catch { - print("Unable to load data from \(url): \(error)") - } - } - parent.completion(dataArray, extensionArray) - } - - func documentPickerWasCancelled(_: UIDocumentPickerViewController) { - parent.cancel() - } - } -} diff --git a/old/View/Screens/Sharing/SharingLocationPickerView.swift b/old/View/Screens/Sharing/SharingLocationPickerView.swift deleted file mode 100644 index def4bff..0000000 --- a/old/View/Screens/Sharing/SharingLocationPickerView.swift +++ /dev/null @@ -1,134 +0,0 @@ -import MapKit -import SwiftUI - -struct SharingLocationPickerView: View { - @StateObject var locationManager = LocationManager() - @State private var region = MKCoordinateRegion() - - var body: some View { - VStack(spacing: 0) { - ZStack { - // MapView - MapView(region: $region) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.region = locationManager.region - } - } - .overlay { - Image(systemName: "mappin") - .foregroundColor(.Material.Elements.active) - .font(.system(size: 30)) - .shadow(color: .white, radius: 2) - } - - // Track button - VStack { - Spacer() - HStack { - Spacer() - Image(systemName: "location.circle") - .resizable() - .frame(width: 40, height: 40) - .foregroundColor(.Material.Elements.active) - .background(Color.Material.Shape.white) - .clipShape(Circle()) - .shadow(color: .white, radius: 2) - .padding(.trailing) - .padding(.bottom, 50) - .tappablePadding(.symmetric(10)) { - self.region = locationManager.region - } - } - } - } - - // Send panel - Rectangle() - .foregroundColor(.Material.Shape.black) - .frame(maxWidth: .infinity) - .frame(height: 50) - .overlay { - HStack { - Text(L10n.Attachment.Send.location) - .foregroundColor(.Material.Text.white) - .font(.body1) - Image(systemName: "arrow.up.circle") - .foregroundColor(.Material.Text.white) - .font(.body1) - .padding(.leading, 8) - } - .padding() - } - .clipped() - .onTapGesture { - store.dispatch(.sharingAction(.shareLocation(lat: region.center.latitude, lon: region.center.longitude))) - store.dispatch(.sharingAction(.showSharing(false))) - } - } - .onAppear { - locationManager.start() - } - } -} - -struct MapView: UIViewRepresentable { - @Binding var region: MKCoordinateRegion - - func makeUIView(context: Context) -> MKMapView { - let mapView = MKMapView() - mapView.delegate = context.coordinator - mapView.showsUserLocation = false - mapView.userTrackingMode = .none - - return mapView - } - - func updateUIView(_ uiView: MKMapView, context _: Context) { - if uiView.region != region { - uiView.setRegion(region, animated: true) - } - } - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject, MKMapViewDelegate { - var parent: MapView - - init(_ parent: MapView) { - self.parent = parent - } - } -} - -class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { - private let locationManager = CLLocationManager() - @Published var region: MKCoordinateRegion - - override init() { - region = MKCoordinateRegion() - super.init() - locationManager.delegate = self - locationManager.desiredAccuracy = kCLLocationAccuracyBest - } - - func start() { - locationManager.requestWhenInUseAuthorization() - locationManager.startUpdatingLocation() - } - - func stop() { - locationManager.stopUpdatingLocation() - } - - func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - if let loc = locations.first { - region = MKCoordinateRegion( - center: loc.coordinate, - span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002) - ) - } - } -} diff --git a/old/View/Screens/Sharing/SharingMediaPickerView.swift b/old/View/Screens/Sharing/SharingMediaPickerView.swift deleted file mode 100644 index 17cbed0..0000000 --- a/old/View/Screens/Sharing/SharingMediaPickerView.swift +++ /dev/null @@ -1,289 +0,0 @@ -import AVFoundation -import MobileCoreServices -import Photos -import SwiftUI - -struct SharingMediaPickerView: View { - @EnvironmentObject var store: AppStore - @State private var showCameraPicker = false - @State private var cameraReady = false - - @State private var selectedItems: [String] = [] - - var body: some View { - let columns = Array(repeating: GridItem(.flexible(), spacing: 0), count: 3) - - VStack(spacing: 0) { - // List of media - ScrollView(showsIndicators: false) { - LazyVGrid(columns: columns, spacing: 0) { - // For camera - if store.state.sharingState.isCameraAccessGranted { - if cameraReady { - ZStack { - CameraView() - .aspectRatio(1, contentMode: .fit) - .frame(maxWidth: .infinity) - Image(systemName: "camera") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 40, height: 40) - .foregroundColor(.white) - .padding(8) - .background(Color.black.opacity(0.5)) - .clipShape(Circle()) - .padding(8) - } - .onTapGesture { - showCameraPicker = true - } - } else { - ProgressView() - .frame(maxWidth: .infinity) - .frame(height: 100) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - cameraReady = true - } - } - } - } else { - Button { - openAppSettings() - } label: { - ZStack { - Rectangle() - .fill(Color.Material.Background.light) - .overlay { - VStack { - Image(systemName: "camera") - .foregroundColor(.Material.Elements.active) - .font(.system(size: 30)) - Text("Allow camera access") - .foregroundColor(.Material.Text.main) - .font(.body3) - } - } - .frame(height: 100) - } - } - } - - // For gallery - if store.state.sharingState.isGalleryAccessGranted { - ForEach(store.state.sharingState.galleryItems) { item in - GridViewItem(item: item, selected: $selectedItems) - } - } else { - Button { - openAppSettings() - } label: { - ZStack { - Rectangle() - .fill(Color.Material.Background.light) - .overlay { - VStack { - Image(systemName: "photo") - .foregroundColor(.Material.Elements.active) - .font(.system(size: 30)) - Text("Allow gallery access") - .foregroundColor(.Material.Text.main) - .font(.body3) - } - } - .frame(height: 100) - } - } - } - } - } - .fullScreenCover(isPresented: $showCameraPicker) { - CameraPicker(sourceType: .camera) { data, type in - store.dispatch(.sharingAction(.cameraCaptured(media: data, type: type))) - showCameraPicker = false - store.dispatch(.sharingAction(.showSharing(false))) - } - .edgesIgnoringSafeArea(.all) - } - - // Send panel - Rectangle() - .foregroundColor(.Material.Shape.black) - .frame(maxWidth: .infinity) - .frame(height: self.selectedItems.isEmpty ? 0 : 50) - .overlay { - HStack { - Text(L10n.Attachment.Send.media) - .foregroundColor(.Material.Text.white) - .font(.body1) - Image(systemName: "arrow.up.circle") - .foregroundColor(.Material.Text.white) - .font(.body1) - .padding(.leading, 8) - } - .padding() - } - .clipped() - .onTapGesture { - store.dispatch(.sharingAction(.shareMedia(ids: selectedItems))) - store.dispatch(.sharingAction(.showSharing(false))) - } - } - .onAppear { - store.dispatch(.sharingAction(.checkCameraAccess)) - store.dispatch(.sharingAction(.checkGalleryAccess)) - } - .onChange(of: store.state.sharingState.isGalleryAccessGranted) { granted in - if granted { - store.dispatch(.fileAction(.fetchItemsFromGallery)) - } - } - } -} - -private struct GridViewItem: View { - let item: SharingGalleryItem - @Binding var selected: [String] - @State var isSelected = false - - var body: some View { - if let data = item.thumbnail { - ZStack { - Image(uiImage: UIImage(data: data) ?? UIImage()) - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: Const.galleryGridSize, height: Const.galleryGridSize) - .clipped() - if let duration = item.duration { - VStack { - Spacer() - HStack { - Spacer() - Text(duration) - .foregroundColor(.Material.Text.white) - .font(.sub1) - .shadow(color: .black, radius: 2) - .padding(4) - } - } - } - if isSelected { - VStack { - HStack { - Spacer() - Circle() - .frame(width: 30, height: 30) - .shadow(color: .black, radius: 2) - .foregroundColor(.Material.Shape.white) - .overlay { - Image(systemName: "checkmark") - .foregroundColor(.Material.Elements.active) - .font(.body3) - } - .padding(4) - } - Spacer() - } - } - } - .onTapGesture { - isSelected.toggle() - if isSelected { - selected.append(item.id) - } else { - selected.removeAll { $0 == item.id } - } - } - } else { - ZStack { - Rectangle() - .fill(Color.Material.Background.light) - .overlay { - ProgressView() - .foregroundColor(.Material.Elements.active) - } - .frame(width: Const.galleryGridSize, height: Const.galleryGridSize) - } - } - } -} - -class CameraUIView: UIView { - var previewLayer: AVCaptureVideoPreviewLayer? - - override func layoutSubviews() { - super.layoutSubviews() - previewLayer?.frame = bounds - } -} - -struct CameraView: UIViewRepresentable { - func makeUIView(context _: Context) -> CameraUIView { - let view = CameraUIView() - - let captureSession = AVCaptureSession() - guard let captureDevice = AVCaptureDevice.default(for: .video) else { return view } - guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return view } - captureSession.addInput(input) - - let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) - previewLayer.videoGravity = .resizeAspectFill - view.layer.addSublayer(previewLayer) - view.previewLayer = previewLayer - - captureSession.startRunning() - - return view - } - - func updateUIView(_ uiView: CameraUIView, context _: Context) { - uiView.previewLayer?.frame = uiView.bounds - } -} - -struct CameraPicker: UIViewControllerRepresentable { - var sourceType: UIImagePickerController.SourceType - var completionHandler: (Data, SharingCameraMediaType) -> Void - - func makeUIViewController(context: Context) -> UIImagePickerController { - let picker = UIImagePickerController() - picker.sourceType = sourceType - picker.delegate = context.coordinator - picker.mediaTypes = [UTType.movie.identifier, UTType.image.identifier] - picker.videoQuality = .typeHigh - picker.videoMaximumDuration = Const.videoDurationLimit - picker.view.backgroundColor = .clear - return picker - } - - func updateUIViewController(_: UIImagePickerController, context _: Context) {} - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { - let parent: CameraPicker - - init(_ parent: CameraPicker) { - self.parent = parent - } - - func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { - // swiftlint:disable:next force_cast - let mediaType = info[.mediaType] as! String - - if mediaType == UTType.image.identifier { - if let image = info[.originalImage] as? UIImage { - let data = image.jpegData(compressionQuality: 1.0) ?? Data() - parent.completionHandler(data, .photo) - } - } else if mediaType == UTType.movie.identifier { - if let url = info[.mediaURL] as? URL { - let data = try? Data(contentsOf: url) - parent.completionHandler(data ?? Data(), .video) - } - } - } - } -} diff --git a/old/View/Screens/Sharing/SharingPickerScreen.swift b/old/View/Screens/Sharing/SharingPickerScreen.swift deleted file mode 100644 index d67e9ca..0000000 --- a/old/View/Screens/Sharing/SharingPickerScreen.swift +++ /dev/null @@ -1,47 +0,0 @@ -import SwiftUI - -struct AttachmentPickerScreen: View { - @EnvironmentObject var store: AppStore - - @State private var selectedTab: SharingTab = .media - - var body: some View { - ZStack { - // Background color - Color.Material.Background.light - .ignoresSafeArea() - - // Content - VStack(spacing: 0) { - // Header - SharedNavigationBar( - centerText: .init(text: L10n.Attachment.Prompt.main), - rightButton: .init( - image: Image(systemName: "xmark"), - action: { - store.dispatch(.sharingAction(.showSharing(false))) - } - ) - ) - - // Pickers - switch selectedTab { - case .media: - SharingMediaPickerView() - - case .files: - SharingFilesPickerView() - - case .location: - SharingLocationPickerView() - - case .contacts: - SharingContactsPickerView() - } - - // Tab bar - SharingTabBar(selectedTab: $selectedTab) - } - } - } -} diff --git a/old/View/Screens/Sharing/SharingTabBar.swift b/old/View/Screens/Sharing/SharingTabBar.swift deleted file mode 100644 index 770dc04..0000000 --- a/old/View/Screens/Sharing/SharingTabBar.swift +++ /dev/null @@ -1,85 +0,0 @@ -import SwiftUI - -enum SharingTab: Int, CaseIterable { - case media - case files - case location - case contacts -} - -struct SharingTabBar: View { - @Binding var selectedTab: SharingTab - - var body: some View { - VStack(spacing: 0) { - Rectangle() - .frame(maxWidth: .infinity) - .frame(height: 0.2) - .foregroundColor(.Material.Shape.separator) - HStack(spacing: 0) { - SharingTabBarButton(tab: .media, selected: $selectedTab) - SharingTabBarButton(tab: .files, selected: $selectedTab) - SharingTabBarButton(tab: .location, selected: $selectedTab) - SharingTabBarButton(tab: .contacts, selected: $selectedTab) - } - .background(Color.Material.Background.dark) - } - .frame(height: 50) - } -} - -private struct SharingTabBarButton: View { - let tab: SharingTab - @Binding var selected: SharingTab - - var body: some View { - ZStack { - VStack(spacing: 2) { - buttonImg - .foregroundColor(selected == tab ? .Material.Elements.active : .Material.Elements.inactive) - .font(.system(size: 24, weight: .light)) - .symbolRenderingMode(.hierarchical) - Text(buttonTitle) - .font(.sub1) - .foregroundColor(selected == tab ? .Material.Text.main : .Material.Elements.inactive) - } - Rectangle() - .foregroundColor(.white.opacity(0.01)) - .onTapGesture { - selected = tab - } - } - } - - var buttonImg: Image { - switch tab { - case .media: - return Image(systemName: "photo.on.rectangle.angled") - - case .files: - return Image(systemName: "doc.on.doc") - - case .location: - return Image(systemName: "location.circle") - - case .contacts: - return Image(systemName: "person.crop.circle") - } - } - - var buttonTitle: String { - switch tab { - case .media: - return L10n.Attachment.Tab.media - - case .files: - return L10n.Attachment.Tab.files - - case .location: - return L10n.Attachment.Tab.location - - case .contacts: - return L10n.Attachment.Tab.contacts - } - } -} diff --git a/old/View/Screens/StartScreen.swift b/old/View/Screens/StartScreen.swift deleted file mode 100644 index ee7a501..0000000 --- a/old/View/Screens/StartScreen.swift +++ /dev/null @@ -1,19 +0,0 @@ -import SwiftUI - -struct StartScreen: View { - @EnvironmentObject var store: AppStore - - var body: some View { - ZStack { - Color.Material.Background.light - Image.logo - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 200, height: 200) - } - .ignoresSafeArea() - .onAppear { - store.dispatch(.startAction(.loadStoredAccounts)) - } - } -} diff --git a/old/View/Screens/WelcomeScreen.swift b/old/View/Screens/WelcomeScreen.swift deleted file mode 100644 index d7a27f7..0000000 --- a/old/View/Screens/WelcomeScreen.swift +++ /dev/null @@ -1,53 +0,0 @@ -import SwiftUI - -struct WelcomeScreen: View { - @EnvironmentObject var store: AppStore - - public var body: some View { - ZStack { - // background - Color.Material.Background.light - .ignoresSafeArea() - - // content - VStack(spacing: 32) { - // icon - Image.logo - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 120, height: 120) - - // texts - VStack(spacing: 10) { - Text(L10n.Global.name) - .font(.head1r) - .foregroundColor(.Material.Text.main) - .fixedSize(horizontal: true, vertical: false) - Text(L10n.Start.subtitle) - .font(.body2) - .foregroundColor(.Material.Text.sub) - .fixedSize(horizontal: false, vertical: true) - .multilineTextAlignment(.center) - } - - // buttons - VStack(spacing: 16) { - Button { - store.dispatch(.accountsAction(.goTo(.addAccount))) - store.dispatch(.changeFlow(.accounts)) - } label: { - Text(L10n.Start.Btn.login) - } - .buttonStyle(SecondaryButtonStyle()) - Button { - // state.flow = .registration - } label: { - Text(L10n.Start.Btn.register) - } - .buttonStyle(PrimaryButtonStyle()) - } - } - .padding(.horizontal, 32) - } - } -} diff --git a/old/View/SharedComponents/SharedListRow.swift b/old/View/SharedComponents/SharedListRow.swift deleted file mode 100644 index 7e5fa74..0000000 --- a/old/View/SharedComponents/SharedListRow.swift +++ /dev/null @@ -1,59 +0,0 @@ -import SwiftUI - -enum SharedListRowIconType { - case charCircle(String) - case image(Image, Color) -} - -struct SharedListRow: View { - let iconType: SharedListRowIconType - let text: String - - var body: some View { - VStack(spacing: 0) { - HStack(spacing: 8) { - // Icon - switch iconType { - case .charCircle(let str): - let char = str.firstLetter - let color = str.firstLetterColor - ZStack { - Circle() - .frame(width: 44, height: 44) - .foregroundColor(color) - Text(char) - .foregroundColor(.white) - .font(.body1) - } - - case .image(let image, let color): - ZStack { - Circle() - .frame(width: 44, height: 44) - .foregroundColor(.clearTappable) - .overlay { - image - .foregroundColor(color) - } - } - } - - // Text - Text(text) - .foregroundColor(Color.Material.Text.main) - .font(.body2) - Spacer() - } - .padding(.horizontal, 16) - .padding(.vertical, 4) - Rectangle() - .frame(maxWidth: .infinity) - .frame(height: 1) - .foregroundColor(.Material.Background.dark) - } - .listRowInsets(.zero) - .listRowSeparator(.hidden) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color.Material.Background.light) - } -} diff --git a/old/View/SharedComponents/SharedNavigationBar.swift b/old/View/SharedComponents/SharedNavigationBar.swift deleted file mode 100644 index f6e651a..0000000 --- a/old/View/SharedComponents/SharedNavigationBar.swift +++ /dev/null @@ -1,78 +0,0 @@ -import SwiftUI - -struct SharedNavBarButton: View { - let image: Image? - let action: () -> Void - var isEnabled: Bool = true - - init( - image: Image, - action: @escaping () -> Void, - isEnabled: Bool = true - ) { - self.image = image - self.action = action - self.isEnabled = isEnabled - } - - var body: some View { - Button { - action() - } label: { - image - .foregroundColor(isEnabled ? .Material.Elements.active : .Material.Elements.inactive) - .tappablePadding(.symmetric(12)) { - action() - } - } - .disabled(!isEnabled) - } -} - -struct SharedNavBarText: View { - let text: String - - var body: some View { - Text(text) - .font(.head2) - .foregroundColor(.Material.Text.main) - } -} - -struct SharedNavigationBar: View { - var leftButton: SharedNavBarButton? - var centerText: SharedNavBarText? - var rightButton: SharedNavBarButton? - - var body: some View { - ZStack { - Color.Material.Background.dark - .ignoresSafeArea() - - VStack { - if centerText != nil { - centerText - } - } - Spacer() - - HStack(alignment: .center) { - VStack { - if leftButton != nil { - leftButton?.padding() - } - } - .frame(minWidth: 40) - Spacer() - VStack { - if rightButton != nil { - rightButton? - .padding() - } - } - .frame(minWidth: 40) - } - } - .frame(height: 44) - } -} diff --git a/old/View/SharedComponents/SharedTabBar.swift b/old/View/SharedComponents/SharedTabBar.swift deleted file mode 100644 index 8ca982c..0000000 --- a/old/View/SharedComponents/SharedTabBar.swift +++ /dev/null @@ -1,76 +0,0 @@ -import SwiftUI - -struct SharedTabBar: View { - var body: some View { - VStack(spacing: 0) { - Rectangle() - .frame(maxWidth: .infinity) - .frame(height: 0.2) - .foregroundColor(.Material.Shape.separator) - HStack(spacing: 0) { - SharedTabBarButton(buttonFlow: .contacts) - SharedTabBarButton(buttonFlow: .chats) - SharedTabBarButton(buttonFlow: .settings) - } - .background(Color.Material.Background.dark) - } - .frame(height: 50) - } -} - -private struct SharedTabBarButton: View { - @EnvironmentObject var store: AppStore - - let buttonFlow: AppFlow - - var body: some View { - ZStack { - VStack(spacing: 2) { - buttonImg - .foregroundColor(buttonFlow == store.state.currentFlow ? .Material.Elements.active : .Material.Elements.inactive) - .font(.system(size: 24, weight: .light)) - .symbolRenderingMode(.hierarchical) - Text(buttonTitle) - .font(.sub1) - .foregroundColor(buttonFlow == store.state.currentFlow ? .Material.Text.main : .Material.Elements.inactive) - } - Rectangle() - .foregroundColor(.white.opacity(0.01)) - .onTapGesture { - store.dispatch(.changeFlow(buttonFlow)) - } - } - } - - var buttonImg: Image { - switch buttonFlow { - case .contacts: - return Image(systemName: "person.2.fill") - - case .chats: - return Image(systemName: "bubble.left.fill") - - case .settings: - return Image(systemName: "gearshape.fill") - - default: - return Image(systemName: "questionmark.circle") - } - } - - var buttonTitle: String { - switch buttonFlow { - case .contacts: - return "Contacts" - - case .chats: - return "Chats" - - case .settings: - return "Settings" - - default: - return "Unknown" - } - } -} diff --git a/old/View/SharedComponents/UniversalInputCollection.swift b/old/View/SharedComponents/UniversalInputCollection.swift deleted file mode 100644 index fb8b5f8..0000000 --- a/old/View/SharedComponents/UniversalInputCollection.swift +++ /dev/null @@ -1,203 +0,0 @@ -import SwiftUI - -// MARK: Public -protocol UniversalInputSelectionElement: Identifiable, Equatable, Hashable { - var icon: Image? { get } - var text: String? { get } -} - -public enum UniversalInputCollection { - struct TextField { - let prompt: String - @Binding var text: String - var focus: FocusState.Binding - var fieldType: T - let contentType: UITextContentType - let keyboardType: UIKeyboardType - let submitLabel: SubmitLabel - let action: () -> Void - } - - struct SecureField { - let prompt: String - @Binding var text: String - var focus: FocusState.Binding - var fieldType: T - let submitLabel: SubmitLabel - let action: () -> Void - } - - struct DropDownMenu { - let prompt: String - let elements: [E] - @Binding var selected: E? - var focus: FocusState.Binding - var fieldType: T - } -} - -// MARK: Inputs implementations -extension UniversalInputCollection.TextField: View { - var body: some View { - TextField("", text: $text) - .padding(.horizontal, 8) - .focused(focus, equals: fieldType) - .font(.body2) - .foregroundColor(.Material.Text.main) - .autocorrectionDisabled(true) - .autocapitalization(.none) - .textContentType(contentType) - .keyboardType(keyboardType) - .submitLabel(submitLabel) - .textSelection(.enabled) - .onSubmit { - action() - } - .modifier(UniversalInputModifier( - prompt: prompt, - focus: focus, - fieldType: fieldType, - isActive: isFilled - )) - } - - var isFilled: Bool { - !text.isEmpty || focus.wrappedValue == fieldType - } -} - -extension UniversalInputCollection.SecureField: View { - var body: some View { - SecureField("", text: $text) - .padding(.horizontal, 8) - .focused(focus, equals: fieldType) - .font(.body2) - .foregroundColor(.Material.Text.main) - .autocorrectionDisabled(true) - .autocapitalization(.none) - .textContentType(.password) - .submitLabel(submitLabel) - .textSelection(.disabled) - .onSubmit { - action() - } - .modifier(UniversalInputModifier( - prompt: prompt, - focus: focus, - fieldType: fieldType, - isActive: isFilled - )) - } - - var isFilled: Bool { - !text.isEmpty || focus.wrappedValue == fieldType - } -} - -extension UniversalInputCollection.DropDownMenu: View { - var body: some View { - ZStack { - HStack { - Text(text) - .font(.body2) - .foregroundColor(.Material.Text.main) - .padding(.leading, 8) - Spacer() - } - .modifier(UniversalInputModifier( - prompt: prompt, - focus: focus, - fieldType: fieldType, - isActive: selected != nil - )) - - Menu { - ForEach(elements, id: \.self.id) { element in - Button { - selected = element - } label: { - Text(element.text ?? "") - } - } - } label: { - Label("", image: "") - .labelStyle(TitleOnlyLabelStyle()) - .padding(.vertical) - .frame(height: 48) - .frame(maxWidth: .infinity) - } - } - } - - var text: String { - if let text = selected?.text { - return text - } else { - return "" - } - } -} - -// MARK: Modifiers -private struct UniversalInputModifier: ViewModifier { - let prompt: String - var focus: FocusState.Binding - var fieldType: T - let isActive: Bool - var promptBackground: Color? - var isCentered: Bool? - var customTapAction: (() -> Void)? - - func body(content: Content) -> some View { - VStack(spacing: 0) { - ZStack { - HStack { - Text(isActive ? "" : prompt) - .font(.body2) - .foregroundColor(.Material.Shape.separator) - .padding(8) - Spacer() - } - content - .frame(height: 48) - } - } - .frame(height: 48) - .background { - ZStack { - RoundedRectangle(cornerRadius: 4) - .foregroundColor(.Material.Shape.white) - RoundedRectangle(cornerRadius: 4) - .stroke(Color.Material.Shape.separator) - } - } - .contentShape(Rectangle()) - .onTapGesture { - if let customTapAction { - customTapAction() - } else { - if focus.wrappedValue != fieldType { - focus.wrappedValue = fieldType - } - } - } - } -} - -// MARK: Validators -extension UniversalInputCollection { - enum Validators { - static func isEmail(_ input: String) -> Bool { - if !input.isEmpty { - let mailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" - if !NSPredicate(format: "SELF MATCHES %@", mailRegex).evaluate(with: input) { - return false - } else { - return true - } - } else { - return true - } - } - } -} diff --git a/old/View/UIToolkit/Binding+Extensions.swift b/old/View/UIToolkit/Binding+Extensions.swift deleted file mode 100644 index 6a2a796..0000000 --- a/old/View/UIToolkit/Binding+Extensions.swift +++ /dev/null @@ -1,12 +0,0 @@ -import SwiftUI - -extension Binding where Value == String { - func max(_ limit: Int) -> Self { - if wrappedValue.count > limit { - DispatchQueue.main.async { - wrappedValue = String(wrappedValue.dropLast()) - } - } - return self - } -} diff --git a/old/View/UIToolkit/ButtonStyles.swift b/old/View/UIToolkit/ButtonStyles.swift deleted file mode 100644 index 62c59ab..0000000 --- a/old/View/UIToolkit/ButtonStyles.swift +++ /dev/null @@ -1,50 +0,0 @@ -import SwiftUI - -private enum ButtonSizes { - static let padding = 16.0 - static let cornerRadius = 4.0 - static let scaleEffect: CGFloat = 0.9 - static let opacity: Double = 0.6 -} - -struct PrimaryButtonStyle: ButtonStyle { - @Environment(\.isEnabled) private var isEnabled - - func makeBody(configuration: Configuration) -> some View { - configuration - .label - .font(.head2) - .padding(ButtonSizes.padding) - .frame(maxWidth: .infinity) - .foregroundColor(.Material.Shape.white) - .background { - RoundedRectangle(cornerRadius: ButtonSizes.cornerRadius) - .foregroundColor(isEnabled ? .Material.Elements.active : .Material.Shape.separator) - } - .contentShape(Rectangle()) - .scaleEffect(configuration.isPressed ? ButtonSizes.scaleEffect : 1.0) - .opacity(configuration.isPressed ? ButtonSizes.opacity : 1.0) - .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) - } -} - -struct SecondaryButtonStyle: ButtonStyle { - @Environment(\.isEnabled) private var isEnabled - - func makeBody(configuration: Configuration) -> some View { - configuration - .label - .font(.head2) - .padding(ButtonSizes.padding) - .frame(maxWidth: .infinity) - .foregroundColor(isEnabled ? .Material.Elements.active : .Material.Shape.separator) - .background { - RoundedRectangle(cornerRadius: ButtonSizes.cornerRadius) - .stroke(isEnabled ? Color.Material.Elements.active : Color.Material.Shape.separator) - } - .contentShape(Rectangle()) - .scaleEffect(configuration.isPressed ? ButtonSizes.scaleEffect : 1.0) - .opacity(configuration.isPressed ? ButtonSizes.opacity : 1.0) - .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) - } -} diff --git a/old/View/UIToolkit/Colors+Tappable.swift b/old/View/UIToolkit/Colors+Tappable.swift deleted file mode 100644 index 718a3df..0000000 --- a/old/View/UIToolkit/Colors+Tappable.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -public extension Color { - static let clearTappable = Color.white.opacity(0.0001) - // static func random(randomOpacity: Bool = false) -> Color { - // Color( - // red: .random(in: 0 ... 1), - // green: .random(in: 0 ... 1), - // blue: .random(in: 0 ... 1), - // opacity: randomOpacity ? .random(in: 0 ... 1) : 1 - // ) - // } -} diff --git a/old/View/UIToolkit/EdgeInsets+Extensions.swift b/old/View/UIToolkit/EdgeInsets+Extensions.swift deleted file mode 100644 index 65f5311..0000000 --- a/old/View/UIToolkit/EdgeInsets+Extensions.swift +++ /dev/null @@ -1,15 +0,0 @@ -import SwiftUI - -extension EdgeInsets { - var inverted: EdgeInsets { - .init(top: -top, leading: -leading, bottom: -bottom, trailing: -trailing) - } - - static var zero: EdgeInsets { - .init(top: 0, leading: 0, bottom: 0, trailing: 0) - } - - static func symmetric(_ value: CGFloat) -> EdgeInsets { - .init(top: value, leading: value, bottom: value, trailing: value) - } -} diff --git a/old/View/UIToolkit/KeyboardDisposableModifier.swift b/old/View/UIToolkit/KeyboardDisposableModifier.swift deleted file mode 100644 index 8b372fd..0000000 --- a/old/View/UIToolkit/KeyboardDisposableModifier.swift +++ /dev/null @@ -1,22 +0,0 @@ -import SwiftUI - -private struct ContentBlockModifier: ViewModifier { - var focus: FocusState.Binding - - func body(content: Content) -> some View { - content - .background { - Rectangle() - .foregroundColor(.white.opacity(0.01)) - .onTapGesture { - focus.wrappedValue = nil - } - } - } -} - -extension View { - func keyboardUnfocus(_ focus: FocusState.Binding) -> some View { - self.modifier(ContentBlockModifier(focus: focus)) - } -} diff --git a/old/View/UIToolkit/Typography.swift b/old/View/UIToolkit/Typography.swift deleted file mode 100644 index db856f3..0000000 --- a/old/View/UIToolkit/Typography.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation -import SwiftUI - -extension Font { - static let head1l = Font.system(size: 34, weight: .light, design: .rounded) - static let head1r = Font.system(size: 34, weight: .regular, design: .rounded) - static let head2 = Font.system(size: 20, weight: .regular, design: .rounded) - static let body1 = Font.system(size: 18, weight: .regular, design: .rounded) - static let body2 = Font.system(size: 16, weight: .regular, design: .rounded) - static let body3 = Font.system(size: 14, weight: .regular, design: .rounded) - static let sub1 = Font.system(size: 10, weight: .regular, design: .rounded) - static let sub2 = Font.system(size: 8, weight: .regular, design: .rounded) -} diff --git a/old/View/UIToolkit/UIImage+Crop.swift b/old/View/UIToolkit/UIImage+Crop.swift deleted file mode 100644 index 1bccefd..0000000 --- a/old/View/UIToolkit/UIImage+Crop.swift +++ /dev/null @@ -1,42 +0,0 @@ -import UIKit - -extension UIImage { - func scaleAndCropImage(toExampleSize _: CGSize, completion: @escaping (UIImage?) -> Void) { - DispatchQueue.global(qos: .background).async { - guard let cgImage = self.cgImage else { - DispatchQueue.main.async { - completion(nil) - } - return - } - let contextImage: UIImage = .init(cgImage: cgImage) - var contextSize: CGSize = contextImage.size - - var posX: CGFloat = 0.0 - var posY: CGFloat = 0.0 - let cgwidth: CGFloat = self.size.width - let cgheight: CGFloat = self.size.height - - // Check and handle if the image is wider than the requested size - if contextSize.width > contextSize.height { - posX = ((contextSize.width - contextSize.height) / 2) - contextSize.width = contextSize.height - } else if contextSize.width < contextSize.height { - // Check and handle if the image is taller than the requested size - posY = ((contextSize.height - contextSize.width) / 2) - contextSize.height = contextSize.width - } - - let rect: CGRect = .init(x: posX, y: posY, width: cgwidth, height: cgheight) - guard let contextCg = contextImage.cgImage, let imgRef = contextCg.cropping(to: rect) else { - DispatchQueue.main.async { - completion(nil) - } - return - } - let image: UIImage = .init(cgImage: imgRef, scale: self.scale, orientation: self.imageOrientation) - - completion(image) - } - } -} diff --git a/old/View/UIToolkit/Vibration.swift b/old/View/UIToolkit/Vibration.swift deleted file mode 100644 index 3eee66b..0000000 --- a/old/View/UIToolkit/Vibration.swift +++ /dev/null @@ -1,17 +0,0 @@ -import SwiftUI -import UIKit - -enum Vibration: String { - case error - case success - - public func vibrate() { - switch self { - case .error: - UINotificationFeedbackGenerator().notificationOccurred(.error) - - case .success: - UINotificationFeedbackGenerator().notificationOccurred(.success) - } - } -} diff --git a/old/View/UIToolkit/View+Debug.swift b/old/View/UIToolkit/View+Debug.swift deleted file mode 100644 index 34f7c65..0000000 --- a/old/View/UIToolkit/View+Debug.swift +++ /dev/null @@ -1,35 +0,0 @@ -import SwiftUI - -#if DEBUG - private let rainbowDebugColors = [ - Color.purple, - Color.blue, - Color.green, - Color.yellow, - Color.orange, - Color.red, - Color.pink, - Color.black.opacity(0.5), - Color.teal, - Color.gray, - Color.mint, - Color.cyan - ] - - public extension Color { - static func random(randomOpacity: Bool = false) -> Color { - Color( - red: .random(in: 0 ... 1), - green: .random(in: 0 ... 1), - blue: .random(in: 0 ... 1), - opacity: randomOpacity ? .random(in: 0 ... 1) : 1 - ) - } - } - - extension View { - func rainbowDebug() -> some View { - background(Color.random()) - } - } -#endif diff --git a/old/View/UIToolkit/View+If.swift b/old/View/UIToolkit/View+If.swift deleted file mode 100644 index f6611ca..0000000 --- a/old/View/UIToolkit/View+If.swift +++ /dev/null @@ -1,11 +0,0 @@ -import SwiftUI - -public extension View { - @ViewBuilder func `if`(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View { - if condition() { - transform(self) - } else { - self - } - } -} diff --git a/old/View/UIToolkit/View+Loader.swift b/old/View/UIToolkit/View+Loader.swift deleted file mode 100644 index 3d0a76c..0000000 --- a/old/View/UIToolkit/View+Loader.swift +++ /dev/null @@ -1,40 +0,0 @@ -import Foundation -import SwiftUI - -public extension View { - func loadingIndicator(_ isShowing: Bool) -> some View { - modifier(LoadingIndicator(isShowing: isShowing)) - } -} - -struct LoadingIndicator: ViewModifier { - var isShowing: Bool - - func body(content: Content) -> some View { - ZStack { - content - if isShowing { - loadingView - } - } - } - - private var loadingView: some View { - GeometryReader { proxyReader in - ZStack { - Color.Material.Elements.active.opacity(0.3) - .frame(maxWidth: .infinity, maxHeight: .infinity) - - // loader - ProgressView() - .progressViewStyle( - CircularProgressViewStyle(tint: .Material.Elements.active) - ) - .position(x: proxyReader.size.width / 2, y: proxyReader.size.height / 2) - .controlSize(.large) - } - } - .ignoresSafeArea() - .transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.1))) - } -} diff --git a/old/View/UIToolkit/View+OnLoad.swift b/old/View/UIToolkit/View+OnLoad.swift deleted file mode 100644 index 4206bff..0000000 --- a/old/View/UIToolkit/View+OnLoad.swift +++ /dev/null @@ -1,27 +0,0 @@ -import SwiftUI - -// MARK: - On load -extension View { - func onLoad(_ action: @escaping () -> Void) -> some View { - modifier(ViewDidLoadModifier(action)) - } -} - -private struct ViewDidLoadModifier: ViewModifier { - private let action: () -> Void - - @State private var didLoad = false - - init(_ action: @escaping () -> Void) { - self.action = action - } - - func body(content: Content) -> some View { - content.onAppear { - if !didLoad { - didLoad.toggle() - action() - } - } - } -} diff --git a/old/View/UIToolkit/View+TappableArea.swift b/old/View/UIToolkit/View+TappableArea.swift deleted file mode 100644 index 8c659d4..0000000 --- a/old/View/UIToolkit/View+TappableArea.swift +++ /dev/null @@ -1,27 +0,0 @@ -import SwiftUI - -extension View { - func tappablePadding(_ insets: EdgeInsets, onTap: @escaping () -> Void) -> some View { - modifier(TappablePadding(insets: insets, onTap: onTap)) - } -} - -struct TappablePadding: ViewModifier { - let insets: EdgeInsets - let onTap: () -> Void - - public init(insets: EdgeInsets, onTap: @escaping () -> Void) { - self.insets = insets - self.onTap = onTap - } - - public func body(content: Content) -> some View { - content - .padding(insets) - .contentShape(Rectangle()) - .onTapGesture { - onTap() - } - .padding(insets.inverted) - } -} diff --git a/project.yml b/project.yml index a920fc2..f90cf15 100644 --- a/project.yml +++ b/project.yml @@ -61,7 +61,6 @@ targets: - path: ConversationsClassic excludes: - .nvim - - old settings: TARGETED_DEVICE_FAMILY: 1 DEBUG_INFORMATION_FORMAT: dwarf-with-dsym